Bear with me, I'm new to Bullet and pretty much coding in general. I'm basically a 3d guy who got carried away creating a tool for my 3d app;)
So, I've been trying to utilize Bullet in a plugin I've been writing for Cinema 4D. For the most part things have been going well until I tried to update the objects in the 3d scene with the bullet transforms from their respective Bullet collision shapes. Position seems to be working without any issue, it's rotations that I'm having trouble with. Since Bullet is designed primarily for games, the solution to keeping dynamic objects in sync with their 3d scene counterpart is proving elusive.
Here are some technical details:
-C4D uses a Left-handed coordinate system, so I'm inverting the x-axis for position transforms and the z-axis (Roll axis) for rotation transforms when transferring data to and from bullet.
-I'm using a tri-mesh representation of the c4d objects to construct my bullet shapes (dynamic objects use a convex hull derived from the trimesh and static collision objects just a straight btbvhTriangleMeshShape)
-The point positions of the triangles used to construct the shape are given in local coordinates (in relation to the object's axis/origin) by c4d.
-I am scaling the world. By default this value is set to 100, and I've reviewed the wiki article on that subject and am pretty sure I have that all set up correctly. Rotation values, of course, are not being scaled.
-C4D does have a Quaternion class... but there aren't any examples of its use; so I'm just passing euler angles into and out of the collision shapes. I know there's the possibility that this might be an interpolation issue that quaternions could solve, so if that's the case I'll just get to learning how to use c4d quaternions:)
Here's my collision object creation function, which gets called on every object flagged as a collision object in the c4d scene:
Code: Select all
Bool BulletHook::ProcessCollisionObject(BaseObject* op, BaseContainer *bc)
{
BaseObject *opClone = (BaseObject*)op->GetClone(COPY_NO_HIERARCHY, NULL);
PolygonObject *polymesh = ToPoly(opClone);
CPolygon *tris;
LONG triCount;
LONG j = 0;
LONG pIndex1, pIndex2, pIndex3;
tris = polymesh->GetPolygonW();
//Mesh Triangulation for CollisionShape processing
ModelingCommandData cd;
cd.doc = GetActiveDocument();
cd.op = opClone;
if (!SendModelingCommand(MCOMMAND_TRIANGULATE, cd)) return FALSE;
tris = polymesh->GetPolygonW();
Vector* point = polymesh->GetPointW();
triCount = polymesh->GetPolygonCount();
mTriMesh = new btTriangleMesh();
//I know, a for loop would probably be better...
while(j<triCount)
{
// For whatever your source of triangles is
// give the three points of each triangle:
pIndex1 = tris[j].a;
pIndex2 = tris[j].b;
pIndex3 = tris[j].c;
point[pIndex1] = point[pIndex1]/WorldScale;
point[pIndex2] = point[pIndex2]/WorldScale;
point[pIndex3] = point[pIndex3]/WorldScale;
btVector3 v0(-point[pIndex1].x,point[pIndex1].y,point[pIndex1].z);
btVector3 v1(-point[pIndex2].x,point[pIndex2].y,point[pIndex2].z);
btVector3 v2(-point[pIndex3].x,point[pIndex3].y,point[pIndex3].z);
//Then add the triangle to the mesh:
mTriMesh->addTriangle(v0,v1,v2);
j++;
}
// Now use mTriMeshShape as your collision shape.
// Everything else is like a normal rigid body
btScalar mass(bc->GetReal(ID_MASS));
//rigidbody is dynamic if and only if mass is non zero, otherwise static
bool isDynamic = (mass != 0);
btVector3 localInertia(0,0,0);
if (isDynamic)
{
tmpshape = new btConvexTriangleMeshShape(mTriMesh);
btShapeHull *hull = new btShapeHull(tmpshape);
btScalar margin = tmpshape->getMargin();
hull->buildHull(margin);
tmpshape->setUserPointer(hull);
triMeshObjects = tmpshape;
triMeshObjects->calculateLocalInertia(mass,localInertia);
}
else
{
triMeshObjects = new btBvhTriangleMeshShape(mTriMesh,true);
}
//Get position and rotation info from c4d object to pass to CollisionShape
Vector pos = op->GetMg().off/WorldScale;
Vector rot = op->GetRot();
btTransform myTransform;
myTransform.setIdentity();
myTransform.setOrigin(btVector3(-pos.x,pos.y,pos.z));
btQuaternion btRot;
btRot.setEuler(rot.x, rot.y, -rot.z);
myTransform.setRotation(btRot);
{
//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
btDefaultMotionState* myMotionState = new btDefaultMotionState(myTransform);
btRigidBody::btRigidBodyConstructionInfo rbInfo(mass,myMotionState,triMeshObjects,localInertia);
btRigidBody* body = new btRigidBody(rbInfo);
body->setRestitution(bc->GetReal(ID_BOUNCE));
body->setFriction(bc->GetReal(ID_FRICTION));
//add the body to the dynamics world
m_dynamicsWorld->addRigidBody(body);
}
return true;
}And the function that steps the simulation:
Code: Select all
Bool BulletHook::RunSim(BaseDocument *doc)
{
Real docfps = doc->GetFps();
m_dynamicsWorld->stepSimulation(1/docfps, 2, btScalar(1.)/btScalar(60.));
for (int j=m_dynamicsWorld->getNumCollisionObjects()-1; j>=0 ;j--)
{
btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[j];
btRigidBody* body = btRigidBody::upcast(obj);
if (body && body->getMotionState())
{
btTransform trans;
body->getMotionState()->getWorldTransform(trans);
//Fill vectors to pass back to c4d object
Vector pos = Vector(-(trans.getOrigin().getX()),trans.getOrigin().getY(),trans.getOrigin().getZ())*WorldScale;
Vector rot = Vector(trans.getRotation().getX(),trans.getRotation().getY(),-(trans.getRotation().getZ()));
//The tag is used to identify the object it's attached to as a collision object
BaseTag *cTag = (BaseTag*)uniqueObject[j].object->GetLink(doc,0);
BaseObject *cObj = cTag->GetObject();
if (cObj)
{
cObj->SetRot(rot);
cObj->SetPos(pos);
}
}
}
return TRUE;
}http://www.kvbarnum.com/Files/Bullet_Woes.mov
The first drop seems ok, the second drop is where the real oddity happens. I don't know what's up with the 3rd drop, you'd think with the rotation values reset to 0 it would be similar to the first... but it isn't.
So, there it is. If anyone can give me some advice on rotations and in general keeping objects in sync with the simulation I'd be much appreciated:)
thanks,
kvb