MalcolmB wrote:Is the COM calculated as an offset by Bullet from the center of the mesh (which bullets calculates automatically from the point cloud)?
I don't know it exactly... I've always thought that Bullet always places the center of mass of a shape in the (0,0,0) point of it, regardless the shape type (compound or hull). This means that a hull with a point cloud centered in a point!=(0,0,0) (say (1,1,1)) will have a center of mass offset implicitly defined ((-1,-1,-1) in this case, because the (0,0,0) point in the point cloud stays (-1,-1,-1) units away from the center of the shape, which is (1,1,1)). When you "drive" rigid bodies through a transform, it should be relative to this same point. This is how I understand the situation, but I may be wrong...
Maybe you just want that the center of mesh supplied by the user is not used by default as a center of mass by Bullet, and want the COM to be the center of the mesh (or you want the user to insert a new COM for it, but I'm considering the simpler case now): in this case is probably better to center the mesh you feed Bullet with, AND to add a graphic offset through a motion state (actually the mesh won't have any COM offset applied (COM=real center of the mesh), but you still need a motion state with COM offset, since you "draw" your mesh from a point that is not used by Bullet as a center of mass (technically it's a "graphic offset"). Remember that by doing so, when you use the motion state to get/set transforms, they will be offset so that they are relative to the original geometric center of input mesh (that was not (0,0,0)); if you set/get them without using the motion states they refer to the center of mass of the body, which is (0,0,0)).
... I think it's very difficult to talk about these topics so that people can easily understand what one means...
Anyway in the example you provided you made a "double step": convex hull inside a compound shape... I suggest you simplify it into a single step to avoid confusion.
Please note that since BulletOpenGLSupport.lib can only draw collision shapes (and not the graphic meshes that "dress" them), you don't need to add motion states with a COM offset (i.e. graphic offset), even if you create shapes with a COM offset (and if you do you should experience a wrong graphic offset). A limitation of it is that you can't retrieve (or set) a "transform outside the COM" from COM offset bodies in the Bullet demos, because you don't pass the graphic offset to the motion state.
Here is a small example of what I meant above, with a little hack that allows users to specify a "transform outside COM" when positioning the bodies (warning: it lacks a bit of testing):
Code: Select all
// In BasicDemo::initPhysics():
{
btCollisionShape* shapeWithNoCOMOffset = new btBoxShape(btVector3(1,8,0.75)); // Half dimensions
m_collisionShapes.push_back(shapeWithNoCOMOffset);
const btScalar mass = 1;
const btTransform baseT(btQuaternion::getIdentity(),btVector3(0,8,0));
btRigidBody* body;
btVector3 COMOffset(0,0,0);
btTransform T; // Transform that does NOT depend on the COM offset
int cnt=0;
for (int i = -2;i<3;i++) {
T = baseT;
T.getOrigin().setX((btScalar)i*2);
COMOffset.setY((btScalar)i*4);
body = localCreateRigidBodyWithCOMOffset(mass,T,shapeWithNoCOMOffset,COMOffset);
if (body->getCollisionShape()!=shapeWithNoCOMOffset) m_collisionShapes.push_back(body->getCollisionShape());
// But the returned transforms are still relative to the center of mass, if we don't add it to the motion state...
btTransform wt;
((btDefaultMotionState*) body->getMotionState())->getWorldTransform(wt);
const btVector3& origin = wt.getOrigin();
printf("T.origin() %1d) (%1.4f,%1.4f,%1.4f)\n",cnt++,origin.x(),origin.y(),origin.z());
}
}
// Added Class Method:
btRigidBody* BasicDemo::localCreateRigidBodyWithCOMOffset(float mass, const btTransform& startTransform,btCollisionShape* shapeWithNoCOMOffset,const btVector3& COMoffset)
{
btAssert((!shape || shape->getShapeType() != INVALID_SHAPE_PROXYTYPE));
btCollisionShape* shape = shapeWithNoCOMOffset;
const btTransform COMOffsetTransform(btTransform(btQuaternion::getIdentity(),COMoffset));
const btTransform inverseCOMOffsetTransform = COMOffsetTransform.inverse();//(btTransform(btQuaternion::getIdentity(),-COMoffset));
if (COMoffset!=btVector3(0,0,0)) {
//Wrap it inside a btCompoundShape (TODO WARNING for unsupported shapes)
btCompoundShape* cs = new btCompoundShape();
cs->addChildShape(inverseCOMOffsetTransform,shapeWithNoCOMOffset);
shape = cs;
}
//rigidbody is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0.f);
btVector3 localInertia(0,0,0);
if (isDynamic)
shape->calculateLocalInertia(mass,localInertia);
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
btDefaultMotionState* myMotionState;
if (COMoffset==btVector3(0,0,0)) myMotionState = new btDefaultMotionState(startTransform);
else {
const btTransform transformedStartTransform(startTransform.getBasis(),startTransform.getOrigin()+startTransform.getBasis()*(COMoffset)); // To test: startTransform.getBasis()!=identity
myMotionState = new btDefaultMotionState(transformedStartTransform); // Here we SHOULD add COMOffsetTransform (or inverseCOMOffsetTransform according on how it is used inside), but everything added here
// seems to result in a wrong graphic offset (that does not happen if nothing is added).
// We can always derive from btDefaultMotionState, and just add an additional transform or vector variable (that is never touched) just to store the COM offset, so that it can be used to get a transform outside COM for rigid bodies, but this is more like a hack than a solution...
}
btRigidBody::btRigidBodyConstructionInfo cInfo(mass,myMotionState,shape,localInertia);
btRigidBody* body = new btRigidBody(cInfo);
body->setContactProcessingThreshold(m_defaultContactProcessingThreshold);
m_dynamicsWorld->addRigidBody(body);
return body;
}
MalcolmB wrote:I have things working fine in cases where the mesh is centered in the origin, but I'd like to avoid this rule to allow my application to be easier to use.
Well, I usually prefer centering input meshes (for both graphic rendering and physics) and then I can apply a COM offset together with a graphic offset (so that things are a bit simpler). I must admit that I've never tried to do what you're trying to achieve; that's why I'm not 100% sure that my answer is correct/helpful

.