[V1] Empty Entity.CollisionInformation.Pairs when waking up Entity

Discuss any questions about BEPUphysics or problems encountered.
Post Reply
Valdiralita
Posts: 3
Joined: Mon Jan 21, 2019 4:59 pm

[V1] Empty Entity.CollisionInformation.Pairs when waking up Entity

Post by Valdiralita »

Hey!

I'm trying to use BEPU for a little sideprojct I'm currently working on.
So far I've created different elements that can be dynamic and kinematic objects which interact fine.
Now I'm trying to create an element that detects intersection with dynamic elements (let's call it a sensor).

This works good most of the time, but when a dynamic element is sleeping while intersecting a sensor and is woken up, the sensors CollisionInformation contain no Pairs for the next physic update. The update after that everything is fine and the Pairs will contain the correct data.

Also if a kinematic entity pushes a dynamic entity into a sensor, sometimes there will be no pairs at all, regardless of how many physic steps I run.

Any guesses why?

Thanks!

EDIT: short video to illustrate the issue: https://youtu.be/ik7m3Qg2G9M
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: [V1] Empty Entity.CollisionInformation.Pairs when waking up Entity

Post by Norbo »

The pairs list is updated upon the addition of a CollidablePairHandler to the narrow phase, and upon pair handler cleanup. The pair handler should persist (i.e. not get cleaned up) for the duration of the broadphase detected overlap regardless of activity state, so I don't have a good explanation for what you observe. In particular, whether a kinematic entity or other force is responsible for making a dynamic move into contact shouldn't affect the existence of pairs at all.

Could there be something odd with the collision filtering or the way in which the pairs are being enumerated?

It's also possible I've just forgotten some complexity in how v1 handled pairs- it has been a while :) Can you reproduce the behavior with a small test case in the BEPUphysicsDemos project?
Valdiralita
Posts: 3
Joined: Mon Jan 21, 2019 4:59 pm

Re: [V1] Empty Entity.CollisionInformation.Pairs when waking up Entity

Post by Valdiralita »

Norbo wrote: Tue Jan 22, 2019 12:14 am The pairs list is updated upon the addition of a CollidablePairHandler to the narrow phase, and upon pair handler cleanup. The pair handler should persist (i.e. not get cleaned up) for the duration of the broadphase detected overlap regardless of activity state, so I don't have a good explanation for what you observe. In particular, whether a kinematic entity or other force is responsible for making a dynamic move into contact shouldn't affect the existence of pairs at all.

Could there be something odd with the collision filtering or the way in which the pairs are being enumerated?

It's also possible I've just forgotten some complexity in how v1 handled pairs- it has been a while :) Can you reproduce the behavior with a small test case in the BEPUphysicsDemos project?
I could reproduce the issue in the BEPUphysicsDemo:
In the Demo the kinematic object will move in steps to the left.
Sometimes when the kinematic object moves the dynamic object, there will be no pairs and the indicator will jump up one step.
(I couldn'd find a way to swap colors, so it has to move :lol: )

Image

Code: Select all

using BEPUphysics.Entities.Prefabs;
using BEPUphysics.CollisionShapes.ConvexShapes;
using System.Linq;
using BEPUphysics.CollisionRuleManagement;
using BEPUphysics.Entities;
using BEPUutilities;

namespace BEPUphysicsDemos.Demos
{
    public class SensorDemo : StandardDemo
    {
        private Box indicator;
        private Entity sensorEntity, dynamicEntity, kinematicEntity;
        private CollisionGroup dynamicgrp, kinematicgrp, sensorgrp;

        public SensorDemo(DemosGame game) : base(game)
        {
            dynamicgrp = new CollisionGroup();
            kinematicgrp = new CollisionGroup();
            sensorgrp = new CollisionGroup();
            CollisionGroup.DefineCollisionRule(kinematicgrp, dynamicgrp, CollisionRule.Normal);
            CollisionGroup.DefineCollisionRule(kinematicgrp, sensorgrp, CollisionRule.NoBroadPhase);
            CollisionGroup.DefineCollisionRule(dynamicgrp, sensorgrp, CollisionRule.NoSolver);

            // kinematic entity which is moved by velocity and hitting the dynamic entity
            kinematicEntity = new Entity(new BoxShape(1, 1, 1))
            {
                Position = new Vector3(3.5f, 1, 0)
            };
            kinematicEntity.CollisionInformation.CollisionRules.Group = kinematicgrp;
            Space.Add(kinematicEntity);

            // dynamic entity
            dynamicEntity = new Entity(new BoxShape(1, 1, 1), 1)
            {
                Position = new Vector3(2.5f, 1, 0)
            };
            dynamicEntity.BecomeDynamic(1);
            dynamicEntity.CollisionInformation.CollisionRules.Group = dynamicgrp;
            Space.Add(dynamicEntity);

            //sensor
            sensorEntity = new Entity(new BoxShape(5, 0.1f, 0.1f))
            {
                Position = new Vector3(0, 0.5f, 0)
            };
            sensorEntity.CollisionInformation.CollisionRules.Group = sensorgrp;
            Space.Add(sensorEntity);

            //floor
            Space.Add(new Box(new Vector3(0, -0.5f, 0), 50, 1, 50));

            //indicator - if this moves, there are no pairs detected
            indicator = new Box(new Vector3(5, 1, 5), 0.5f, 0.5f, 0.5f);
            Space.Add(indicator);

            game.Camera.Position = new Vector3(3, 1.5f, 10);
        }

        private float dtsum = 0;
        public override void Update(float dt)
        {
            if (sensorEntity.CollisionInformation.Pairs.Any())
            {
                indicator.Position = new Vector3(5, 1, 5);
            }
            else
            {
                //  Debugger.Break(); // no pairs - oh noes!
                indicator.Position = new Vector3(5, 2, 5);
            }

            kinematicEntity.LinearVelocity = Vector3.Zero;
            dtsum += dt;
            if (dtsum > 1)
            {
                dtsum = 0;
                kinematicEntity.LinearVelocity = new Vector3(-1f, 0, 0);
            }

            base.Update(dt);
        }

        /// <summary>
        /// Gets the name of the simulation.
        /// </summary>
        public override string Name
        {
            get { return "Sensor Demo"; }
        }
    }
}
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: [V1] Empty Entity.CollisionInformation.Pairs when waking up Entity

Post by Norbo »

Replicated- that's pretty weird! The pairs do indeed get removed due to 'staleness' a frame after activation, only to be readded another frame later. I probably reordered some activation logic in the narrowphase without realizing a dependency eons ago.

Until I fix it, a workaround is to set the sensor.ActivityInformation.IsAlwaysActive to true.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: [V1] Empty Entity.CollisionInformation.Pairs when waking up Entity

Post by Norbo »

Yikes, I think this bug dates back to the last time I refactored island merging to occur on constraint add, and if I'm right, it's an ugly one:

1) Dynamic-Sensor pair is found by the broad phase, but both objects are inactive. The pair's 'updated' flag is left unset.
2) Kinematic-dynamic pair is found by the broad phase, and creates a contact constraint with the dynamic, activating the dynamic body.
3) All existing pairs for checked for 'staleness'- if either involved entity is active and the 'updated' flag on the pair is unset, then the pair is considered stale and removed.
4) The next frame, the dynamic is seen as active and the pairs are recreated.

In other words, the 'staleness' state of the dynamic-sensor pair depends on when it executes relative to the kinematic-dynamic pair, so there's a race condition.

One possible fix would be to defer the constraint additions, or at the very least the activations triggered by constraint additions, until after the stale checking postpass completes. That is how it used to work, as this obsoleted comment in the NarrowPhase notes:

Code: Select all

//Remove stale objects BEFORE adding new objects. This ensures that simulation islands which will be activated 
//by new narrow phase pairs will not be momentarily considered stale.
Deferring the entire constraint addition pushes a fairly heavyweight bunch of operations into strictly sequential execution. That's why I pulled it into the narrow phase parallel phase in the first place.

Some of that expense could be avoided by only doing the activations in a postpass, but that would require some slightly annoying architectural changes. The DeactivationManager.Merge function would need to somehow punt responsibility the activity state changes. DeactivationManager.Add already performs a global lock, so that wouldn't be too horrible to add a queue which is then flushed by the narrowphase alongside the AddNewNarrowPhaseObjects call.

Given the relatively high complexity and risk of breaking other things accidentally if I'm wrong about the details, I'm hesitant to immediately push a fix for this. I think I'd rather get v2 out the door first unless the workaround of forcing IsAlwaysActive becomes insufficient. (You can give it a shot if you'd like :P)

Edit: referenced from github: https://github.com/bepu/bepuphysics1/issues/5
Valdiralita
Posts: 3
Joined: Mon Jan 21, 2019 4:59 pm

Re: [V1] Empty Entity.CollisionInformation.Pairs when waking up Entity

Post by Valdiralita »

Norbo wrote: Tue Jan 22, 2019 10:42 pmUntil I fix it, a workaround is to set the sensor.ActivityInformation.IsAlwaysActive to true.
Thanks for the awesone and detailed explanations! I haven't done any extensive profiling but for now setting IsAlwaysActive for the sensors seems to work just fine.
If I encounter any perfromance problems I'll let you know.
Post Reply