What is the best practice to exchange transforms?

Post Reply
lucky7456969
Posts: 26
Joined: Thu Sep 11, 2014 5:40 am

What is the best practice to exchange transforms?

Post by lucky7456969 »

From the GameObject class, I've got a world transformation variable.
So that I can SetRot and SetPos of it at any time any where.
Now that I have added in a physics variable. It's got a motion state in it
I reckon these transformations are incremental.
Should I keep a variable to track the motion state and
a separate variable for other stuff (SetRot etc)
and combine them at the Update function, or
should I pass a pointer to the bullet and
have the transformations changed within the physics class on the fly?
Thanks
Jack
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: What is the best practice to exchange transforms?

Post by drleviathan »

The way I would do it: give the MotionState a btTransform data member that you can update and implement MotionState::getWorldTransform() to apply it.
lucky7456969
Posts: 26
Joined: Thu Sep 11, 2014 5:40 am

Re: What is the best practice to exchange transforms?

Post by lucky7456969 »

drleviathan wrote:The way I would do it: give the MotionState a btTransform data member that you can update and implement MotionState::getWorldTransform() to apply it.
Hello,
Here is what I am doing during a frame render.
I've set the motion state to identity (locally). I don't know, I've used back
the btDefaultMotionState, didn't know if that's correct,
Having a -10 gravity, but even with or without gravity, there are some spinnings of the objects and
flying around up to the sky except the static objects... Why was that happening?

Code: Select all

D3DXQUATERNION BT2DX_QUATERNION(const btQuaternion& q) {
	return D3DXQUATERNION(q.x(), q.y(), q.z(), q.w());
}

D3DXMATRIX BT2DX_MATRIX(const btTransform &ms) {
	btQuaternion q = ms.getRotation();
	btVector3 p = ms.getOrigin();

	D3DXMATRIX pos, rot, world;
	D3DXMatrixTranslation(&pos, p.x(), p.y(), p.z());
	D3DXMatrixRotationQuaternion(&rot, &BT2DX_QUATERNION(q));
	D3DXMatrixMultiply(&world, &rot, &pos);
	return world;
}

void Physics::addMesh(CObjects* pObj) {
	m_pObj = pObj;	  
          
	D3DXVECTOR3 vMin, vMax;
	pObj->m_Mesh->GetBoundingBox(vMin, vMax);
	float width = vMax.x - vMin.x;
	float length = vMax.y - vMin.y;
	float height = vMax.z - vMin.z;

	btBoxShape *shape = new btBoxShape(btVector3(width / 2.0f, length / 2.0f, height / 2.0f));	 

	m_collisionShapes.push_back(shape);
	{
		btScalar mass(10.0f);		
 
		bool isDynamic = (mass != 0.f);

		btVector3 localInertia(0,0,0);
		if (isDynamic)
			shape->calculateLocalInertia(mass,localInertia); 
 
		btDefaultMotionState* ms = new btDefaultMotionState();

		m_pObj->SetSimMotionState(ms);	 

		btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,ms,shape,localInertia);	
		btRigidBody* body = new btRigidBody(rbInfo);			 

		//add the body to the dynamics world
		m_dynamicsWorld->addRigidBody(body);

		 
	}
}
During a frame render,

Code: Select all

btTransform physTrans;
GetSimMotionState()->getWorldTransform(physTrans);
D3DXMATRIX physDXTrans;
physDXTrans = BT2DX_MATRIX(physTrans);
m_matWorld = physDXTrans * m_matWorld; 
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: What is the best practice to exchange transforms?

Post by drleviathan »

What I meant was that you implement your own custom MotionState class (don't use btDefaultMotionState). It might look something like this:

(Disclaimer: I did not compile or test this code so dunno if it actually works...)

Code: Select all

// utilities for translating math objects (implement both directions for symmetry)

D3DXQUATERNION BT2DX_QUATERNION(const btQuaternion& q) {
   return D3DXQUATERNION(q.x(), q.y(), q.z(), q.w());
}

D3DXMATRIX BT2DX_MATRIX(const btTransform &t) {
   btQuaternion q = t.getRotation();
   btVector3 p = t.getOrigin();

   D3DXMATRIX pos, rot, world;
   D3DXMatrixTranslation(&pos, p.x(), p.y(), p.z());
   D3DXMatrixRotationQuaternion(&rot, &BT2DX_QUATERNION(q));
   D3DXMatrixMultiply(&world, &rot, &pos);
   return world;
}

btQuaternion DX2BT_QUATERNION(const D3DXQUATERNION& q) {
    return btQuaternion(q.x, q.y, q.z, q.w);
}

btTransform DX2BT_MATRIX(const D3DXMATRIX& m) {
    // TODO: implement this
}

// make your custom MotionState

class MyMotionState : public btMotionState {
public:

    MyMotionState(CObjects* obj) : m_object(obj) {
        assert(m_object);
    }

    void getWorldTransform(btTransform& worldTrans) {
        // from game to physics
        // Bullet will call this every frame for "kinematic" objects, or it can be called manually
        // when it is time to update the physics engine.
        worldTrans = DX2BT_MATRIX(m_object->getMatWorld());
    }

    void setWorldTransform(const btTransform& worldTrans) {
        // from physics to game
        // Bullet will call this every frame for any "active" object (that has a MotionState).
        m_object->setWorldTransform(BT2DX_MATRIX(worldTrans));
    }

protected:
    CObjects* m_object;
};

void Physics::addMesh(CObjects* pObj) {
   m_pObj = pObj;    
         
   D3DXVECTOR3 vMin, vMax;
   pObj->m_Mesh->GetBoundingBox(vMin, vMax);
   float width = vMax.x - vMin.x;
   float length = vMax.y - vMin.y;
   float height = vMax.z - vMin.z;

   btBoxShape *shape = new btBoxShape(btVector3(width / 2.0f, length / 2.0f, height / 2.0f));   

   m_collisionShapes.push_back(shape);
   {
      btScalar mass(10.0f);      
 
      bool isDynamic = (mass != 0.f);

      btVector3 localInertia(0,0,0);
      if (isDynamic)
         shape->calculateLocalInertia(mass,localInertia);
 
      // Object and MotionState get back pointers both ways
      MyMotionState* ms = new MyMotionState(m_pObj);
      m_pObj->SetSimMotionState(ms);   

      btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,ms,shape,localInertia);   
      btRigidBody* body = new btRigidBody(rbInfo);         

      //add the body to the dynamics world
      m_dynamicsWorld->addRigidBody(body);
   }
}

// there is no need to translate transforms from Bullet to DX during the render pass, 
// because Bullet will call MotionState::setWorldTransform() as part of the DynamicsWorld::stepSimulation()
// but only on objects that have a MotionState.
lucky7456969
Posts: 26
Joined: Thu Sep 11, 2014 5:40 am

Re: What is the best practice to exchange transforms?

Post by lucky7456969 »

Update:
I finally decided to update the object's transform first, next update the physics engine,
But when I debug the final transform at the render method, I find the world transformations
finally shrank back to a very small value.

Update2:
Got a small problem, just don't know why when I pass in the bullet the object's transformation that has a scale of 0.1, when coming back with the interpolated transformation, it returns on 1.0 on every value on the diagonal basis?

Thanks
Jack
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: What is the best practice to exchange transforms?

Post by drleviathan »

I don't think btTransform supports scale. It only supports translation and rotation.
lucky7456969
Posts: 26
Joined: Thu Sep 11, 2014 5:40 am

Re: What is the best practice to exchange transforms?

Post by lucky7456969 »

Hi there,
Thanks
It works now.
But the returned rotations are a bit annoying, which are very close to 0
and accumulating and then those small values get enlarged that would make objects spin around vigorously...
Thanks
Jack
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: What is the best practice to exchange transforms?

Post by drleviathan »

Perhaps your rotation is getting un-normalized? It's very important to normalize quaternions when they are the result of a multiplication between two quaternions, or pulled out of a matrix.

Code: Select all

// this is bad because it can accumulate non-unit quaternion length
rotation = deltaRotation * rotation;

// this is better
rotation = (deltaRotation * rotation).normalize();
The one caveat here is that the normalization is only important if the resulting quaternion is being stored for later, since the non-unity scale of the quaternion will geometrically increase/decrease each iteration. If the quaternion is being computed for some temporary purpose (i.e. for rotating some vectors) and is then discarded then the normalization isn't necessary since there is no feedback loop.

So perhaps your BT2DX_MATRIX() and DX2BT_MATRIX() implementations should be normalizing the quaternions.
Post Reply