Improving collision between dynamic and kinematic entities

Discuss any questions about BEPUphysics or problems encountered.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Improving collision between dynamic and kinematic entiti

Post by Norbo »

More conscisely, joints operate on a sort of bone-like hierarchy, where connected joints' transforms trickle down, but work seperately from any other non-connected motors and constraints acting on those entities, correct?
Yup. A constraint operates locally on connected objects.
Null connections are like being attached to nothing, correct? I would assume that's what connection to a kinetic entity would do, but then are the objects connected to it pulled towards it?
Null connections are indeed the same as connecting to a kinematic entity. Objects would not be pulled anywhere unless the constraint was set up to do so.

I would recommend just fiddling around in the demos for a while, creating some simple contraptions. It's fun and should make things clearer :)
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

Alright, so one more quick question. Would attaching a SingleEntityLinearMotor to the base of the structure also perform all subsequent transforms? I would assume not, but I thought I'd ask.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Improving collision between dynamic and kinematic entiti

Post by Norbo »

I'm not sure exactly what you mean. The motor will affect the entity with which it is associated. If that entity is connected to other objects with constraints, those objects will also be affected. There is no directed skeletal graph that the engine uses to apply forces; there's just a bunch of connections between entities which act locally. Conceptually, the two-entity joints behave like real-world joints. One-entity constraints are a bit more 'magical' (there are not too many sources of free energy in reality) but fit into the same framework.
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

Okay.

I tried setting up RevoluteJoints with rollers and machines, but it didn't quite work like I hoped. I think what I'm looking for is more like "parenting" objects, where I have one base entity with other entities parented to it, and these entities can have seperate forces working on them while additional forces from the parent also act on them, but they aren't "connected," per se. Joints connect two objects at a point, but that's not exactly what I want, I just want forces to trickle down through a hierarchy. If we were using an actual conveyor belt, I could attach a bunch of rectangles to each other with door-hinge-like joints and then increase the velocity of one of them, and if the joints were set up correctly it would move all of them, since they constrict relative velocities. Or something like that. But rollers are almost more difficult, because they roll on an axis attached to something else. I think I'm kind of rambling now, just ignore everything about conveyor belts.

Summary: Is there something similar to joints in that you can "connect" entities and have forces trickle down through local space, but dissimilar in that they aren't exactly joints, but rather forces that make sure the relative positions of two entities are the same? I guess I could code something by having a class that has two entities, the parent and the child, and keeps track of the movement of the parent from this frame to the next frame (assuming velocity remained constant between the time of calculation and the next frame), then apply that velocity to the child, which may or may not have it's own connector. And if the child had a connector, it would then repeat the same process. Something like that.

I'm attempting to say the same thing over multiple times in an attempt to not be confusing (which I have a tendancy to do), so if you understand one part but not another just go with the part you understand.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Improving collision between dynamic and kinematic entiti

Post by Norbo »

There are quite a few constraint types, and there are quite a few ways to configure them to do just about anything involving two entities. For example:
but rather forces that make sure the relative positions of two entities are the same?
A WeldJoint does that :) The WeldJoint is a combo-joint made of a BallSocketJoint (3DOF linear) and a NoRotationJoint (3DOF angular).
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

Oh. But, if I apply a SingleEntityAngularMotor (or another non-joint force) to a roller with a WeldJoint connecting it and the machine, will it rotate like I expect or will the WeldJoint keep it welded in place?

I just realized I think I misunderstood joints. I was thinking they constricted freedoms period, but they just constrict freedoms between two entities, without affecting other forces on it.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Improving collision between dynamic and kinematic entiti

Post by Norbo »

A weld joint welds two objects together. So, a SingleEntityAngularMotor affecting one entity will exert torque on that entity, and because the other entity is connected, it will be dragged along too. The distribution of forces follows physics. Imagine twisting or pulling part of a contraption in real life.

If you want to have a roller or something parented to another object while still allowing one degree of freedom, use a BallSocketJoint and an angular complement constraint which does what you want- perhaps a RevoluteAngularJoint, which restricts two angular degrees of freedom. The RevoluteJoint combo-joint would do the trick (it is composed of a BallSocketJoint, a RevoluteAngularJoint, an optional RevoluteLimit, and an optional RevoluteMotor).
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

So... new RevoluteJoint(base, roller, rotationAxis)? I had this setup:

Code: Select all

SwivelHingeJoint worldToBase = new SwivelHingeJoint(null, base, centerOfRotation, rotationAxis);
RevoluteJoint baseToRoller = new RevoluteJoint(base, roller, rotationAxis);
Then it complained about the axes not being perpendicular with the SwivelHingeJoint or something like that, so I changed the worldToBase joint to be a RevoluteJoint with offset in the entities. But that didn't work, it was being all wonky and no-good.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Improving collision between dynamic and kinematic entiti

Post by Norbo »

If the swivel hinge joint complains about about not being perpendicular, that means something went wrong with the setup. The SwivelHingeJoint is a combo joint which tries to guess your intent, but it is possible to confuse it with certain input. To address this, set the angular joint's twist and hinge axes directly. However, make sure a SwivelHingeJoint is actually what is needed. The pictures associated with the constraints can make this easier: http://bepuphysics.codeplex.com/wikipag ... umentation

As for being wonky and/or no-good, the configuration is likely wrong. Check out the BEPUphysicsDemos joint-involving demos and try to make some very simple contraptions within it so you can see the visualizations directly. If there's a particular joint you want to make but can't get set up, draw up a little diagram of the connected entities and desired joint and I'll try to help.
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

While I'm working on this, one typo and a suggestion. The Box constructor talks about the parameter for height twice (position, dimensions, no mass), and I suggest that you might change Space.Add's signature to this:

Code: Select all

public void Add(params ISpaceObject[] objects)
All it would be is making a for loop for the count of the objects, and making the other Add a protected method. Just makes it a little easier for the user, because it doesn't break existing code but cleans up initalization where you're adding lots of objects (plus it'll take a single array as a parameter too, as an added bonus).
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

So... revoluteMotor.Limit.Minimum/MaximumAngle. What orientation corresponds to 0? Because I set these to 0 and pi/2, but that gave me a V-shaped limit. Also, an initial goal orientation of zero is, like... 45 degrees on Z.

Also, I got it to work doing this:

Code: Select all

Entity ground = new Box(Vector3.Zero, 30, 1, 30);
Box platform = new Box(new Vector3(0, 1.5f, 0), 3, .5f, 3, 10);
Cylinder roller = new Cylinder(platform.Position + new Vector3(0, .5f, 0), 3, .25f, 10);
roller.Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitX, MathHelper.PiOver2);

baseJoint = new RevoluteJoint(null, platform, new Vector3(1.5f, 1.5f, 0), Vector3.UnitZ);
baseJoint.Motor.IsActive = true;
baseJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
baseJoint.Motor.Settings.Servo.Goal = MathHelper.PiOver2 + MathHelper.PiOver4;
baseJoint.Limit.MaximumAngle = MathHelper.PiOver2 + MathHelper.PiOver4;
baseJoint.Limit.MinimumAngle = MathHelper.PiOver4;
baseJoint.Limit.IsActive = true;
baseJoint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
baseJoint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 10;
baseJoint.Motor.Settings.Servo.BaseCorrectiveSpeed = MathHelper.PiOver2 / 1.5f;

rollerToBase = new RevoluteJoint(platform, roller, roller.Position, Vector3.UnitZ);
rollerToBase.Motor.IsActive = true;
rollerToBase.Motor.Settings.Mode = MotorMode.Servomechanism;
I still don't understand the min/max angles, though. Those angles cause the platform to lie flat on the ground, then rotate so it's pointing straight up on Y, so I would think I'd need, like... 0 and pi/2 or pi/2 and pi, or something like that.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Improving collision between dynamic and kinematic entiti

Post by Norbo »

The Box constructor talks about the parameter for height twice (position, dimensions, no mass), and I suggest that you might change Space.Add's signature to this:
Thanks for the typo report.

Unfortunately, params triggers an allocation. It's very minor, but the engine has pretty strict restrictions on runtime allocations. You might see a IList overload later if batch adds and removes become functionally meaningful, though.
So... revoluteMotor.Limit.Minimum/MaximumAngle. What orientation corresponds to 0? Because I set these to 0 and pi/2, but that gave me a V-shaped limit. Also, an initial goal orientation of zero is, like... 45 degrees on Z.
The meaning of angles depends on your configuration (entity states, constraint basis, etc.). A hinge, for example, can be configured such that a door being closed is 0 and being opened is Pi/2, or it can be configured such that a door being open is 0 and closed is Pi/2 (or an infinite number of other options).
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

Norbo wrote: Unfortunately, params triggers an allocation. It's very minor, but the engine has pretty strict restrictions on runtime allocations. You might see a IList overload later if batch adds and removes become functionally meaningful, though.
Oh, right, it would. I didn't even think of that. Eh, I'll add it myself, then. =p I don't mind the overhead.
Norbo wrote:The meaning of angles depends on your configuration (entity states, constraint basis, etc.). A hinge, for example, can be configured such that a door being closed is 0 and being opened is Pi/2, or it can be configured such that a door being open is 0 and closed is Pi/2 (or an infinite number of other options).
Alright. How might one change these configurations... or, rather, which members, properties, and such would change the configuration so I can make sure I can find bugs? If there's a lot, a comprehensive list isn't necessary, just guidelines on what usually does and doesn't.
Norbo
Site Admin
Posts: 4929
Joined: Tue Jul 04, 2006 4:45 am

Re: Improving collision between dynamic and kinematic entiti

Post by Norbo »

Each constraint has a few properties. The RevoluteMotor, for example, has a TestAxis (plus a local version) and a Basis. The Basis is attached to entity A, the test axis is attached to entity B. The test axis is tested against the basis to compute the angle.

Other constraint types have different properties. For angular constraints, the usual fare are axes and bases. For linear constraints, there will also be anchor offsets and things like that. The documentation for individual properties- especially on the Basis properties- is useful for determining how things work together.

So, to use the previously posted code as an example: the pi/4 originates from how the anchor is positioned relative to the connected entities' positions. If the platform is lying flat, the angle between the anchor offsets is Pi/4. The RevoluteJoint constructor is assuming that you want the constraint angles configured according to this. It's a decent heuristic that works intuitively in many cases, but not so much when connecting things to the ghostly World entity. You can, however, configure the joint however you want. For example, if you didn't want to move things around to adhere to the heuristic, you could adjust the constraint like so:

Code: Select all

            Entity ground = new Box(Vector3.Zero, 30, 1, 30);
            Box platform = new Box(new Vector3(0, 1.5f, 0), 3, .5f, 3, 10);
            Cylinder roller = new Cylinder(platform.Position + new Vector3(0, .5f, 0), 3, .25f, 10);
            roller.Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitX, MathHelper.PiOver2);

            var baseJoint = new RevoluteJoint(null, platform, new Vector3(1.5f, 1.5f, 0), Vector3.UnitZ);
            baseJoint.Motor.IsActive = true;
            baseJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
            baseJoint.Motor.Settings.Servo.Goal = MathHelper.PiOver2;

            baseJoint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            baseJoint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 10;
            baseJoint.Motor.Settings.Servo.BaseCorrectiveSpeed = MathHelper.PiOver2 / 1.5f;

            //Adjust the motor basis such that 0 corresponds to the platform being horizontal, and pi/2 to vertical.
            //The first axis is the rotation axis, the second axis is the 'base' direction corresponding to 0 on the measurement plane,
            //and the third axis is the second axis on the measurement plane and completes the basis.
            baseJoint.Motor.Basis.SetWorldAxes(Vector3.Forward, Vector3.Left, Vector3.Up);
            //The test axis is projected onto the measurement plane to determine the angle.  We align it with the base direction we chose initially.
            baseJoint.Motor.TestAxis = Vector3.Left;

            baseJoint.Limit.MaximumAngle = MathHelper.PiOver2;
            baseJoint.Limit.MinimumAngle = 0;
            baseJoint.Limit.IsActive = true;

            //Similar to the above, set up the bases to match the angle expectations.  
            //Note the lack of a third axis- it is implicitly defined by the first two axes.
            baseJoint.Limit.Basis.SetWorldAxes(Vector3.Forward, Vector3.Left);
            baseJoint.Limit.TestAxis = Vector3.Left;


            var rollerToBase = new RevoluteJoint(platform, roller, roller.Position, Vector3.UnitZ);
            rollerToBase.Motor.IsActive = true;
            rollerToBase.Motor.Settings.Mode = MotorMode.Servomechanism;

            Space.Add(ground);
            Space.Add(platform);
            Space.Add(roller);
            Space.Add(baseJoint);
            Space.Add(rollerToBase);
Also note that the alternative empty joint constructor will not try to guess intent or use any heuristics. It just assumes you will configure everything. So, an equivalent configuration without using the helpy-constructor would look like this:

Code: Select all

            Entity ground = new Box(Vector3.Zero, 30, 1, 30);
            Box platform = new Box(new Vector3(0, 1.5f, 0), 3, .5f, 3, 10);
            Cylinder roller = new Cylinder(platform.Position + new Vector3(0, .5f, 0), 3, .25f, 10);
            roller.Orientation = Quaternion.CreateFromAxisAngle(Vector3.UnitX, MathHelper.PiOver2);

            var baseJoint = new RevoluteJoint();
            //The empty constructor does not automatically set anything to active, so we must manually activate and configure everything we want.
            baseJoint.IsActive = true;

            baseJoint.AngularJoint.IsActive = true;
            //When the empty constructor is used, connections should be considered *uninitialized* rather than merely null.
            //This is because directly setting a connection to null will set it internally to the world entity.
            //Leaving it unset will result in null reference exceptions.
            baseJoint.AngularJoint.ConnectionA = null;
            baseJoint.AngularJoint.ConnectionB = platform;
            baseJoint.AngularJoint.WorldFreeAxisA = Vector3.UnitZ;
            baseJoint.AngularJoint.WorldFreeAxisB = Vector3.UnitZ;

            baseJoint.BallSocketJoint.IsActive = true;
            baseJoint.BallSocketJoint.ConnectionA = null;
            baseJoint.BallSocketJoint.ConnectionB = platform;
            var anchor = new Vector3(1.5f, 1.5f, 0);
            baseJoint.BallSocketJoint.OffsetA = anchor; //"World" entity has a position of (0,0,0).
            baseJoint.BallSocketJoint.OffsetB = anchor - platform.Position;

            baseJoint.Motor.IsActive = true;
            baseJoint.Motor.ConnectionA = null;
            baseJoint.Motor.ConnectionB = platform;
            baseJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
            baseJoint.Motor.Settings.Servo.Goal = MathHelper.PiOver2;

            baseJoint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            baseJoint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 10;
            baseJoint.Motor.Settings.Servo.BaseCorrectiveSpeed = MathHelper.PiOver2 / 1.5f;

            //Adjust the motor basis such that 0 corresponds to the platform being horizontal, and pi/2 to vertical.
            //The first axis is the rotation axis, the second axis is the 'base' direction corresponding to 0 on the measurement plane,
            //and the third axis is the second axis on the measurement plane and completes the basis.
            baseJoint.Motor.Basis.SetWorldAxes(Vector3.Forward, Vector3.Left, Vector3.Up);
            //The test axis is projected onto the measurement plane to determine the angle.  We align it with the base direction we chose initially.
            baseJoint.Motor.TestAxis = Vector3.Left;

            baseJoint.Limit.IsActive = true;
            baseJoint.Limit.ConnectionA = null;
            baseJoint.Limit.ConnectionB = platform;
            baseJoint.Limit.MaximumAngle = MathHelper.PiOver2;
            baseJoint.Limit.MinimumAngle = 0;

            //Similar to the above, set up the bases to match the angle expectations.  
            //Note the lack of a third axis- it is implicitly defined by the first two axes.
            //(Small oops here: the basis rotation matrix defaults to the zero matrix for 2d bases in the current version.
            //That's going to be changed, but in the mean time, the rotation matrix needs to be specified explicitly.)
            baseJoint.Limit.Basis.SetWorldAxes(Vector3.Forward, Vector3.Left, Matrix3X3.Identity);
            baseJoint.Limit.TestAxis = Vector3.Left;


            var rollerToBase = new RevoluteJoint();
            rollerToBase.IsActive = true;
            rollerToBase.BallSocketJoint.IsActive = true;
            rollerToBase.BallSocketJoint.ConnectionA = platform;
            rollerToBase.BallSocketJoint.ConnectionB = roller;
            rollerToBase.BallSocketJoint.OffsetA = roller.Position- platform.Position;
            //offsetB is zero since the roller's position is the roller's position.

            rollerToBase.AngularJoint.IsActive = true;
            rollerToBase.AngularJoint.ConnectionA = platform;
            rollerToBase.AngularJoint.ConnectionB = roller;
            rollerToBase.AngularJoint.WorldFreeAxisA = Vector3.UnitZ;
            rollerToBase.AngularJoint.WorldFreeAxisB = Vector3.UnitZ;

            rollerToBase.Motor.IsActive = true;
            rollerToBase.Motor.ConnectionA = platform;
            rollerToBase.Motor.ConnectionB = roller;
            rollerToBase.Motor.Basis.SetWorldAxes(Vector3.Forward, Vector3.Left, Vector3.Up);
            rollerToBase.Motor.TestAxis = Vector3.Left;
            rollerToBase.Motor.Settings.Mode = MotorMode.Servomechanism;

            Space.Add(ground);
            Space.Add(platform);
            Space.Add(roller);
            Space.Add(baseJoint);
            Space.Add(rollerToBase);

I should note that the basis associated with the RevoluteMotor may be changing soon. It implies you have the freedom to assign the third axis to either perpendicular option, but in fact, the third axis is uniquely defined by the first two axes and failure to adhere to this secretive requirement will result in wonky no-goodness. Expect it to become a 2D basis like the RevoluteLimit instead of a 3D basis.
snoozbuster
Posts: 172
Joined: Sat Sep 24, 2011 7:31 am

Re: Improving collision between dynamic and kinematic entiti

Post by snoozbuster »

Awesome, thanks. You're a great help. ^^
Post Reply