contact callbacks and friction

Post Reply
Marcell
Posts: 14
Joined: Fri Oct 19, 2012 8:58 am

contact callbacks and friction

Post by Marcell »

Hello!

I would like to create a perfectly sticky horizontal floor, so that an object touching it would not be allowed to translate or rotate around the vertical axis. I'm having difficulties with the implementation though.

I'm using the contact added, processed and destroyed callbacks to route contact events directly to rigid bodies like so:

Code: Select all


bool customContactAddedCallback(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0, const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1)
{
	RigidBody* obj1 = reinterpret_cast<RigidBody*> (colObj0Wrap->getCollisionObject()->getUserPointer());
	RigidBody* obj2 = reinterpret_cast<RigidBody*> (colObj1Wrap->getCollisionObject()->getUserPointer());

	if (obj1 == 0 or obj2 == 0)
		return false;

	return (obj1->collide(obj2) or obj2->collide(obj1));
}

bool customContactProcessedCallback(btManifoldPoint& cp, void* body0, void* body1)
{
	RigidBody* obj1 = reinterpret_cast<RigidBody*> (static_cast<btCollisionObject*> (body0)->getUserPointer());
	RigidBody* obj2 = reinterpret_cast<RigidBody*> (static_cast<btCollisionObject*> (body1)->getUserPointer());

	if (obj1 == 0 or obj2 == 0)
		return false;

	return (obj1->collide(obj2) or obj2->collide(obj1));
}

bool customContactDestroyedCallback(void* ptr)
{
	return false;
}

extern ContactAddedCallback gContactAddedCallback;
extern ContactProcessedCallback gContactProcessedCallback;
extern ContactDestroyedCallback gContactDestroyedCallback;


BulletSimulation::BulletSimulation()
{
	gContactAddedCallback = customContactAddedCallback;
	gContactProcessedCallback = customContactProcessedCallback;
	gContactDestroyedCallback = customContactDestroyedCallback;
}



Now inside the collide() method of my RigidBody class I tried several things.

1. Using setLinearFactor() and setAngularFactor() to set a factor of 0 on all the right axes. This seems to do the trick, but it's also a permanent modification. And since I'm unable to route the contact destroyed callback to specific rigid bodies, I screw up the physics of my RigidBodies for good.

2. Using setLinearVelocity() and setAngularVelocity() on the right axes to set a velocity of 0. No noticable effect.

3. Setting m_combinedFriction to a high value (1000). This is a property of the contact points that I get passed as an argument (btManifoldPoint) to the callback, so it doesn't fit with my routing to the rigid bodies framework, but at least it does something and increases the friction. It's not perfectly sticky though and I would like to have more.

Can anyone please help me with achieving what I want and make suggestion of what I'm doing right or wrong?
Marcell
Posts: 14
Joined: Fri Oct 19, 2012 8:58 am

Re: contact callbacks and friction

Post by Marcell »

Did I not formulate my question well or why am I not getting any answers?
Megadanxzero
Posts: 13
Joined: Fri Sep 07, 2012 9:50 pm

Re: contact callbacks and friction

Post by Megadanxzero »

I have a feeling this forum has very few users, and most of those that are here are probably here because they need help with something and as such are probably unable to help other people. I'm pretty new to Bullet myself, but I'll have a try anyway though! :3

It seems strange that setting the velocity to 0 wouldn't work, unless there's maybe another type of velocity I don't know about (Rotational velocity or something? That sounds like what AngularVelocity could be though...). That said, if you want the rigid bodies to stop completely as soon as they touch the floor maybe you could try deactivating them? No idea if that would work, since I've not tried it before, but it sounds plausible. I'm not sure if a rigid body retains its velocity if it gets manually deactivated though, so you might need to set the velocity to 0 when you deactivate it anyway.

Also it's possible that, since I think those callbacks happen right in the middle of the physics simulation, Bullet is then overriding the things you're changing with whatever it wants to do? As far as I can tell from a few other threads on the forum it's considered better to look through all the contact manifolds after each simulation step and then call any callbacks from there (As detailed in this thread: http://bulletphysics.org/Bullet/phpBB3/ ... 997#p14944). I switched to using that method because I was having some strange behaviour when I tried using the existing callbacks, and I think it's probably a better idea since that way you wait for Bullet to finish all of its own stuff before you try and change anything.

No idea if any of that'll help, but hopefully it'll give you some stuff to try at least! :3
Marcell
Posts: 14
Joined: Fri Oct 19, 2012 8:58 am

Re: contact callbacks and friction

Post by Marcell »

I managed to put a satisfactory solution in place. I did as suggested and examine the contact manifolds after every simulation tick instead of using the collision callback framework. From the contact manifold I detect contact creation and termination events and dispatch them to the related rigid bodies. This is exactly what the collision callbacks would do probably much faster, but due to the current unfinished state of the contact destroyed callback, I see no other way to accomplish this. For the rigid bodies I provide collide() and uncollide() methods, that set the angular and linear velocity. Below you will find the relevant code and here are some remarks.

- I used Qt's QMultiHash to register contact points. Feel free to use something else (e.g. std::map) if Qt is out of your way.

- I had some difficulties subclassing btRigidBody so I decided to wrap it in my own RigidBody object instead.

Code: Select all


// Steps the simulation forward by one step.
// Currently the simulation rate is fixed at 100 Hz.
// It also updates the collision states of the bodies and calls the contact methods of bodies where appropriate.
void BulletSimulation::step()
{
	dynamicsWorld->stepSimulation(1/100.f, 10, 1/100.f); // 100 Hz

	checkCollisions();
}

// Handles the collision dispatch and calls the contact methods of rigid bodies that are in contact.
// The algorithm examines all collision points reported by the bullet simulation and maintains a data
// structure that allows the detection of new collisions and the removal of contact points after a
// contact has been terminated.
void BulletSimulation::checkCollisions()
{
	QMultiHash<RigidBody*, RigidBody*> newContacts;

	// Browse all collision pairs.
	int numManifolds = dynamicsWorld->getDispatcher()->getNumManifolds();
	for (int i = 0; i < numManifolds; i++)
	{
		btPersistentManifold* contactManifold =	dynamicsWorld->getDispatcher()->getManifoldByIndexInternal(i);

		RigidBody* objA = (RigidBody*) ( (static_cast<const btCollisionObject*> (contactManifold->getBody0()))->getUserPointer());
		RigidBody* objB = (RigidBody*) ( (static_cast<const btCollisionObject*> (contactManifold->getBody1()))->getUserPointer());

		if (objA == 0 or objB == 0)
			return;

		// Check all contacts points.
		int numContacts = contactManifold->getNumContacts();
		for (int j = 0; j < numContacts; j++)
		{
			btManifoldPoint& pt = contactManifold->getContactPoint(j);

			// Check collision state.
			if (pt.getDistance() < 0.f)
			{
				// Order A < B, will make things easier with the contact hash.
				RigidBody* objT;
				if (objA > objB)
				{
					objT = objA;
					objA = objB;
					objB = objT;
				}

				newContacts.insert(objA, objB);

				// Call the collide() method on the bodies if they are not registered yet.
				if (!contacts.values(objA).contains(objB))
				{
					objA->collide(objB);
					objB->collide(objA);
				}
			}
		}
	}

	// Now check if a registered contact can be removed.
	QHashIterator<RigidBody*, RigidBody*> i(contacts);
	while (i.hasNext())
	{
		i.next();
		if (!newContacts.values(i.key()).contains(i.value()))
		{
			i.key()->uncollide(i.value());
			i.value()->uncollide(i.key());
		}
	}

	contacts = newContacts;
}

// When a collision is detected and it's the floor, disallow linear translation
// and rotation about the z axis. It's important to set the velocities to 0 in
// the moment of the contact to prevent sliding.
void RigidBody::collide(RigidBody* other)
{
	if (other->id == 0) // is floor
	{
		btVector3 tau = rigidBody.getAngularVelocity();
		rigidBody.setLinearVelocity(btVector3(0, 0, 0));
		rigidBody.setAngularVelocity(btVector3(tau[0], tau[1], 0));
		rigidBody.setLinearFactor(btVector3(0, 0, 1));
		rigidBody.setAngularFactor(btVector3(1, 1, 0));
	}
}

// Release the linear and angular velocity limits.
void RigidBody::uncollide(RigidBody* other)
{
	if (other->id == 0) // is floor
	{
		rigidBody.setLinearFactor(btVector3(1, 1, 1));
		rigidBody.setAngularFactor(btVector3(1, 1, 1));
	}
}


Post Reply