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!
StaticGroup Flexibility
Re: StaticGroup Flexibility
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.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.
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)
(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.)
It's hard to say whether this is reasonable or not. Deep penetration does tend to be more expensive on some collision types.Whenever a player stands within a NoSolver child or make contacts, on mobile phone that alone increases physic time by almost 1 millisecond!
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.
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.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 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).
Re: StaticGroup Flexibility
I forgot to mention:
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:
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:
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
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.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.
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;
};
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();
}
}
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
Re: StaticGroup Flexibility
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.
Thank you in advance!
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.
Could you please give me a quick example of how you do this?I actually lean towards checking the collision pair lists directly in my own projects.)
Could you also give me example of this as well?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.
Thank you in advance!
Re: StaticGroup Flexibility
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, staticI feel that in certain conditions CompoundBody is doing some extra unnecessary calculations that is causing some performance issues.
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.
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.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.
This is part of the reason why I'd like to do away with as much of the events system as possible.
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:Could you please give me a quick example of how you do 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.");
}
}
}
}
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:Could you also give me example of this as well?
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);
}
}
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);
}