Using meters vs centimeters causes tunneling and stuck objs

z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

I'm experimenting with Bullet. I'm interested in modeling billiard balls and related things. I decided to create a bare-bones GLUT app that does some simple simulation to experiment with friction between the balls, balls and table, balls and cushion, etc.

In my first attempt, I decided to use realistic units of measure. That is, a billiard ball is 0.0572 m (2.25 inches) in diameter. I'm using MKS and I expect/assume that Bullet expects and works well in this standard.

The problem is that using such a small value, the ball either tunnels through a box I have in my scene under it, or gets stuck in the upper face of that box.

I really don't want to have to scale all of my objects up by a factor of 10 or 100. However, it works with a scale of 100. What's up with this?

Code: Select all

#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include <btBulletDynamicsCommon.h>

#ifdef HAVE_GLDEBUG_DRAWER
#include "GLDebugDrawer.h"
#endif

#include <vector>
using namespace std;

#define PHYSICS_MAX_PROXIES 8192

// Simple initialization for Bullet.

struct Physics 
{
    typedef std::vector<btCollisionShape*> ShapeVector;

    btDiscreteDynamicsWorld *dynamicsWorld;
    btCollisionDispatcher *dispatcher;
    btDefaultCollisionConfiguration *collisionConfiguration;
    btSequentialImpulseConstraintSolver *constraintSolver;
    btAxisSweep3 *overlappingPairCache;
    ShapeVector collisionShapes;

#ifdef HAVE_GLDEBUG_DRAWER
    GLDebugDrawer drawer;
#endif
    
    Physics(const btVector3& worldMin, const btVector3& worldMax);
    virtual ~Physics();
};

Physics::Physics(const btVector3& worldMin, const btVector3& worldMax)
{
    collisionConfiguration = new btDefaultCollisionConfiguration();
    dispatcher = new btCollisionDispatcher(collisionConfiguration);
    overlappingPairCache = new btAxisSweep3(worldMin, worldMax, 
                                            PHYSICS_MAX_PROXIES);
    constraintSolver = new btSequentialImpulseConstraintSolver;
    dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, overlappingPairCache, 
                                                constraintSolver, 
                                                collisionConfiguration);
    dynamicsWorld->setGravity(btVector3(0, -9.81, 0));

#ifdef HAVE_GLDEBUG_DRAWER
    drawer.setDebugMode(btIDebugDraw::DBG_DrawWireframe|
                        btIDebugDraw::DBG_DrawAabb|
                        btIDebugDraw::DBG_DrawFeaturesText|
                        btIDebugDraw::DBG_DrawContactPoints|
                        btIDebugDraw::DBG_DrawText);
    dynamicsWorld->setDebugDrawer(&drawer);
#endif
}

Physics::~Physics()
{
    for (int i = dynamicsWorld->getNumCollisionObjects() - 1; i >= 0; --i)
    {
        btCollisionObject* obj = dynamicsWorld->getCollisionObjectArray()[i];
        btRigidBody* body = btRigidBody::upcast(obj);
    
        if (body && body->getMotionState())
            delete body->getMotionState();
        
        dynamicsWorld->removeCollisionObject(obj);
        delete obj;
    }
    
    for (int j = 0; j < collisionShapes.size(); ++j)
        delete collisionShapes[j];
    
    delete dynamicsWorld;
    delete constraintSolver;
    delete overlappingPairCache;
    delete dispatcher;
    delete collisionConfiguration;    
}

// Constants controlling this demo

// PROBLEM # 1
// ================================================================
// The diameter of a billiards ball is 0.0572 m.
// If I use this number however, the ball either tunnels through the box
// or gets stuck in the top face of the box.
// Thus, I'm forced to change my base unit of measure for lengths 
// to the centimeter.

#define USE_METERS
//#define USE_CENTIMETERS


// PROBLEM # 2
// ========================================================
// Here I simulate a spinning ball by applying some torque so that 
// the ball spins in place (about our UP vector, the +Y axis).
// However since the collision shape is an idealized sphere, the
// ball does not ever (I stopped waiting) stop spinning since there
// is only 1 contact point between it and the box, and thus not a 
// lot of friction.  I use angular damping but it seems hard to 
// get a *feel* for the proper values.  Note that the first problem
// plays directly into this problem... If I use centimeters, then
// I have to tweak the torque + impulse values differently in my
// app; it's unintuituve... 

// Comment this out to hide this problem.
#define NO_APPLY_TORQUE


float ballDiameter    = 0.0572;                      // m

#ifdef USE_METERS
float ballRadius      = ballDiameter/2.f;            // m
float boxExtent       = 1;                           // m

#else  // use centimeters:

float ballRadius      = (ballDiameter/2.f) * 100;    // cm
float boxExtent       = 100;                         // cm
#endif

float ballMass        = 0.260;                       // kg
float boxHalfExtent   = boxExtent*0.5;

float boxColor[4]     = {0.4,0.4,0.4,1};
float ballColor[4]    = {1,0,1,1};
float outlineColor[4] = {1, 1, 1, 0.5};

float lightAmbient[]  = {0.5, 0.5, 0.5, 1.0};
float lightDiffuse[]  = {1.0, 1.0, 1.0, 1.0};
float lightPosition[] = {0, boxExtent*2, boxExtent*2, 0.0}; 
float lightShininess  = 64.0;
float matAmbient[]    = {0.4, 0.4, 0.4, 1.0};
float matDiffuse[]    = {0.7, 0.7, 0.7, 1.0};	
float matSpecular[]   = {1.0, 1.0, 1.0, 1.0};

float eye[3]          = {0, 0, boxExtent};

// Physics setup for this demo

struct DemoPhysics : public Physics 
{
    DemoPhysics();
    
    btRigidBody *boxBody;
    btRigidBody *ballBody;
};

DemoPhysics::DemoPhysics() 
: Physics(btVector3(-boxExtent*5,-boxExtent*5,-boxExtent*5), 
          btVector3(+boxExtent*5,+boxExtent*5,+boxExtent*5))
{
    // add a box that we refer to as the "box"

    btCollisionShape *boxShape = new btBoxShape(btVector3(boxHalfExtent, boxHalfExtent, boxHalfExtent));
    collisionShapes.push_back(boxShape);
    btTransform boxTransform;
    boxTransform.setIdentity();
    boxTransform.setOrigin(btVector3(0, -boxHalfExtent, 0));   // Top of box at Y=0
    btDefaultMotionState *motionState = new btDefaultMotionState(boxTransform);
    btScalar boxMass = 0;
    btVector3 localInertia(0,0,0);
    btRigidBody::btRigidBodyConstructionInfo info(boxMass, motionState, boxShape, localInertia);
    boxBody = new btRigidBody(info);
    dynamicsWorld->addRigidBody(boxBody);
    
    // add a ball 
    
    btCollisionShape *ballShape = new btSphereShape(ballRadius);
    collisionShapes.push_back(ballShape);
    btTransform ballTransform;
    ballTransform.setIdentity();
    ballTransform.setOrigin(btVector3(0, boxExtent * 2, 0));   // Above the box
    btDefaultMotionState *ballMotionState = new btDefaultMotionState(ballTransform);
    btVector3 ballLocalInertia;
    ballShape->calculateLocalInertia(ballMass, ballLocalInertia);
    btRigidBody::btRigidBodyConstructionInfo ballBodyInfo(ballMass, ballMotionState, ballShape, ballLocalInertia);
    
    // Thresholds for when the body goes to sleep.  The defaults are a little high
    // for our application.
    
    ballBodyInfo.m_linearSleepingThreshold = 0.1;
    ballBodyInfo.m_angularSleepingThreshold = 0.1;

    ballBody = new btRigidBody(ballBodyInfo);
    dynamicsWorld->addRigidBody(ballBody);
    
#ifndef NO_APPLY_TORQUE
    ballBody->applyTorque(btVector3(0,45,0));
    
    float linearDamping = 0;
    float angularDamping = 0.05;
    
    ballBody->setDamping(linearDamping, angularDamping);
#endif
}

DemoPhysics *physics = new DemoPhysics();

enum { fDrawSolid = 0x1, fDrawOutline = 0x2 };

void drawBoxShapedBody(btRigidBody *body, float size, float color[3], int flags = 0xFFFF)
{    
    btTransform transform;
    body->getMotionState()->getWorldTransform(transform);
 
    float worldMatrix[16];
    transform.getOpenGLMatrix(worldMatrix);

    glPushMatrix();
    glMultMatrixf(worldMatrix);

    if (flags & fDrawSolid) {
        glEnable(GL_LIGHTING);
        glColor4fv(color);
        glutSolidCube(size);
    }
    
    if (flags & fDrawOutline) {
        glDisable(GL_LIGHTING);    
        glColor4fv(outlineColor);
        glutWireCube(size);
    }
    
    glPopMatrix();    
}

void drawSphereShapedBody(btRigidBody *body, float radius, float color[3], int flags = 0xFFFF)
{    
    btTransform transform;
    body->getMotionState()->getWorldTransform(transform);
    
    float worldMatrix[16];
    transform.getOpenGLMatrix(worldMatrix);
    
    glPushMatrix();
    glMultMatrixf(worldMatrix);
    
    int slices = 10;
    int stacks = 10;
    
    if (flags & fDrawSolid) {
        glEnable(GL_LIGHTING);
        glColor4fv(color);
        glutSolidSphere(radius, slices, stacks);
    }
    
    if (flags & fDrawOutline) {
        glDisable(GL_LIGHTING);
        glColor4fv(outlineColor);
        glutWireSphere(radius, slices, stacks);
    }
    
    glPopMatrix();    
}

static void draw(void)
{
    glLoadIdentity();
    gluLookAt(eye[0], eye[1], eye[2], 0,0,0, 0,1,0);
    
    glClearColor(0.1, 0.1, 0.1, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

#ifdef HAVE_GLDEBUG_DRAWER
    //physics->dynamicsWorld->debugDrawWorld();
#endif
    
    drawBoxShapedBody(physics->boxBody, boxExtent, boxColor);
    drawSphereShapedBody(physics->ballBody, ballRadius, ballColor);

    glutSwapBuffers();
}

static void timer(int msec)
{
    physics->dynamicsWorld->stepSimulation(10);
    
    glutTimerFunc(msec, timer, 5);
    glutPostRedisplay();
}

static void keyboard(unsigned char  key, int, int)
{
    if (key == 27) 
    {
        delete physics;
        exit(0);
    }
}

int main(int argc, char **argv)
{
    float w = 512, h = 384;
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(w, h);
    glutCreateWindow("bullet demo");
    
    glutDisplayFunc(draw);
    glutKeyboardFunc(keyboard);
    glutTimerFunc(100, timer, 10);
    
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    glEnable(GL_COLOR_MATERIAL);
    glShadeModel(GL_SMOOTH);
    
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmbient);
    glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiffuse);
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, matSpecular);
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, lightShininess);
    
    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); 			
        
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    glMatrixMode(GL_PROJECTION);
    gluPerspective(45, w/h, 1, 5000.0);
    glMatrixMode(GL_MODELVIEW);
    
    glHint(GL_LINE_SMOOTH, GL_NICEST);
    glLineWidth(2);

    glutMainLoop();
}
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

Collecting some relevant links here as it seems I'm not the only one experiencing this small-scale issue.

http://www.bulletphysics.com/Bullet/php ... f=9&t=2469

http://code.google.com/p/bullet/issues/detail?id=45

http://www.bulletphysics.com/Bullet/php ... 099&p=4043

http://brockley.ath.cx/~spark/bullet/st ... oData.html

http://www.bulletphysics.com/Bullet/php ... nits#p9306

http://www.bulletphysics.com/Bullet/php ... ilit=units

http://www.bulletphysics.com/Bullet/php ... f=9&t=1487


This seems to work better for now:

1) change the update rate to 400Hz (3rd parameter to stepSimulation)
2) add to the half extents of the box the same distance used as a "collision margin", 0.04
Last edited by z8000 on Thu Aug 21, 2008 3:10 am, edited 1 time in total.
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

I thought I read somewhere [EDIT: yes, see below] that if you do upscale you also need to upscale your object's mass. For a sphere I read that the mass should be upscaled by the cube, not linearly. So if I use centimeters instead of meters I'd upscale radius by a factor of 100 but would need to also upscale mass by 100^3? Does this make any sense to people here? I assume this has to do with the fact that the volume of a sphere is 4/3 pi r^3 but my math is fuzzy at best.

If this is true, what does this imply for creating impulses for a game say?

EDIT: found it ... http://www.bulletphysics.com/Bullet/php ... game#p3649
Last edited by z8000 on Thu Aug 21, 2008 4:42 pm, edited 1 time in total.
sparkprime
Posts: 508
Joined: Fri May 30, 2008 2:51 am
Location: Ossining, New York
Contact:

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by sparkprime »

If you upscale distances (e.g. 1 metres to 100 centimetres) then you must also upscale velocities and accelerations (particularly gravity!) by the same factor. This is because you now are using cm/s and cm/s/s as units. You will also have to upscale impulses because their unit is actually changing from kg*m/s to kg*cm/s

If you upscale mass, you will just have to upscale impulses.

Mass and distances should be pretty much independent I think. Have a try and see.
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

Well I tried upscaling to cm from m. It acts weirdly.

http://brianhammond.com/mov/m.mov

http://brianhammond.com/mov/cm.mov

Code: Select all

float ballDiameter    = 0.0572;                      // m

#ifdef USE_METERS

float ballRadius      = ballDiameter/2.f;            // m
float boxExtent       = 0.5;                         // m
float ballMass        = 0.260;                       // kg

btVector3 g(0, -9.81, 0);

#else  // use centimeters:

float ballRadius      = (ballDiameter/2.f) * 100;    // cm
float boxExtent       = 0.5 * 100;                   // cm
float ballMass        = 0.260 * 1e6;                 // kg

btVector3 g(0, -9.81 * 100, 0);

#endif
BTW, my note about mass needing to be upscaled by 100^3 seems to be bogus as the line

Code: Select all

float ballMass        = 0.260 * 1e6;                 // kg
and

Code: Select all

float ballMass        = 0.260;                 // kg
do the same thing regardless of ballRadius value.

I'm also scaling the applied torque in my simple demo when using cm.

So the question is shouldn't the behavior be the same regardless of m or cm use as long as things are scaled correctly?

What am I missing?
sparkprime
Posts: 508
Joined: Fri May 30, 2008 2:51 am
Location: Ossining, New York
Contact:

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by sparkprime »

the centimetre one seems to be ok...
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

sparkprime wrote:the centimetre one seems to be ok...
What do you mean by "ok"? Any idea why they'd be different?
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

So the cm version and the m version are different because of damping. I don't fully comprehend how to control linear/angular damping to be equivalent given upscaling however.
sparkprime
Posts: 508
Joined: Fri May 30, 2008 2:51 am
Location: Ossining, New York
Contact:

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by sparkprime »

damping is the proportion of velocity lost in 1 second so shouldn't need upscaling

i think you need to increase the torque with the size because the intertia increases.
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

sparkprime wrote:damping is the proportion of velocity lost in 1 second so shouldn't need upscaling

i think you need to increase the torque with the size because the intertia increases.
Makes sense. But, I tried that. I scaled the torque vector by 100 in the cm case. Still, different behavior as seen in the videos.
sparkprime
Posts: 508
Joined: Fri May 30, 2008 2:51 am
Location: Ossining, New York
Contact:

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by sparkprime »

Hmm, I wonder it might be more complicated than that, if you uniformly scale an object, does its inertia scale uniformly? You could try 100x again.

I haven't actually used torques in my own project.

Torque I believe has a unit of Newton Metre, i.e. Metre * Metre * Mass / second / second

This suggests it needs to be scaled up twice, i.e. by 10000
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

Ah, excellent. Yes, that makes the cm and m versions behave identically, as long as there's no linear or angular damping applied. Updated the movies.
Last edited by z8000 on Thu Aug 21, 2008 3:45 pm, edited 1 time in total.
sparkprime
Posts: 508
Joined: Fri May 30, 2008 2:51 am
Location: Ossining, New York
Contact:

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by sparkprime »

There is currently a bug with linear/angular damping

http://code.google.com/p/bullet/issues/detail?id=74

however I'm not sure it would cause your demos to behave differently.
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

sparkprime wrote:There is currently a bug with linear/angular damping
I applied the patch from that issue report. Now things look quite close to each other.

I applied the same linear and angular velocity in the m and cm versions.

Here are the movies before patching applyDamping:

http://brianhammond.com/mov/cm.mov
http://brianhammond.com/mov/m.mov

and afterwards:

http://brianhammond.com/mov/cm2.mov
http://brianhammond.com/mov/m2.mov
z8000
Posts: 24
Joined: Mon Aug 04, 2008 3:54 pm

Re: Using meters vs centimeters causes tunneling and stuck objs

Post by z8000 »

To summarize what I've learned thus far:

- Bullet does not work well with small bodies where "small" is < ~ 0.15 units

- To compensate for this, you can upscale bodies. However, this of course requires you to upscale forces (e.g. gravity), impulses, and torques.

- E.g. if you upscale meters to centimeters, you are upscaling by a factor of 100. You thus need to upscale:

- gravity by 100 (e.g. -9.81 * 100)
- torques by 100^2 = 10000
- impulses by 100

- Mass does not need to be upscaled.

- Damping factors do not need to be altered. However, upscaling exacerbates a problem with btRigidBody::applyDamping. A patch provided in http://code.google.com/p/bullet/issues/detail?id=74 fixes this issue such that damping applied to upscaled and unscaled bodies behave in the same manner.
Post Reply