Character control

gautam
Posts: 8
Joined: Sun Dec 10, 2006 5:14 am

Character control

Post by gautam »

Hi,

I have been able to add Bullet physics to my code and I am trying to create a character controller - however I didn't see any demo on the character control to see how its done in bullet. I am a little new to physics integration and I am not sure how I should approach this.

I am thinking that I should make the object a kinematic actor but I don't see how.

Thanks.
Gautam
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: Character control

Post by Erwin Coumans »

Please make Bullet related postings in the Bullet section. This FAQ section is more general physics questions (not Bullet specific).

As a starting point, you can try to model the character with a dynamic btRigidBody, using a sphere shape or capsule, and set the angular impulse factor to zero:

Code: Select all

 void    setAngularFactor(float angFac)
This will prevent the sphere from rolling, and only sliding. Note you can create a capsule, by using the btMultiSphereShape, and adding 2 spheres at different locations.

I am planning a more advanced custom character controller, but that is not in Bullet 2.39 yet.
Thanks,
Erwin

gautam wrote:Hi,

I have been able to add Bullet physics to my code and I am trying to create a character controller - however I didn't see any demo on the character control to see how its done in bullet. I am a little new to physics integration and I am not sure how I should approach this.

I am thinking that I should make the object a kinematic actor but I don't see how.

Thanks.
Gautam
gautam
Posts: 8
Joined: Sun Dec 10, 2006 5:14 am

Post by gautam »

Hi Erwin,

Thanks for the reply. I will definitely put other bullet requests on this section. I will definitely try that.

I also want to nullify the effect of gravity on one rigid body only - that is the characters - is this possible.

Thanks
Gautam
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

gautam wrote:Hi Erwin,

Thanks for the reply. I will definitely put other bullet requests on this section. I will definitely try that.

I also want to nullify the effect of gravity on one rigid body only - that is the characters - is this possible.

Thanks
Gautam
You can set the gravity per body. Make sure you do this AFTER you add the rigidbody to the world, and after you set the global gravity. Note that dynamicsWorld.setGravity will overwrite gravity of rigidbodies added to the world. So the order makes a difference.

It is also recommended to move the body (character) using setLinearVelocity, instead of directly setting the world transform. If the character still gets some angular drift, you can apply a 'pure angular' hinge, without the translation part: add a usual hinge, with the character 'up' vector as axis, and (0,0,0) as pivot. Then, call 'setAngularOnly(true) for that hinge.

Please let us know if this "setAngularFactor" works out for you.
Erwin
gautam
Posts: 8
Joined: Sun Dec 10, 2006 5:14 am

Post by gautam »

Hi,

Yes the setAngularFactor works for me at the moment but now I have another problem. Whenever 2 rigid body i.e. dynamic body collide the model seems to disappear. However it works fine when colliding with a user defined mesh.

I have taken the liberty to post an image as I may be able to explain better with it.

Image

If you see the image the - sphere collide fine with the floors but when the 2 spheres collide with each other both vanish from the screen.

Is there any specific reason for this to happen. I use a btBvhTriangleMeshShape to create a rigidbody for the floor and register that as the first object in the world. Is this an issue.

Thanks
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Post by Erwin Coumans »

Does this happen only between two character-rigidbodies that have a modified 'angular factor'?

Or does it also happen between two regula' rigidbodies, and/or character versus regular rigidbody (unmodified angular factor)?

Can you share your testbed that reproduces the problem?
Thanks,
Erwin
gautam
Posts: 8
Joined: Sun Dec 10, 2006 5:14 am

Post by gautam »

Erwin Coumans wrote:Does this happen only between two character-rigidbodies that have a modified 'angular factor'?

Or does it also happen between two regula' rigidbodies, and/or character versus regular rigidbody (unmodified angular factor)?
No - this happens even with regular rigid bodies and character rigid bodies. When they collide their position seems to go astray on the y and z coordinates - I start seeing values like the following

-472.421875 -18379186.000000 351334944.000000
-300.936829 4987717632.000000 -2644217856.000000

Erwin Coumans wrote: Can you share your testbed that reproduces the problem?
Thanks,
Erwin
Indeed I can - but its quite a bit of code as I encapsulated a lot of things - so please bear with me.

This is how set up the physics and physics bodies for now.

Code: Select all

CPhysics::GetInstance().InitPhysics();
CPhysics::GetInstance().SetGravity(vector3(0.0f, -10.0f, 0.0f));

model.LoadModel("media/land.flex");
castleObject = new GameObject();
castleObject->RegisterObject();
castleObject->SetModel(&model);
castleObject->SetPosition(vector3(0, -2, 0));
castleObject->SetMass(0.0f);
castleObject->SetPhysics(WORLD);	castleObject->SetCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);

// setup the box information	
box.LoadModel("media/sphere.flex");
boxObject = new GameObject();
boxObject->RegisterObject();
boxObject->SetModel(&box);
boxObject->SetPosition(vector3(-20.0f, 0.0f, -10.0f));
boxObject->SetMass(5.0f);
boxObject->SetCenterofMass(btTransform::getIdentity());
boxObject->SetLinearVelocity(vector3(0.0f, -10.0f, 0.0f));
boxObject->SetScale(vector3(5.0f, 5.0f, 5.0f));
boxObject->SetPhysics(SPHERE);
//boxObject->SetAngularFactor(0.0f);
boxObject->SetPlayerVelocity(vector3(0.1f, 0.1f, 0.1f));
	//boxObject->SetCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
//boxObject->SetActivationState(DISABLE_DEACTIVATION);

sphere.LoadModel("media/sphere.flex");
sphereObject = new GameObject();
sphereObject->RegisterObject();
sphereObject->SetModel(&sphere);
sphereObject->SetPosition(vector3(-20.0f, 20.0f, -15.0f));
sphereObject->SetScale(vector3(5.0f, 5.0f, 5.0f));
sphereObject->SetLinearVelocity(vector3(0.0f, -1.0f, 0.0f));
sphereObject->SetMass(1.0f);
sphereObject->SetPhysics(SPHERE);
SetPhysics function is where I set the physics and attach the body to the btDiscreteDynamicsWorld.

Code: Select all

void GameObject::SetPhysics(RIGIDBODY_SHAPE shape)
{
	btVector3 v = ConvertToBulletVector(m_Position);
	btTransform transform;
	transform.setIdentity();
	transform.setOrigin(v);

	btVector3 scale = ConvertToBulletVector(m_Scale);

	switch(shape)
	{
		case BOX :
			this->m_Body = createRigidBodyBox(scale, transform, m_Mass);
			break;

		case TRIANGLES :
			this->m_Body = createRigidBodyFromTriangles(*m_Model, transform, m_Mass);		
			break;

		case SPHERE :
			this->m_Body = createRigidBodySphere(scale.getX(), transform, m_Mass);
			break;

		case WORLD:
			{
				m_Body = createRigidBodyFromTriangles(*m_Model, transform, 0.0f);
				CPhysics::GetInstance().SetupWorldRigidBody(m_Body);
			}
			break;
		
		default :
			break;
	}

	if(!m_Body->isStaticObject() && !m_Body->isKinematicObject())
	{
		GameObject::SetPhysicsAttribute();
	}

	CPhysics::GetInstance().AddRigidBody(m_Body);
}
The SetPhysicsAttribute sets the various attributes like linear velocity, angular velocity etc.

Code: Select all

void GameObject::SetPhysicsAttribute()
{
	if(m_Body != NULL)
	{
		btVector3 lVel = ConvertToBulletVector(m_LinearVel);
		m_Body->setLinearVelocity(lVel);

		btVector3 aVel = ConvertToBulletVector(m_AngularVel);
		m_Body->setAngularVelocity(aVel);

		m_Body->setRestitution(m_Restitution);

		btDynamicsWorld *m_World = CPhysics::GetInstance().GetPhysicsWorld();
		m_World->getBroadphase()->cleanProxyFromPairs(m_Body->getBroadphaseHandle());
	}
}
These are the functions I use to create rigid bodies for shapes like sphere, box etc - parts of it I have taken directly from the sample code

Code: Select all

btRigidBody* localCreateRigidBody(float mass, const btTransform& startTransform,btCollisionShape* shape)
{
	//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

#define USE_MOTIONSTATE 1
#ifdef USE_MOTIONSTATE
	btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);
	btRigidBody* body = new btRigidBody(mass,myMotionState,shape,localInertia);

#else
	btRigidBody* body = new btRigidBody(mass,startTransform,shape,localInertia);	
#endif//
	return body;
}


btRigidBody *createRigidBodyFromTriangles(CModel &model, const btTransform &startTransform,
										  float mass)
{
	unsigned int numVertices = model.GetNumberOfVertices();
	CustomVertex *vertex = model.GetVertexInfoPointer();
	
	btVector3 *gVertices = new btVector3[numVertices];
	int *gIndices = new int[numVertices];

	for(unsigned int i = 0; i < numVertices; i++)
	{
		gVertices[i] = ConvertToBulletVector(vertex[i].m_Vertex);
		gIndices[i] = i;
	}

	int vertStride = sizeof(btVector3);
	int indexStride = 3*sizeof(int);

	int totalTriangles = numVertices / 3;

	btTriangleIndexVertexArray* indexVertexArrays = new btTriangleIndexVertexArray(totalTriangles,
																					gIndices,
																					indexStride,
																					(int)numVertices,
																					(float*) &gVertices[0].x(),
																					vertStride);

	btCollisionShape* trimeshShape  = new btBvhTriangleMeshShape(indexVertexArrays);
	btRigidBody *body = localCreateRigidBody(mass, startTransform,trimeshShape);

	return body;
}

btRigidBody *createRigidBodyBox(const btVector3 &halfExtents,
								const btTransform &transform, float mass)
{
	btCollisionShape* shape = new btBoxShape(halfExtents);
	btRigidBody *boxBody = localCreateRigidBody(mass,transform,shape);
	return boxBody;
}

btRigidBody *createRigidBodySphere(float radius, const btTransform &transform, float mass)
{
	btCollisionShape* shape = new btSphereShape(radius);
	btRigidBody *sphereBody = localCreateRigidBody(mass,transform,shape);
	return sphereBody;
}
This is how Initialize and update the physics.

Code: Select all

void CPhysics::InitPhysics()
{
	btCollisionDispatcher *dispatcher = new btCollisionDispatcher();

	btVector3 worldAabbMin(-100,-100,-100);
	btVector3 worldAabbMax(100,100,100);

	btOverlappingPairCache* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax,maxProxies);
	btSequentialImpulseConstraintSolver *solver = new btSequentialImpulseConstraintSolver();

	m_bWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver);
	btVector3 gravity = ConvertToBulletVector(m_Gravity);

	m_bWorld->setGravity(gravity);
}

void CPhysics::Update()
{
	m_bWorld->updateAabbs();
	m_bWorld->stepSimulation(0.002f);
}
And this is what ConvertToBulletVector and ConvertFromBulletVector works -

Code: Select all

inline btVector3 ConvertToBulletVector(const vector3 &v)
{
	btVector3 ret;
	
	ret.setX(v.x);
	ret.setY(v.y);
	ret.setZ(v.z);

	return ret;
}

inline vector3 ConvertFromBulletVector(const btVector3 &v)
{
	vector3 ret;
	ret.x = v.getX();
	ret.y = v.getY();
	ret.z = v.getZ();

	return ret;
}

And this is how update the game objects

Code: Select all

void GameObject::UpdateTransform()
{
	btDefaultMotionState *state = static_cast<btDefaultMotionState *>(m_Body->getMotionState());
	float m[16];

	state->m_graphicsWorldTrans.getOpenGLMatrix(m);
	m_Model->SetTransform(m);

	if(!m_Body->isStaticObject())
	{
		FILE *fp = fopen("errorlog", "a+");
		if(fp)
		{
			btTransform tr;
			state->getWorldTransform(tr);

			btVector3 v = tr.getOrigin();
			fprintf(fp, "%f %f %f\n", v.getX(), v.getY(), v.getZ());
			fclose(fp);
		}
	}

}
I am not sure what I am doing wrong. I will be trying to do it without these encapsulation and see how it works.

Please let me know if you need anything else.

Thanks and regards.