Updating viewport objects

kvb
Posts: 4
Joined: Sat Jul 10, 2010 11:45 pm

Updating viewport objects

Post by kvb »

Hey all,

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;
}
and a quick video showing the results.

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
kvb
Posts: 4
Joined: Sat Jul 10, 2010 11:45 pm

Re: Updating viewport objects

Post by kvb »

Well, I ended up figuring it out:)

In the end I used a btMatrix3x3 that I fill and retrieve rotation values through get/setBasis and the get/setEulerYPR functions of the btMatrix class (with an inverted z-axis). Also discovered an error in the way my trimesh shapes were being generated; for some reason the point vectors weren't scaling according to the world scale, which was also cause for some of the strange behavior in the video, but that's remedied now:)

-kvb
kvb
Posts: 4
Joined: Sat Jul 10, 2010 11:45 pm

Re: Updating viewport objects

Post by kvb »

Even though the main issues I was facing are solved; I'd still appreciate advice on keeping objects up to date with their 3d counterparts.

For instance:

When I flag an object as a collision shape, it's processed and added to the dynamics world. But what happens when the origin or point positions change? How can I "update" the collision shape without having to completely reprocess it. For the most part, completely reprocessing isn't too bad, but there's the possibility of some fairly high poly objects being used as collision shapes. Reprocessing those objects can take a second or two when having to fill a trimesh and I have to keep these objects in sync quite frequently.

Origin updating isn't an issue... it's mostly about if the points changing. I'd rather not have to repopulate a trimesh object fresh (that's where the processing overhead becomes a problem). I haven't found a way to "update" the triangles in a trimesh. So, if anyone knows how that can be done I'd appreciate it:)

-kvb