Page 1 of 1

Motors and attachment points?

Posted: Thu Aug 04, 2011 3:22 am
by speciesUnknown
it looks as if motors can only be attached between two objects at the centre point, but I want to be able to throw in an arbitrary attachment point. here are some use cases I need to consider:

the turret of a tank relative to the body, rotating on one axis; the attachment point of the turret to the body is not the centre of the tank, it is offset in the X and Z dimensions. This axis needs to stay relative to the position of the tank (i.e. if the tank is sideways on a hill, the turret stays in place)

the barrel as it is elevated up and down. this needs to be relative to the turret, but again, not attached at the centre.

part of the barrel flies inward using a linear motor, relative to the turret, which is relative to the tank.

I've read about the various joints and motors which are available, but I dont understand how to set the attachment point. What I want to do is implement the earlier suggestion of having a single dynamic body with various kinematic bodies, without any solver settings on them, attached to that body, via a motor.


[update]

ive looked at the ragdoll in UnfortunateGuyDemo and it appears that joints do allow arbitrary attachment points, but I only see 1 attachment point in this code,

Code: Select all

            var ballSocketJoint = new BallSocketJoint(torso, upperArm, torso.Position + new Vector3(1, .7f, 0));
            Space.Add(ballSocketJoint);
how does this know where the attachment points are on both objects? It appears I've missed something critical in how this works.

Re: Motors and attachment points?

Posted: Thu Aug 04, 2011 4:13 am
by Norbo
The anchor point passed into the constructor is the world space location from which the local offsets are computed. For constraints that do store local offsets, there are properties reflecting the local offsets. The local offsets are also settable after the fact, but it's usually easier to just use the up-front world space parameter.

Here's an example that shows a tank-like body connected to its turret using a revolute joint. This allows it to swivel around the Y axis. The turret and its rotation axis are offset backwards.

Code: Select all

            Box tankBody = new Box(new Vector3(0, 5, 10), 3.5f, 2, 6, 30);
            CompoundBody turret = new CompoundBody(
                new List<CompoundShapeEntry>() 
                {
                    new CompoundShapeEntry(new BoxShape(.5f, .5f, 4), tankBody.Position + new Vector3(0, 1.5f, 2), 5),
                    new CompoundShapeEntry(new BoxShape(1.5f, .7f, 2f), tankBody.Position + new Vector3(0, 1.5f, -1), 5)
                }, 10);
            RevoluteJoint axisJoint = new RevoluteJoint(tankBody, turret, tankBody.Position + new Vector3(0, 1, -1), Vector3.Up);

            Space.Add(tankBody);
            Space.Add(turret);
            Space.Add(axisJoint);
With the BEPUphysicsDrawer, you can see the local offsets from the centers of mass of each connected object to the anchor point, along with the current position error at the joint. The drawer also has debug drawing for most of the other constraint types too, which could help get things set up.
What I want to do is implement the earlier suggestion of having a single dynamic body with various kinematic bodies, without any solver settings on them, attached to that body, via a motor.
I don't think this will work as you expect. A motor cannot change a kinematic entity's velocity because a kinematic entity has infinite inertia. Instead, all the force will be fed into the single connected dynamic body. If you want to use physical constraints between objects to simulate the connections, then the objects involved should be dynamic.

Also note that CollisionRule.NoSolver will not stop constraints from working on an entity; the "NoSolver" only applies to collisions.

Re: Motors and attachment points?

Posted: Thu Aug 04, 2011 7:40 am
by speciesUnknown
So how would I use a motor on these two bodies? I've looked through the interfaces of the various motors and cant see how to deal with the offset between bodies. It seems like the best option would be to use the servo mode of a motor to rotate the turret of the vehicle. What would the code look like to add a motor to the sample you gave?

I considered at first using a kinematic body and setting the absolute position, but this causes a problem - since the position is set after a complete physics update, the turret will be behind in its relative position, by 1 frame.

Re: Motors and attachment points?

Posted: Thu Aug 04, 2011 7:58 am
by Norbo
So how would I use a motor on these two bodies? I've looked through the interfaces of the various motors and cant see how to deal with the offset between bodies. It seems like the best option would be to use the servo mode of a motor to rotate the turret of the vehicle. What would the code look like to add a motor to the sample you gave?
The RevoluteJoint is a group of other constraints with a motor within it which starts out inactive. So first, turn it on:

Code: Select all

            axisJoint.Motor.IsActive = true;
If you wanted a velocity motor that just spins with a given goal velocity:

Code: Select all

            axisJoint.Motor.Settings.VelocityMotor.GoalVelocity = 1;
If you wanted the motor to target a specific angle, instead of a goal velocity:

Code: Select all

            axisJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
            axisJoint.Motor.Settings.Servo.Goal = MathHelper.PiOver4;
There may be some confusion about the fact that the angular motors do not appear to care about linear offsets or anchor points. They work entirely on the angular degrees of freedom between the connections, leaving the linear degrees to other constraints, like the BallSocketJoint. The solver converges to a solution that satisfies both the angular and linear degrees of freedom.

Note that there's nothing special about the RevoluteJoint and the other SolverGroup types. It's just a convenient grouping of commonly associated constraints. Technically, grouping multiple constraints that connect the same two objects together within a SolverGroup (a prefab like RevoluteJoint or a CustomizableSolverGroup) will scale a tiny fraction better in multithreading, though it's pretty much impossible to measure.
I considered at first using a kinematic body and setting the absolute position, but this causes a problem - since the position is set after a complete physics update, the turret will be behind in its relative position, by 1 frame.
The position update is the last physical stage that happens in a time step, so setting another object's position after the time step should work.

Re: Motors and attachment points?

Posted: Thu Aug 04, 2011 8:09 am
by Norbo
An extra note about the 'frame behind' problem:
Are you using internal time stepping? When a time is passed into the Space.Update method, the space will take as many time steps are necessary to reach the accumulated elapsed time. In this case, if you are setting the position externally to the Space.Update method, there may be a time step that occurs where the kinematics are not synchronized with the dynamic object. Note that in this situation, the graphic will still appear properly synchronized. If your graphics are not synchronized, something else is going on.

To resolve this, the position of the kinematics can be updated in the time step. There are a few ways to do this.
1) All update stages in the engine have a Starting and Finishing event. You could attach an event handler to the Finishing event of the Space.PositionUpdater and update the positions there.

2) There exists a convenience system built in that allows you to add objects which are updated by the space at various stages. The stage you'd be interested in is the Space.EndOfTimeStepUpdateables. To make use of this, you could make a class that inherits from Updateable and implements IEndOfTimeStepUpdateable. The positions could be updated in the update method required by the interface. Add the updateable to the Space like you would an entity, and the space will call it.

3) You could attach an event handler to the dynamic entity's PositionUpdated event. This does not get called when the entity is inactive, but it doesn't move while inactive, so this is probably fine :) You also have to take some care when handling shared data because the PositionUpdated event is called from a multithreaded context if the engine is using multiple threads. On the other hand, you get easy multithreading for free doing it this way.