StaticGroup Flexibility

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
test44x
Posts: 15
Joined: Tue Jan 20, 2015 1:40 am

StaticGroup Flexibility

Post by test44x »

Hi Norbo,

Currently it seems that StaticGroup does not support individual settings for each ConvexCollidable shape. For example, it does not allow some pieces to have it's own event callbacks or collision rule.

I am trying to find the best way to implement around 500 static NoSolver objects on the map that is only used for triggering events when colliding with player.

I am currently using CompoundBody to have 500 objects on the map. Putting bunch of static objects in a CompoundBody seems to preform a lot better than objects having each its own separate Entity. However, there seems to be a strange overhead when a player collides with a static NoSolver child of the compoundBody. Whenever a player stands within a NoSolver child or make contacts, on mobile phone that alone increases physic time by almost 1 millisecond!

When ever a player makes contact with the child, the child's collisionRules changes to NobroadPhase. Even If the child's collision is set to NobroadPhase, when ever the player stands within the child, there is still a spike. As soon as the player moves out, there is a big drop in physics time. There seems to be a correlation with how many child the CompoudBody has and the increase in physics time. Btw, I am running only single thread.

So I was hoping maybe StaticGroup could solve that, but it seems StaticGroup acts as a solid Entity. Was it was because of performance to have StaticGroup to be implemented in this way? Also, what do you think would be best performance wise to have lots of Static NoSolver objects on map that is only used to trigger events. This is really important in games as it uses triggers for lots of things such as picking up coins and interacting.

Thanks in advance!
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: StaticGroup Flexibility

Post by Norbo »

Currently it seems that StaticGroup does not support individual settings for each ConvexCollidable shape. For example, it does not allow some pieces to have it's own event callbacks or collision rule.
Collision rules should work. Event callbacks should work too, but it turns out there's a bug that breaks deferred events on StaticGroup children. Immediate events (those with present tense names) should still work; you could use them as a workaround while I think about how to best fix deferred events.

Here's some example code showing how to customize the children rules and events:

Code: Select all

            var a = new ConvexCollidable<BoxShape>(new BoxShape(1, 1, 1)) { WorldTransform = new RigidTransform(new Vector3(0, 3, 3)) };
            var b = new ConvexCollidable<BoxShape>(new BoxShape(1, 1, 1)) { WorldTransform = new RigidTransform(new Vector3(0, 5, 3)) };
            a.CollisionRules.Personal = BEPUphysics.CollisionRuleManagement.CollisionRule.NoSolver;
            a.Events.DetectingInitialCollision += (x, y, pair) => Console.WriteLine("Collision");
            var collidables = new[] { a, b };
            var group = new StaticGroup(collidables);
            Space.Add(group)
Another workaround would be to put the event handler on the players.

(I should mention that deferred events and much of the event API is going away in v2.0, so there may be some value in looking into other approaches. I actually lean towards checking the collision pair lists directly in my own projects.)
Whenever a player stands within a NoSolver child or make contacts, on mobile phone that alone increases physic time by almost 1 millisecond!
It's hard to say whether this is reasonable or not. Deep penetration does tend to be more expensive on some collision types.
1) Depending on the phone, a 1ms jump might be expected if the trigger object is a solid MobileMeshShape or something else with very expensive deep tests.
2) Pairs that rely on GJK/MPR (most convex-convex tests) would be a little cheaper than MobileMeshShapes in deep penetration, but it would still be a hit. 1ms sounds high for this, even on a phone.
3) Things with special case collision detection like box-box, sphere-sphere, and box-sphere should not suffer at all during deep penetration. If you see a 1ms spike with one of these cases, something odd is happening.
Also, what do you think would be best performance wise to have lots of Static NoSolver objects on map that is only used to trigger events. This is really important in games as it uses triggers for lots of things such as picking up coins and interacting.
If possible, I would recommend periodic bounding volume queries rather than persistent collidable shapes. Looking for AABB overlaps with a query AABB every 10th frame will be tons faster than maintaining an actual collidable object in the space.

If you actually need fine-grained collision detection, then StaticGroup or a compound is probably the way to go in v1.4.0. This is an area that will see some changes in v2.0- the broadphase revamp may eliminate the need for complicated performance-saving schemes in this kind of situation. StaticGroup will very likely be disappearing completely, and the compound will become significantly more flexible (mutability).
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: StaticGroup Flexibility

Post by Norbo »

I forgot to mention:
When ever a player makes contact with the child, the child's collisionRules changes to NobroadPhase. Even If the child's collision is set to NobroadPhase, when ever the player stands within the child, there is still a spike. As soon as the player moves out, there is a big drop in physics time.
Changing the collision rules to NoBroadPhase will not automatically update the collision rules of all existing pairs, so an existing collision will keep its initial rule.

Using immediate events is actually handy in this case, because you can directly change the pair handler's evaluated collision rule to whatever you want. For example, as a modification to the earlier example:

Code: Select all

            a.Events.PairTouching += (x, y, pair) =>
                {
                    Console.WriteLine("Touching");
                    pair.CollisionRule = CollisionRule.NoBroadPhase;
                };
The console will only print "Touching" for the first timestep of impact, since later frames will see the new rule and not perform any collision detection on the pair.

Changing the pair's rule has no effect on the involved collidables' rules.

Regarding the deferred event fix, the more I look at it, the more I don't want to fix it. The whole deferred event system is just really poorly designed. The minimum developmental effort 'fix' would consume a lot of cycles for very little benefit. It would amount to using something like this:

Code: Select all

    public class StaticGroupEventManager : ContactEventManager<Collidable>
    {

        protected override void DispatchEvents()
        {
            //Go through all children and dispatch events.
            //They won't be touched by the primary event manager otherwise.
            var group = this.owner as StaticGroup;
            if (group != null)
            {
                foreach (Collidable child in group.Shape.Collidables)
                {
                    var eventOwner = child as IDeferredEventCreatorOwner;
                    if (eventOwner != null && eventOwner.EventCreator.IsActive)
                    {
                        eventOwner.EventCreator.DispatchEvents();
                    }
                }
            }
            else
            {
                throw new InvalidOperationException("Cannot use a StaticGroupEventManager with anything but a StaticGroup.");
            }
            base.DispatchEvents();
        }
    }
StaticGroup.Events would then be changed to use the StaticGroupEventManager instead of ContactEventManager<StaticGroup>.

This would require that StaticGroupShape gain a Collidables list which it doesn't currently need.

I really don't want to spend a bunch of time revamping the deferred event system in the context of v1.4 when it's going to get deleted in the not too distant future, but I'm also not happy about it just being broken. We'll see what happens. In the mean time, use anything but deferred events on static group children I guess :P
test44x
Posts: 15
Joined: Tue Jan 20, 2015 1:40 am

Re: StaticGroup Flexibility

Post by test44x »

Thanks for advice Norbo.

I implemented StaticGroup in my app anyways and I am shocked at the improved performance compare to Compoundbody.

On mobile with about 500 static objects compound to one body takes about 3.5ms physics time. Using static group, it brought the physics time to about 2.0ms! I can't help but think that there is just something off about compundBody's performance. Changing my player from compoundBody(2 children) to single Sphere, stripped away about .3ms.

I tried creating a quick demo to prove this on BEPUPhysicsDemos, but wasn't able to get noticeable performance hits... I will look into it more when I get the chance.. Maybe I integrated BEPU Compoundbody in Unity with a bug. I will try to recreate the exact scene in BEPUPhysicsDemo from Unitys and measure performance, I feel that in certain conditions CompoundBody is doing some extra unnecessary calculations that is causing some performance issues.

As for events, that's no problem. I set the player to listen to events and it works fine when it hits a child of StaticGroup. Another thing I noticed is that, if you set, lets say about 500 children to have an event, there is noticeably decrease in performance compare to having one object, such as the player listening to events. I will try to reproduce this in BEPUPhysicsDemo.
I actually lean towards checking the collision pair lists directly in my own projects.)
Could you please give me a quick example of how you do this?
If possible, I would recommend periodic bounding volume queries rather than persistent collidable shapes. Looking for AABB overlaps with a query AABB every 10th frame will be tons faster than maintaining an actual collidable object in the space.
Could you also give me example of this as well?

Thank you in advance!
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: StaticGroup Flexibility

Post by Norbo »

I feel that in certain conditions CompoundBody is doing some extra unnecessary calculations that is causing some performance issues.
I'm not certain this would fully explain your observations, but compounds definitely have extra overhead compared to static groups. They use a dynamic world space hierarchy that gets updated each frame. The StaticGroup hierarchy is, well, static :)

That would not be a significant cost when there are only 2 children in the compound, though. If you've got a consistent big difference between 2 children in a compound and some other primitive, I would suspect some other source of slowdown- perhaps the types of shapes and collision pairs involved. Or it might just be a quirk of the phone's architecture mismatched with part of the workload; it's hard to say.
Another thing I noticed is that, if you set, lets say about 500 children to have an event, there is noticeably decrease in performance compare to having one object, such as the player listening to events.
That is somewhat expected (depending on the degree of slowdown). Even immediate events do have some overhead. Deferred events in general are a lot more expensive than immediate events, too- especially deferred events associated with compounds.

This is part of the reason why I'd like to do away with as much of the events system as possible.
Could you please give me a quick example of how you do this?
All collidables have a Pairs listing, which contains every active collision pair. Each pair has a Contacts enumerable. So, you can do things like this:

Code: Select all

            foreach (var pair in collidable.Pairs)
            {
                if (pair.CollidableA == Boyston)
                {
                    if (pair.Colliding)
                    {
                        Console.WriteLine("Boyston in collision.");
                        break;
                    }
                }
                else
                {
                    foreach (var contactInfo in pair.Contacts)
                    {
                        if (contactInfo.NormalImpulse > 10 && Math.Abs(Vector3.Dot(contactInfo.Contact.Normal, boystonDirection)) > 0.5f)
                        {
                            Console.WriteLine("Strong boyston collision aligned with boystonDirection detected.");
                        }
                    }
                }
            }
StaticGroup, StaticMesh, InstancedMesh and Terrain are all Collidables. The Collidable associated with an entity is stored in its Entity.CollisionInformation property.
Could you also give me example of this as well?
Suppose you had a bunch of places in the world that you wanted to check for players, and you only cared about AABB-level overlap. You could ask the broad phase for a list of all overlaps with a query AABB, and then analyze the results:

Code: Select all

            //Once every X frames, check to see if any boyston is close enough to this instance of disgusting goop to pick it up.
            Space.BroadPhase.QueryAccelerator.GetEntries(disgustingGoopItemBoundingBox, outputOverlapsList);
            foreach (var overlap in outputOverlapsList)
            {
                var tag = overlap.Tag as BoystonGameDataTag;
                if (tag != null)
                {
                    //Found an overlapping boyston!
                    tag.Boyston.PickUp(disgustingGoopItem);
                }
            }
The periodic check allows the work to be distributed over many frames rather than spiking any one frame (or all frames).

Depending on the design, it may be more efficient to reverse the query targets. Rather than querying the world at every point where a player might trigger something, you could query another structure using the player's AABB. For example, if the pickups are not moving, it would be efficient to stick them into a BoundingBoxTree<DisgustingGoopItem>. Then:

Code: Select all

            //Build the goops tree up front.
            BoundingBoxTree<DisgustingGoopItem> disgustingGoopsTree = new BoundingBoxTree<DisgustingGoopItem>(disgustingGoops);
...
            //Every X frames (or even every frame, since chances are there aren't many boystons and this is pretty cheap),
            //use the boyston's AABB to find any nearby disgusting goops.
            disgustingGoopsTree.GetOverlaps(boystonBoundingBox, outputNearbyDisgustingGoops);
            foreach (var disgustingGoop in outputNearbyDisgustingGoops)
            {
                boyston.PickUp(disgustingGoop);
            }
This avoids polluting the broad phase with tons of potentially dynamic objects to track.
Post Reply