I am posting here because I have not been successful in finding a helpful answer either directly on the Bullet Physics forum or any of those associated forums which have either ported the library [JBullet, BulletCS] or incorporated it in their own product [Blender, Panda3d]. Here it seems, at least if I can ask the question properly, the odds seem best of getting a helpful reply.
I want to simulate the rolling of dice. All the examples I can find seem content with using a Box shape. Mine is an interest in children's games, not Las Vegas. The defining difference, at least in my application, is that the dice [whether 6-sided, or something else, but let's just stick with 6-sided here] have 'kinder-gentler' rounded edges with no pointed corners.
It seems that btMultiSphereShape would be just perfect for me -- me being someone who does not have the knowledge to construct any complex shape from lower-level primitives, me being someone who hopes to use what is already built into the collision intelligence of the Bullet engine, not someone trying to engineer any new, exotic behavior.
With that preface I would really appreciate a succinct example of:
a. how to pass the arguments to the btMultiSphhereShape constructor; b. how to configure the transform that needs to be passed to btDefaultMaotionState; and c. how to set the localInertia.
I understand that this is a general-purpose library with classes that can support almost anything, but please, for a beginner, instead of discussions of those very nice attributes of a well-conceived toolkit, what would be most helpful to me [and I believe to many other users] would be a clear example of supplying those key properties to the necessary classes and methods to these specifics:
a. a six-sided die with equal dimensions, let's make the rounded cube about 2cm per side; b. let's have the radius of the 'corners' a gentle 0.1.cm.
Now, one more request, please. In order to observe the behavior of the die beginning with a simple 'drop', taking into account only forces due to gravity, please suggest a transform that will result in some kind of interesting tumbling result from a collision against the usual 'floor' example; in other words, it should impact the floor on a rounded corner.
Bullet Physics MultiSphereShape Usage
1.4k Views Asked by Terry Corbet At
2
There are 2 best solutions below
11

I used Bullet Physics on iOS devices to create a dice game. To get more realistic looking dice, I created a "RoundedCube" object which is just a cube with beveled edges. Here is the Objective-c code I wrote to create the verticies
//
// Created by John Carter on 4/9/09.
//
#import "RoundedCube.h"
static const GLfloat voffset[] =
{
// Front Face (counterclockwise winding)
+1.0f, +1.0f, +1.0f,
-1.0f, +1.0f, +1.0f,
-1.0f, -1.0f, +1.0f,
+1.0f, +1.0f, +1.0f,
-1.0f, -1.0f, +1.0f,
+1.0f, -1.0f, +1.0f,
// Back Face (clockwise winding)
+1.0f, +1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, +1.0f, -1.0f,
+1.0f, +1.0f, -1.0f,
+1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
// Top Face (counterclockwise winding)
-1.0f, +1.0f, +1.0f,
+1.0f, +1.0f, -1.0f,
-1.0f, +1.0f, -1.0f,
-1.0f, +1.0f, +1.0f,
+1.0f, +1.0f, +1.0f,
+1.0f, +1.0f, -1.0f,
// Bottom Face (clockwise winding)
-1.0f, -1.0f, +1.0f,
-1.0f, -1.0f, -1.0f,
+1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, +1.0f,
+1.0f, -1.0f, -1.0f,
+1.0f, -1.0f, +1.0f,
// Right Face (clockwise winding)
+1.0f, -1.0f, +1.0f,
+1.0f, +1.0f, -1.0f,
+1.0f, +1.0f, +1.0f,
+1.0f, -1.0f, +1.0f,
+1.0f, -1.0f, -1.0f,
+1.0f, +1.0f, -1.0f,
// Left Face (counterclockwise winding)
-1.0f, -1.0f, +1.0f,
-1.0f, +1.0f, +1.0f,
-1.0f, +1.0f, -1.0f,
-1.0f, +1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, +1.0f,
};
// Private Methods
//
@interface RoundedCube()
- (void) computeRoundedCubeNormals;
@end
@implementation RoundedCube
- (void) dealloc
{
[super dealloc];
}
- (void) computeRoundedCubeNormals
{
int i;
xMinVertex = FLT_MAX;
yMinVertex = FLT_MAX;
zMinVertex = FLT_MAX;
xMaxVertex = -FLT_MAX;
yMaxVertex = -FLT_MAX;
zMaxVertex = -FLT_MAX;
for (i=0; i<vertexDataCount; i+=3)
{
if ( vertexData[i+0] < xMinVertex )
xMinVertex = vertexData[i+0] ;
if ( vertexData[i+1] < yMinVertex )
yMinVertex = vertexData[i+1] ;
if ( vertexData[i+2] < zMinVertex )
zMinVertex = vertexData[i+2] ;
if ( vertexData[i+0] > xMaxVertex )
xMaxVertex = vertexData[i+0] ;
if ( vertexData[i+1] > yMaxVertex )
yMaxVertex = vertexData[i+1] ;
if ( vertexData[i+2] > zMaxVertex )
zMaxVertex = vertexData[i+2] ;
}
xVertexRange = fabs(xMaxVertex - xMinVertex);
yVertexRange = fabs(yMaxVertex - yMinVertex);
zVertexRange = fabs(zMaxVertex - zMinVertex);
for (i=0; i<vertexDataCount; i+=9)
{
btVector3 V0 = btVector3( vertexData[i+0], vertexData[i+1], vertexData[i+2] );
btVector3 V1 = btVector3( vertexData[i+3], vertexData[i+4], vertexData[i+5] );
btVector3 V2 = btVector3( vertexData[i+6], vertexData[i+7], vertexData[i+8] );
btVector3 delta1 = (V1) - (V0);
btVector3 delta2 = (V2) - (V0);
btVector3 vertexNormal = delta1.cross(delta2);
vertexNormal = vertexNormal.normalize();
if ( useAbsNormals )
{
vertexNormal = vertexNormal.absolute();
}
normalData[i+0] = vertexNormal[0];
normalData[i+1] = vertexNormal[1];
normalData[i+2] = vertexNormal[2];
normalData[i+3] = vertexNormal[0];
normalData[i+4] = vertexNormal[1];
normalData[i+5] = vertexNormal[2];
normalData[i+6] = vertexNormal[0];
normalData[i+7] = vertexNormal[1];
normalData[i+8] = vertexNormal[2];
}
}
- (void) addBeveledCorner:(int)bevelPlane At:(btVector3)bevelLocation Size:(btVector3)bevelSize Rotation:(double)rotationOrigin
{
btVector3 vertexPoint[6];
double delta;
double theta0;
double theta1;
double phi0;
double phi1;
int vp;
int i;
int j;
int k;
delta = (double)M_PI_2 / (double)smoothFactor;
// vertical segments
//
for( i=0; i<smoothFactor; i++)
{
theta0 = (double)i * (double)delta;
theta1 = (double)(i+1) * (double)delta;
if ( bevelPlane==1 )
{
theta0 += M_PI;
theta1 += M_PI;
}
// horizontal segments
//
for( j=0; j<smoothFactor; j++)
{
phi0 = (double)j * (double)delta;
phi1 = (double)(j+1) * (double)delta;
phi0 += rotationOrigin;
phi1 += rotationOrigin;
if ( bevelPlane==1 )
{
phi0 += M_PI;
phi1 += M_PI;
}
vp=0;
if ( bevelPlane==0 )
{
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0)*cos(phi0)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta0)*sin(phi0)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0)*cos(phi1)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta0)*sin(phi1)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1)*cos(phi1)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta1)*sin(phi1)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0)*cos(phi0)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta0)*sin(phi0)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1)*cos(phi1)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta1)*sin(phi1)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1)*cos(phi0)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta1)*sin(phi0)));
}
else
{
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0)*cos(phi0)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta0)*sin(phi0)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1)*cos(phi1)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta1)*sin(phi1)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0)*cos(phi1)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta0)*sin(phi1)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0)*cos(phi0)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta0)*sin(phi0)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1)*cos(phi0)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta1)*sin(phi0)));
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1)*cos(phi1)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1)), bevelLocation[2]+bevelSize[2]*btScalar(sin(theta1)*sin(phi1)));
}
for ( k=0; k<6; k++ )
{
vertexData[coordinateElement++] = vertexPoint[k][0];
vertexData[coordinateElement++] = vertexPoint[k][1];
vertexData[coordinateElement++] = vertexPoint[k][2];
colorData[colorElement++] = rgba[0];
colorData[colorElement++] = rgba[1];
colorData[colorElement++] = rgba[2];
colorData[colorElement++] = rgba[3];
}
}
}
}
- (void) addBeveledEdge:(int)bevelPlane At:(btVector3)bevelLocation Size:(btVector3)bevelSize Rotation:(double)rotationOrigin
{
btVector3 vertexPoint[6];
double origin;
double delta;
double theta0;
double theta1;
int vp;
int i;
int j;
delta = (double)(M_PI_2) / (double)smoothFactor;
for ( i=0; i<smoothFactor; i++ )
{
theta0 = (double)i * (double)delta;
theta1 = (double)(i+1) * (double)delta;
theta0 += rotationOrigin;
theta1 += rotationOrigin;
origin = (double)0.0;
vp=0;
switch( bevelPlane )
{
case 0:
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0], bevelLocation[1]+bevelSize[1]*btScalar(sin(theta0+origin)), bevelLocation[2]+bevelSize[2]*btScalar(cos(theta0+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0], bevelLocation[1]+bevelSize[1]*btScalar(sin(theta1+origin)), bevelLocation[2]+bevelSize[2]*btScalar(cos(theta1+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]-bevelSize[0], bevelLocation[1]+bevelSize[1]*btScalar(sin(theta1+origin)), bevelLocation[2]+bevelSize[2]*btScalar(cos(theta1+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]-bevelSize[0], bevelLocation[1]+bevelSize[1]*btScalar(sin(theta0+origin)), bevelLocation[2]+bevelSize[2]*btScalar(cos(theta0+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0], bevelLocation[1]+bevelSize[1]*btScalar(sin(theta0+origin)), bevelLocation[2]+bevelSize[2]*btScalar(cos(theta0+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]-bevelSize[0], bevelLocation[1]+bevelSize[1]*btScalar(sin(theta1+origin)), bevelLocation[2]+bevelSize[2]*btScalar(cos(theta1+origin)) );
break;
case 1:
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0+origin)), bevelLocation[1]+bevelSize[1], bevelLocation[2]+bevelSize[2]*btScalar(cos(theta0+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1+origin)), bevelLocation[1]-bevelSize[1], bevelLocation[2]+bevelSize[2]*btScalar(cos(theta1+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1+origin)), bevelLocation[1]+bevelSize[1], bevelLocation[2]+bevelSize[2]*btScalar(cos(theta1+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0+origin)), bevelLocation[1]-bevelSize[1], bevelLocation[2]+bevelSize[2]*btScalar(cos(theta0+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1+origin)), bevelLocation[1]-bevelSize[1], bevelLocation[2]+bevelSize[2]*btScalar(cos(theta1+origin)) );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0+origin)), bevelLocation[1]+bevelSize[1], bevelLocation[2]+bevelSize[2]*btScalar(cos(theta0+origin)) );
break;
case 2:
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0+origin)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0+origin)), bevelLocation[2]+bevelSize[2] );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1+origin)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1+origin)), bevelLocation[2]+bevelSize[2] );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1+origin)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1+origin)), bevelLocation[2]-bevelSize[2] );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0+origin)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0+origin)), bevelLocation[2]-bevelSize[2] );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta0+origin)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta0+origin)), bevelLocation[2]+bevelSize[2] );
vertexPoint[vp++] = btVector3( bevelLocation[0]+bevelSize[0]*btScalar(sin(theta1+origin)), bevelLocation[1]+bevelSize[1]*btScalar(cos(theta1+origin)), bevelLocation[2]-bevelSize[2] );
break;
}
for ( j=0; j<6; j++ )
{
vertexData[coordinateElement++] = vertexPoint[j][0];
vertexData[coordinateElement++] = vertexPoint[j][1];
vertexData[coordinateElement++] = vertexPoint[j][2];
colorData[colorElement++] = rgba[0];
colorData[colorElement++] = rgba[1];
colorData[colorElement++] = rgba[2];
colorData[colorElement++] = rgba[3];
}
}
}
- (RoundedCube *) Length:(GLfloat)initLength Width:(GLfloat)initWidth Height:(GLfloat)initHeight Bevel:(GLfloat)initBevel SmoothFactor:(int)initSmoothFactor RGBA:(GLubyte *)initRGBA AbsNormals:(BOOL)initUseAbsNormals
{
int i;
smoothFactor = initSmoothFactor;
useAbsNormals = initUseAbsNormals;
shape = (id *)self;
length = initLength;
width = initWidth;
height = initHeight;
bevel = initBevel;
coordinateElement=0;
colorElement=0;
for (i=0; i<24; i++)
rgba[i] = initRGBA[i];
vertexCount = 36 + (12 * 6 * smoothFactor) + (smoothFactor * smoothFactor * 6 * 8);
modelType = MT_BOX;
constructMode = GL_TRIANGLES;
showColorAndTexture = NO;
vertexDataCount = vertexCount * 3;
colorDataCount = vertexCount * 4;
normalDataCount = vertexCount * 3;
[self setSurfaceToDefault];
vertexData = (GLfloat *)malloc(sizeof(GLfloat) * vertexDataCount);
memset(vertexData, (unsigned int)0, sizeof(GLfloat) * vertexDataCount);
normalData = (GLfloat *)malloc(sizeof(GLfloat) * normalDataCount);
memset(normalData, (unsigned int)0, sizeof(GLfloat) * normalDataCount);
colorData = (GLubyte *)malloc(sizeof(GLubyte) * colorDataCount);
memset(colorData, (unsigned int)0, sizeof(GLubyte) * colorDataCount);
int cubeSide;
int trianglesPerSide;
int pointsPerTriangle;
for ( cubeSide=0; cubeSide<6; cubeSide++ )
{
for ( trianglesPerSide=0; trianglesPerSide<2; trianglesPerSide++ )
{
for ( pointsPerTriangle=0; pointsPerTriangle<3; pointsPerTriangle++ )
{
switch( cubeSide )
{
case 0:
case 1:
vertexData[coordinateElement+0] = voffset[coordinateElement+0] * (length - bevel);
vertexData[coordinateElement+1] = voffset[coordinateElement+1] * (width - bevel);
vertexData[coordinateElement+2] = voffset[coordinateElement+2] * height;
break;
case 2:
case 3:
vertexData[coordinateElement+0] = voffset[coordinateElement+0] * (length - bevel);
vertexData[coordinateElement+1] = voffset[coordinateElement+1] * width;
vertexData[coordinateElement+2] = voffset[coordinateElement+2] * (height - bevel);
break;
case 4:
case 5:
vertexData[coordinateElement+0] = voffset[coordinateElement+0] * length;
vertexData[coordinateElement+1] = voffset[coordinateElement+1] * (width - bevel);
vertexData[coordinateElement+2] = voffset[coordinateElement+2] * (height - bevel);
break;
}
coordinateElement += 3;
colorData[colorElement++] = rgba[(cubeSide*4) + 0]; // 1 RGBA color set per side
colorData[colorElement++] = rgba[(cubeSide*4) + 1]; // 1 RGBA color set per side
colorData[colorElement++] = rgba[(cubeSide*4) + 2]; // 1 RGBA color set per side
colorData[colorElement++] = rgba[(cubeSide*4) + 3]; // 1 RGBA color set per side
}
}
}
[self addBeveledEdge:2 At:btVector3((length-bevel), (width-bevel), 0) Size:btVector3(bevel,bevel,height-bevel) Rotation:(double)0.0];
[self addBeveledEdge:2 At:btVector3(-(length-bevel), (width-bevel), 0) Size:btVector3(bevel,bevel,height-bevel) Rotation:(double)-M_PI_2];
[self addBeveledEdge:2 At:btVector3(-(length-bevel), -(width-bevel), 0) Size:btVector3(bevel,bevel,height-bevel) Rotation:(double)-M_PI];
[self addBeveledEdge:2 At:btVector3((length-bevel), -(width-bevel), 0) Size:btVector3(bevel,bevel,height-bevel) Rotation:(double)M_PI_2];
[self addBeveledEdge:1 At:btVector3((length-bevel), 0, (height-bevel)) Size:btVector3(bevel,width-bevel,bevel) Rotation:(double)0.0];
[self addBeveledEdge:1 At:btVector3(-(length-bevel), 0, (height-bevel)) Size:btVector3(bevel,width-bevel,bevel) Rotation:(double)-M_PI_2];
[self addBeveledEdge:1 At:btVector3(-(length-bevel), 0, -(height-bevel)) Size:btVector3(bevel,width-bevel,bevel) Rotation:(double)-M_PI];
[self addBeveledEdge:1 At:btVector3((length-bevel), 0, -(height-bevel)) Size:btVector3(bevel,width-bevel,bevel) Rotation:(double)M_PI_2];
[self addBeveledEdge:0 At:btVector3(0, (width-bevel), (height-bevel)) Size:btVector3(length-bevel,bevel,bevel) Rotation:(double)0.0];
[self addBeveledEdge:0 At:btVector3(0, -(width-bevel), (height-bevel)) Size:btVector3(length-bevel,bevel,bevel) Rotation:(double)-M_PI_2];
[self addBeveledEdge:0 At:btVector3(0, -(width-bevel), -(height-bevel)) Size:btVector3(length-bevel,bevel,bevel) Rotation:(double)-M_PI];
[self addBeveledEdge:0 At:btVector3(0, (width-bevel), -(height-bevel)) Size:btVector3(length-bevel,bevel,bevel) Rotation:(double)M_PI_2];
[self addBeveledCorner:0 At:btVector3((length-bevel), (width-bevel), (height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)0.0];
[self addBeveledCorner:0 At:btVector3((length-bevel), (width-bevel), -(height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)-M_PI_2];
[self addBeveledCorner:0 At:btVector3(-(length-bevel), (width-bevel), (height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)M_PI_2];
[self addBeveledCorner:0 At:btVector3(-(length-bevel), (width-bevel), -(height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)M_PI];
[self addBeveledCorner:1 At:btVector3((length-bevel), -(width-bevel), (height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)0.0];
[self addBeveledCorner:1 At:btVector3((length-bevel), -(width-bevel), -(height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)-M_PI_2];
[self addBeveledCorner:1 At:btVector3(-(length-bevel), -(width-bevel), (height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)M_PI_2];
[self addBeveledCorner:1 At:btVector3(-(length-bevel), -(width-bevel), -(height-bevel)) Size:btVector3(bevel,bevel,bevel) Rotation:(double)M_PI];
[self computeRoundedCubeNormals];
return self;
}
@end
If you have an iOS device you can see an example of how this looks in my free app Amazing Dice HD on iTunes
I've posted another block of code from some old projects of mine that may get you going in the right direction. I added all the dependancies as well so its a lot. The method that may help is the MeshObject where I basically take the vertices of a model to create a corresponding bullet object for adding to the physics world