MotionStates

From Physics Simulation Wiki

Jump to: navigation, search

Contents

MotionStates

What's a MotionState?

MotionStates are a way for Bullet to do all the hard work for you getting the objects being simulated into the rendering part of your program.

In most situations, your game loop would iterate through all the objects you're simulating before each frame render. For each object, you would update the position of the render object from the physics body. Bullet uses something called MotionStates to save you this effort.

Motion states for objects communicate movement caused by forces in the physics simulation to your program. There are some important exceptions you need to remember.

Static objects don't move so there is no need to communicate movement. They don't need a motion state.

Kinematic objects are controlled by your program and the motion state works in reverse. It communicates movement of your object to bullet so it can detect collisions with it.

Other benefits

There are multiple other benefits of MotionStates:

  • Computation involved in moving bodies around is only done for bodies that have moved; no point updating the position of a render object every frame if it isn't moving.
  • You don't just have to do render stuff in them. They could be effective for notifying network code that a body has moved and needs to be updated across the network.
  • Interpolation is usually only meaningful in the context of something visible on-screen. Bullet manages body interpolation through MotionStates.
  • They're easy

Interpolation

Bullet knows how to interpolate body movement for you. As mentioned, implementation of interpolation is handled through MotionStates.

If you attempt to ask a body for its position, it will return the position at the end of the last physics tick. That's useful for many things, but for rendering you will want some interpolation. Bullet interpolates the transform of the body before passing the value to setWorldTransform.

If you want the non-interpolated position of a body [which will be the position as it was calculated at the end of the last physics tick], use btRigidBody::getWorldTransform() and query the body directly.

So how do I use one?

MotionStates are used in two places in Bullet.

  1. The first is when the body is first created. Bullet grabs the initial position of the body from the motionstate when the body enters the simulation
    • Bullet calls getWorldTransform with a reference to the variable it wants you to fill with transform information
    • Bullet also calls getWorldTransform on kinematic bodies. Please see the section below
  2. After the first update, during simulation Bullet will call the motionstate for a body to move that body around
    • Bullet calls setWorldTransform with the transform of the body, for you to update your object appropriately

To implement one, simply inherit btMotionState and override getWorldTransform and setWorldTransform.

DefaultMotionState

btDefaultMotionState
A basic motion state for creating your rigid body
Full documentation

Even if you don't want a motionstate as it is described here, you will need one to help instantiate a rigidBody. Bullet provides a default motionstate that you can use for this. Simply construct it with the default transform of your body:

btDefaultMotionState* ms = new btDefaultMotionState(btTransform(btQuaternion(0,0,0,1), btVector3(0,10,0)));

The constructor has default parameters that are the identity. If you just want to create a body, you can construct a btDefaultMotionState with no parameters.

Ogre3d

Since Ogre3d seems popular [and I'm using it myself], here's a full implementation of a motionstate for Bullet. Instantiate it with a the initial position of a body and a pointer to your Ogre SceneNode that represents that body. As a bonus, it provides the ability to set the SceneNode much later. This is useful if you want an object in your simulation, but not actively visible, or if your application archictecture calls for delayed creation of visible objects.

class MyMotionState : public btMotionState
{
protected:
    Ogre::SceneNode* mSceneNode;
    btTransform mInitialPosition;

public:
    MyMotionState(const btTransform &initialPosition, Ogre::SceneNode *node)
    {
        mSceneNode = node;
	mInitialPosition = initialPosition;
    }

    virtual ~MyMotionState()
    {
    }

    void setNode(Ogre::SceneNode *node)
    {
        mSceneNode = node;
    }

    virtual void getWorldTransform(btTransform &worldTrans) const
    {
        worldTrans = mInitialPosition;
    }

    virtual void setWorldTransform(const btTransform &worldTrans)
    {
        if(mSceneNode == nullptr)
            return; // silently return before we set a node

        btQuaternion rot = worldTrans.getRotation();
        mSceneNode ->setOrientation(rot.w(), rot.x(), rot.y(), rot.z());
        btVector3 pos = worldTrans.getOrigin();
        mSceneNode ->setPosition(pos.x(), pos.y(), pos.z());
    }
};

And for MOgre:

public class MogreMotionState : MotionState {

	protected SceneNode node;
	protected Matrix4 transform;
	...

	public MogreMotionState(..., Matrix4 transform, SceneNode node) {
		this.node = node;
		this.transform = transform;
		...
	}

	public MogreMotionState(..., Vector3 position, Quaternion orientation, SceneNode node) {
		transform = new Matrix4(orientation);
		transform.MakeTransform(position, Vector3.UNIT_SCALE, orientation);
		this.node = node;
		...
	}

	public MogreMotionState(..., SceneNode node)
		: this(..., node.Position, node.Orientation, node)
	{ }


	public override Matrix4 WorldTransform {
		get {
			return transform;
		}
		set {
			if (node == null)
				base.Dispose();
			node.Orientation = value.ExtractQuaternion();
			node.Position = value.GetTrans();
			...
			transform = value;
		}
	}
}

Kinematic Bodies

If you are using kinematic bodies, then getWorldTransform is called every simulation step. This means that your kinematic body's motionstate should have a mechanism to push the current position of the kinematic body into the motionstate:

class MyKinematicMotionState : public btMotionState {
public:
    MyKinematicMotionState(const btTransform &initialpos) { mPos1 = initialpos; }
    virtual ~ MyKinematicMotionState() { }
    void setNode(Ogre::SceneNode *node) { mVisibleobj = node; }
    virtual void getWorldTransform(btTransform &worldTrans) const { worldTrans = mPos1; }
    void setKinematicPos(btTransform &currentPos) { mPos1 = currentPos; }
    virtual void setWorldTransform(const btTransform &worldTrans) { }

protected:
    btTransform mPos1;
};

To use this, just call setKinematicPos every time that you move your body. This can be combined with the motionstate in the ogre3d section above if you want, by just adding setKinematicPos to your ogre motionstate.

Personal tools