Weld one entity to another on collision

Discuss any questions about BEPUphysics or problems encountered.
Telanor
Posts: 57
Joined: Sun May 06, 2012 10:49 pm

Weld one entity to another on collision

Post by Telanor »

Am I missing something, or is there no way to get the Entity associated with the collidable passed in from the PairTouched event? I'm trying to make arrows weld to whatever they hit, but the Weld constraint wants 2 entities. Do I have to put the entity in the tag for all the collidables or is there a better way? Also, how can I make it so an arrow penetrates the collidable a little bit so it doesnt just stick to the surface?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Weld one entity to another on collision

Post by Norbo »

Am I missing something, or is there no way to get the Entity associated with the collidable passed in from the PairTouched event? I'm trying to make arrows weld to whatever they hit, but the Weld constraint wants 2 entities. Do I have to put the entity in the tag for all the collidables or is there a better way?
EntityCollidable is a subclass of Collidable. If a cast attempt succeeds, then you can use the EntityCollidable.Entity property to grab the entity associated with the entity.

Edit:
CollidablePairHandlers also expose two entity properties. Note that these can return null if there's no entity associated with the collidable.
Also, how can I make it so an arrow penetrates the collidable a little bit so it doesnt just stick to the surface?
Scoot the arrow entity forward a bit upon impact before welding it. "Forward" in this case depends on how the arrow is oriented in local space. Transform the direction the arrow points in local space by the orientation of the entity to get the world space forward vector to scoot along. If it's one of the three primary axes of the orthonormal basis defining the orientation, you can just grab it straight out of the entity.OrientationMatrix.Forward etc. properties.

When doing this, collision rules should be set to prevent the arrow from undergoing collision response with the hit object.

If this is a purely graphical effect, I'd recommend removing the arrow from the simulation and just using a purely graphical proxy. It will be quite a bit more efficient.
Telanor
Posts: 57
Joined: Sun May 06, 2012 10:49 pm

Re: Weld one entity to another on collision

Post by Telanor »

Ah. I knew there had to be a better way. It is a purely graphical effect, but if it attaches to a moving entity, I'd need some way to make it move with that entity. I'm having 2 other issues though. When the arrow collides with a surface, if I don't weld it, it bounces off and begins spinning wildly. The problem is, if I do weld it, it still starts to rotate a bit before its stopped by the weld (or in the case when it collides with static terrain, I just remove the physics entity entirely). What ends up happening is that the arrow gets stuck at weird angles:
RuinValor-2012-09-10-01-40-32-74.jpg
RuinValor-2012-09-10-01-40-32-74.jpg (36.65 KiB) Viewed 12422 times
The second issue is minor but when you shoot the arrow up (it doesn't even have to be shot straight up), it comes back down with the arrow head facing upwards. Ive tried constructing the arrow as a compound object made of 2 boxes, with the arrowhead one weighing more, but it didn't help.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Weld one entity to another on collision

Post by Norbo »

It is a purely graphical effect, but if it attaches to a moving entity, I'd need some way to make it move with that entity.
On impact, compute the local space transform of the arrow relative to the entity. To find where to put the arrow graphic each frame, multiply the local space transform by the world transform of the entity. This will be infinitely rigid (compared to a merely mostly rigid weld), and it will be a lot faster.
When the arrow collides with a surface, if I don't weld it, it bounces off and begins spinning wildly. The problem is, if I do weld it, it still starts to rotate a bit before its stopped by the weld (or in the case when it collides with static terrain, I just remove the physics entity entirely). What ends up happening is that the arrow gets stuck at weird angles:
Assuming that the bounce is simply caused by the large impact forces plus the low arrow inertia as opposed to some problem like the collision shape not matching the graphic, this could be solved by making the arrow not respond to collisions with anything. It just generates contacts. So, you'll know it hit something without it bouncing around. You may want to incorporate your own form of collision response for the arrows to stop them from continuing to fly through the obstacle during the end-of-timestep position update. This could be done by computing the relative linear velocity between the arrow and the impacted entity (if any), and then computing the component of that relative linear velocity which is along the direction of motion of the arrow. That component is then subtracted out of the arrow's linear velocity in one of the immediate events so that the change is visible by the solver and position update.
The second issue is minor but when you shoot the arrow up (it doesn't even have to be shot straight up), it comes back down with the arrow head facing upwards. Ive tried constructing the arrow as a compound object made of 2 boxes, with the arrowhead one weighing more, but it didn't help.
One option would be to control the orientation of the arrow based upon the linear velocity. Align the local forward vector of the arrow with the linear velocity direction. Beware of the zero-velocity singularity.
Telanor
Posts: 57
Joined: Sun May 06, 2012 10:49 pm

Re: Weld one entity to another on collision

Post by Telanor »

Ok, so I turned off the solver for the arrow and when it collides, I set its linear velocity to zero. This mostly works, but sometimes the arrows go through the ground or at least penetrate it pretty far. I assume this is exactly what you were talking about with the custom collision response, however I'm not sure I understand your solution. You said calculate the relative velocity between the arrow and its target, compute the amount along the motion vector and subtract that from the linear velocity. If the target isn't moving, wouldn't the result just be the negative of the linear velocity? If that's the case you'd simply end up with zero velocity, which is what I've already done... I'm doing all of this inside the PairTouching event, is that the correct event to use?

As for the orientation trick, that worked perfectly, thanks.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Weld one entity to another on collision

Post by Norbo »

You said calculate the relative velocity between the arrow and its target, compute the amount along the motion vector and subtract that from the linear velocity. If the target isn't moving, wouldn't the result just be the negative of the linear velocity? If that's the case you'd simply end up with zero velocity, which is what I've already done... I'm doing all of this inside the PairTouching event, is that the correct event to use?
That's all correct. DetectingInitialCollision might be a bit more direct than PairTouching, but in this case they should be functionally equivalent.

If the arrow is moving fast relative to the time step duration, then the arrow could still penetrate pretty far into the target even with full collision response. This is because the engine takes discrete steps forward in time. To mitigate this, you can set the arrow's entity.PositionUpdateMode = PositionUpdateMode.Continuous. This will prevent full tunneling (except in the case of significant angular motion), but the object could still go through a little less than halfway because the primary form of motion clamping CCD is applied between a core shape and the other object (and vice versa).

However, by default, the MotionSettings.CCDFilter will avoid testing any pair of objects which don't undergo collision response. This would need to be modified. The default filter looks like this:

Code: Select all

        static bool DefaultCCDFilter(Entity entity, Collidable other, CollidablePairHandler pair)
        {
            return pair.broadPhaseOverlap.collisionRule < CollisionRule.NoSolver;
        }
Making an allowance for particular pairs involving an arrow or simply returning true would allow it to run.
Telanor
Posts: 57
Joined: Sun May 06, 2012 10:49 pm

Re: Weld one entity to another on collision

Post by Telanor »

Modifying the CCDFilter to return true for arrows fixed it. Now the arrows stop exactly on the surface every time. Everything is working quite nicely now, thanks for the help!
Mokgra
Posts: 11
Joined: Wed Oct 24, 2012 3:16 am

Re: Weld one entity to another on collision

Post by Mokgra »

Norbo mentioned removing the arrow's Box from the Space as a simple solution. I'm attempting that...

I am hooked onto the arrow's InitialCollisionDetected event. When that fires I do space.Remove(sender.Entity).

This works fine for a while, but if I get going shooting arrows rapid style I get an exception:
"The object does not belong to this space; cannot remove it."

What else would I need to do to prevent this from happening?
Telanor
Posts: 57
Joined: Sun May 06, 2012 10:49 pm

Re: Weld one entity to another on collision

Post by Telanor »

I get that a lot. Make sure that you're using Space.SpaceObjectBuffer.Remove and that you don't try to remove the same object multiple times
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Weld one entity to another on collision

Post by Norbo »

Removing objects multiple times is indeed the cause. Since InitialCollisionDetected is deferred, the object could accumulate quite a few instances of the event due to multiple collisions in one frame. Trying to remove the object from each event will fail on the second attempt. This is more noticeable when firing many arrows since individual objects are more likely to have more than one event in one frame.

[In contrast to deferred events, immediate events execute in-line and would technically allow you to call Space.Remove before the next event ran, but it would fail due to asynchronous access and bookkeeping corruption. Removing objects from beneath the feet of the system while it's running does not work well :) Space.SpaceObjectBuffer.Remove allows you to safely enqueue an object for removal from an immediate event, though later immediate events will still execute so this is similar to using deferred events.]
Mokgra
Posts: 11
Joined: Wed Oct 24, 2012 3:16 am

Re: Weld one entity to another on collision

Post by Mokgra »

Another solution I found was to check if the entity.space property was null. Is there any snafus with that (other than efficiency and extra events firing)?

So I can remove the entity from just the Space.SpaceObjectBuffer? Or do I need to do both Space and Space.SpaceObjectBuffer?
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Weld one entity to another on collision

Post by Norbo »

Another solution I found was to check if the entity.space property was null. Is there any snafus with that (other than efficiency and extra events firing)?
That'll work fine.
So I can remove the entity from just the Space.SpaceObjectBuffer? Or do I need to do both Space and Space.SpaceObjectBuffer?
Adding/removing with the Space.SpaceObjectBuffer just enqueues and object for addition to or removal from the Space later on. It's just deferring the call to Space.Add or Space.Remove to a time when it is safe to do so (the beginning of the next time step); that's why Space.SpaceObjectBuffer is only needed in dangerous situations like asynchronous access. So, it is unnecessary and incorrect to call both Space.Remove and Space.SpaceObjectBuffer.Remove, because that would end up calling Space.Remove twice.
Mokgra
Posts: 11
Joined: Wed Oct 24, 2012 3:16 am

Re: Weld one entity to another on collision

Post by Mokgra »

One option would be to control the orientation of the arrow based upon the linear velocity. Align the local forward vector of the arrow with the linear velocity direction. Beware of the zero-velocity singularity.
How would I go about doing this exactly? I've tried calculating a set of angle for orientation based on the linear velocity, but I can't seem to get the math right. FYI the yaw for my arrow needs a (yaw - PI/2) modifier to start straight (pun intended).
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Weld one entity to another on collision

Post by Norbo »

The easiest way would be to just grab the Forward (or Right, or Left) vector from the entity's OrientationMatrix. There's a property for them. This is equivalent to taking something like (0,0,1) and transforming it by the rotation matrix, except because a rotation matrix is an orthonormal basis, you can just read the appropriate direction right out of the matrix's components without any extra math.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Weld one entity to another on collision

Post by Norbo »

Oops, the above post was a bit backward as I was doing three things at once :)

To generate an orientation matrix from a direction, you have to decide on the remaining degree of freedom. Any method of creating a complete basis from one direction will do the trick. For example, taking the cross product of Vector3.Up and the DesiredForwardDirection gives you another axis in the basis (so long as the DesiredForwardDirection is not parallel to Vector3.Up, in which case you'd need to use Vector3.Right or something). Normalize that new axis, then take the cross product of it and the DesiredForwardDirection again to get the third axis. This set of three vectors defines an orthonormal basis, which is a rotation matrix. Note that the inverse of a rotation matrix is its transpose; if things end up doing the opposite of what you want, this is probably related.

Another less involved option would be to use Toolbox.GetQuaternionBetweenNormalizedVectors with a parameter of the current 'forward' direction of the entity and the other parameter of DesiredForwardDirection (from the velocity). Then, transform the current entity rotation by the change returned. (Beware the multiplication order!)
Post Reply