Trouble with BulletOde

jst
Posts: 1
Joined: Sun Jan 01, 2006 11:52 pm

Trouble with BulletOde

Post by jst »

Hello Erwin,

many thanks for your wonderful project.

I am currently trying to use BulletOde to add collision support for convex shapes to my application, but have run into some difficulies with that : eg. my usually well-behaved stack of Jenga blocks is exploding like popcorn in a microwave, single Jenga blocks are gaining momentum and rolling across the tabletop when they should just rest in place, and I get displays of collision points in far outside the geoms involved.

To investigate this, I have made some simple tests (without using my own framework to avoid introducing errors from there):
I create a large tabletop using an ode box primitive (object1 o1) and a small box by adding the 8 corner points to a convex shape (object2 o2).
I manually position o2 so that is just intersects the top surface of o1 and then call dCollide directly on the two geoms.
The result is just 1 contact point, which I think would need to be in the center of contact area to avoid generating torque for o2.
If o2 is centered on top of o1, the results are as expected, but as o2 moves away from the center of o1 towards the edge of the "table", the difference between the expected and reported contact position get larger. This effect seems very small if o1 and o2 have similar scale, but increases dramatically if that is not the case.
If the scale ratio is large enough (75:1) the system can even miss the collision entirely if o2 isn't close to the center of the table o1.

Example 1:
Ratio 2.5:1
o1 Tabletop length/width/height 5.0/5.0/2.0
o1 Position 0.0, 0.0, -1.0
o2 ConvexBox x: (-1)...(1) y: (-1)...(1) z: (-0.1)...(0.1)
o2 Position (2.0, 0.0, 0.05)

1 Collisions detected @ (1.42857 , 1.04305e-007 , -0.0250005) Normal : (0 , 1.78803e-006 , -1) Depth : 0.050001

Deviation from expected collision point (x-axis only) (2.0 - 1.42857) = 0.571429 ~ 57% relative to o2's size


Example 2:
Ratio 75:1
o1 Tabletop length/width/height 150.0/150.0/2.0
o1 Position 0.0, 0.0, -1.0
o2 ConvexBox x: (-1)...(1) y: (-1)...(1) z: (-0.1)...(0.1)
o2 Position (48.75, 0.0, 0.05)

0 Collisions detected. !!!


This is the code that I used for the tests:

Code: Select all

#include "ode/ode.h"
#include "CollisionShapes/ConvexHullShape.h"
#include "BulletOdeCollide.h"

#include <iostream>
#include <fstream>
#include <sstream>

void buildBox(std::stringstream& LOG, ConvexHullShape* hullShape, float x_min, float x_max, float y_min, float y_max, float z_min, float z_max)
{
	hullShape->AddPoint(SimdPoint3(x_min, y_min, z_min));
	hullShape->AddPoint(SimdPoint3(x_min, y_min, z_max));
	hullShape->AddPoint(SimdPoint3(x_min, y_max, z_min));
	hullShape->AddPoint(SimdPoint3(x_min, y_max, z_max));
	hullShape->AddPoint(SimdPoint3(x_max, y_min, z_min));
	hullShape->AddPoint(SimdPoint3(x_max, y_min, z_max));
	hullShape->AddPoint(SimdPoint3(x_max, y_max, z_min));
	hullShape->AddPoint(SimdPoint3(x_max, y_max, z_max));
	//hullShape->SetMargin(0.04f);
	hullShape->SetMargin(1e-6f);
	hullShape->setLocalScaling(SimdVector3(1.f, 1.f, 1.f));
	LOG << "o2 ConvexBox x:(" << x_min << ")...(" << x_max << 
		") y:(" << y_min << ")...(" << y_max << 
		") z:(" << z_min << ")...(" << z_max << ")";
		
}

void collideLOG(std::stringstream& LOG, dGeomID o1, dGeomID o2, float cboxhsize)
{
	dContactGeom contactGeom[1024];

	int numc = dCollide(o1, o2, 128, contactGeom, sizeof(dContactGeom));

	LOG << std::endl;

	LOG << "Geom 1 Pos: (" << 
		dGeomGetPosition(o1)[0] << " , " <<
		dGeomGetPosition(o1)[1] << " , " <<
		dGeomGetPosition(o1)[2] << ") ";

	LOG << "Geom 2 Pos: (" << 
		dGeomGetPosition(o2)[0] << " , " <<
		dGeomGetPosition(o2)[1] << " , " <<
		dGeomGetPosition(o2)[2] << ")" << std::endl;

	LOG <<  numc << " Collisions detected." ;
	if (numc > 0)
		LOG << " Contact diff.%: " << ((dGeomGetPosition(o2)[0] - contactGeom[0].pos[0]) / cboxhsize * 100.f )<< std::endl;
	else
		LOG << std::endl;

	for (int i = 0; i < numc; ++i) {
		LOG << "Col " << (i+1) << ": (" <<
			contactGeom[i].pos[0] << " , " <<
			contactGeom[i].pos[1] << " , " <<
			contactGeom[i].pos[2] << ")" <<
			" Normal : (" <<
			contactGeom[i].normal[0] << " , " <<
			contactGeom[i].normal[1] << " , " <<
			contactGeom[i].normal[2] << ")" <<
			" Depth : " <<
			contactGeom[i].depth << std::endl;
	}
}

void BulletOdeTest()
{
	// setup logging
	std::stringstream LOG;
	LOG.precision(6);
	LOG.width(0);
	LOG.fill(' ');

	// create world
	dSpaceID World = dSimpleSpaceCreate(0);
	const dQuaternion identity = {1.f, 0.f, 0.f, 0.f};

	// setup object parameters
	// EDIT HERE
	const float o1length = 150.0f;
	const float o1width = 150.0f;
	const float o1height = 2.0f;
	
	const float cboxhsize = 1.0f;
	const float cboxhheight = 0.1f;

	const float startpos = 0.f;

	// create object 1
	LOG << "o1 Box l:" << o1length << " w: " << o1width << " h: " << o1height << " ";
	dGeomID o1 = dCreateBox(World, o1length, o1width, o1height);
	dGeomSetPosition(o1, startpos, 0.0f, -(o1height / 2.f));
	dGeomSetQuaternion(o1, identity);

	// create object 2
	ConvexHullShape* hullShape = new ConvexHullShape(0,0);
	buildBox(LOG, hullShape, -cboxhsize, +cboxhsize, -cboxhsize, +cboxhsize, -cboxhheight, +cboxhheight);
	dGeomID o2 = dCreateConvex(World,hullShape);
	dGeomSetQuaternion(o2, identity);


	// move object 2 across o1's surface
	
	for (float pos = startpos; pos < startpos + (o1length / 2.f); pos += (o1length / 40.f)) {
		dGeomSetPosition(o2, pos, 0.f, cboxhheight / 2.f);
		collideLOG(LOG, o1, o2, cboxhsize);
	}
	

	std::ofstream LOGFILE;
	LOGFILE.open("logfile.txt");
	LOGFILE << LOG.str();
	LOGFILE.close();
	std::cout << LOG.str();
}



int main(int argc, char* argv[])
{
	BulletOdeTest();
	return 0;
}
Is this behavior to be expected, e.g. a weakness of the GJK-algorithm (of which I know nothing yet, will pick up Christer Ericson's book tomorrow) or just a fluke in Bullet or BulletOde?

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

Post by Erwin Coumans »

Hi Jan,

Thanks for the feedback. BulletOde is not very robust for large size-ratios. Partly this is due to the implementation, and partly due to the nature of the sub-distance algorithm (innerloop replacement of Johnson within GJK).

There are are couple of options to check out, and I will have a look at it.
Basically there are several independent cases in contact generation:
penetration deeper then the collision margins, distance within the collision margins, separating distance bigger then the collision margin.

BulletOde will pick up 1 contact point at a time, and add new points to a persistent manifold. Usually this isn't a big problem, but it does add artifacts. I don't think this is your problem.

I will try to make some tests and let you know. If the problem turns out to be GJK related, I will ask Gino or Christer if they also experienced problems with large ratios between features (150 versus 0.1 in your testcase) are similar.

Thanks again,
Erwin