Friday, December 7, 2018

collision detection - Create bullet physics rigid body along the vertices of a blender model


I am working on my first 3D game, for iphone, and I am using Blender to create models, Cocos3D game engine and Bullet for physics simulation. I am trying to learn the use of physics engine.


What I have done


I have created a small model in blender which contains a Cube (default blender cube) at the origin and a UVSphere hovering exactly on top of this cube (without touching the cube) enter image description here


I saved the file to get MyModel.blend. Then I used


File -> Export -> PVRGeoPOD (.pod/.h/.cpp)

in Blender to export the model to .pod format to use along with Cocos3D.


In the coding side, I added necessary bullet files to my Cocos3D template project in XCode.


I am also using a bullet objective C wrapper.



-(void) initializeScene {
_physicsWorld = [[CC3PhysicsWorld alloc] init];
[_physicsWorld setGravity:0 y:-9.8 z:0];

/*Setup camera, lamp etc.*/
..........
...........

/*Add models created in blender to scene*/
[self addContentFromPODFile: @"MyModel.pod"];


/*Create OpenGL ES buffers*/
[self createGLBuffers];

/*get models*/
CC3MeshNode* cubeNode = (CC3MeshNode*)[self getNodeNamed:@"Cube"];
CC3MeshNode* sphereNode = (CC3MeshNode*)[self getNodeNamed:@"Sphere"];

/*Those boring grey colors..*/
[cubeNode setColor:ccc3(255, 255, 0)];

[sphereNode setColor:ccc3(255, 0, 0)];

float *cVertexData = (float*)((CC3VertexArrayMesh*)cubeNode.mesh).vertexLocations.vertices;
int cVertexCount = (CC3VertexArrayMesh*)cubeNode.mesh).vertexLocations.vertexCount;

btTriangleMesh* cTriangleMesh = new btTriangleMesh();

// for (int i = 0; i < cVertexCount * 3; i+=3) {
// printf("\n%f", cVertexData[i]);
// printf("\n%f", cVertexData[i+1]);

// printf("\n%f", cVertexData[i+2]);
// }

/*Trying to create a triangle mesh that curresponds the cube in 3D space.*/
int offset = 0;
for (int i = 0; i < (cVertexCount / 3); i++){
unsigned int index1 = offset;
unsigned int index2 = offset+6;
unsigned int index3 = offset+12;
cTriangleMesh->addTriangle(

btVector3(cVertexData[index1], cVertexData[index1+1], cVertexData[index1+2] ),
btVector3(cVertexData[index2], cVertexData[index2+1], cVertexData[index2+2] ),
btVector3(cVertexData[index3], cVertexData[index3+1], cVertexData[index3+2]
));
offset += 18;
}

[self releaseRedundantData];

/*Create a collision shape from triangle mesh*/

btBvhTriangleMeshShape* cTriMeshShape = new btBvhTriangleMeshShape(cTriangleMesh,true);
btCollisionShape *sphereShape = new btSphereShape(1);

/*Create physics objects*/
gTriMeshObject = [_physicsWorld createPhysicsObjectTrimesh:cubeNode shape:cTriMeshShape mass:0 restitution:1.0 position:cubeNode.location];
sphereObject = [_physicsWorld createPhysicsObject:sphereNode shape:sphereShape mass:1 restitution:0.1 position:sphereNode.location];
sphereObject.rigidBody->setDamping(0.1,0.8);
}

When I run the sphere and cube shows up fine. I expect the sphere object to fall directly on top of the cube, since I have given it a mass of 1 and the physics world gravity is given as -9.8 in y direction. But What is happening the spere rotates around cube three or times and then just jumps out of the scene. Then I know I have some basic misunderstanding about the whole process.



So my question is, how can I create a physics collision shape which corresponds to the shape of a particular mesh model. I may need complex shapes than cube and sphere, but before going into them I want to understand the concepts.



Answer



Well, I got this working. I had to make some changes. For starters, I did throw out the Bullet wrapper, and used my own code to create physics bodies.


These are the changes I have done.


Changes in code


My initializeScene method now looks like this


-(void) initializeScene {
// Create the camera, place it back a bit, and add it to the scene
CC3Camera* cam = [CC3Camera nodeWithName: @"Camera"];
cam.location = cc3v( 0.0, 7.0, 10.0 );

cam.rotation = cc3v(-20.0, 0.0, 0.0);
[self addChild: cam];

CC3Light* lamp = [CC3Light nodeWithName: @"Lamp"];
lamp.location = cc3v( -2.0, 2.0, 10.0 );
lamp.isDirectionalOnly = NO;
[cam addChild: lamp];

[self addContentFromPODFile: @"MyModel.pod"];


[self createGLBuffers];

//Bullet codes
btBroadphaseInterface* broadphase = new btDbvtBroadphase();
btDefaultCollisionConfiguration* collisionConfiguration =
new btDefaultCollisionConfiguration();
btCollisionDispatcher* dispatcher
= new btCollisionDispatcher(collisionConfiguration);
btSequentialImpulseConstraintSolver* solver
= new btSequentialImpulseConstraintSolver();

dynamicsWorld
= new btDiscreteDynamicsWorld(dispatcher
,broadphase,solver,collisionConfiguration);
dynamicsWorld->setGravity(btVector3(0,-10.0,0));


/*get models*/
CC3MeshNode* cubeNode = (CC3MeshNode*)[self getNodeNamed:@"Cube"];
CC3MeshNode* sphereNode = (CC3MeshNode*)[self getNodeNamed:@"Sphere"];


/*Those boring grey colors..*/
[cubeNode setColor:ccc3(255, 255, 0)];
[sphereNode setColor:ccc3(255, 0, 0)];

[self setPhysicsBodyForCube:cubeNode];

[self setPhysicsBodyForSphere:sphereNode];

[self releaseRedundantData];


[[CCScheduler sharedScheduler]
scheduleSelector:@selector(updatePhysics:)
forTarget:self interval:0 paused:NO];
}

and setPhysicsBodyForCube method is


-(void)setPhysicsBodyForCube:(CC3MeshNode*)mesh{
float *gVertexData = (float*)((CC3VertexArrayMesh*)mesh.mesh)
.vertexLocations.vertices;
GLushort* gIndices = (GLushort*)((CC3VertexArrayMesh*)mesh.mesh)

.vertexIndices.vertices;
int gIndiceCount = ((CC3VertexArrayMesh*)mesh.mesh)
.vertexIndices.vertexCount;
btTriangleMesh* gTriangleMesh = new btTriangleMesh();

float locX = mesh.location.x;
float locY = mesh.location.y;
float locZ = mesh.location.z;

for (int i = 0; i < gIndiceCount; i+=3){

unsigned int index1 = gIndices[i] * VERTEX_SIZE;
unsigned int index2 = gIndices[i+1] * VERTEX_SIZE;
unsigned int index3 = gIndices[i+2] * VERTEX_SIZE;
gTriangleMesh->addTriangle(
btVector3( gVertexData[index1] + locX
, gVertexData[index1+1] + locY
, gVertexData[index1+2] + locZ),
btVector3( gVertexData[index2] + locX
, gVertexData[index2+1] + locY
, gVertexData[index2+2] + locZ),

btVector3( gVertexData[index3] + locX
, gVertexData[index3+1] + locY
, gVertexData[index3+2] + locZ));
}//end of for loop.

btBvhTriangleMeshShape *gTriMeshShape
= new btBvhTriangleMeshShape(gTriangleMesh,true);
btDefaultMotionState *groundMotionState
= new btDefaultMotionState(btTransform(
btQuaternion(0,0,0,1),btVector3(0,-.1,0)));

btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI(
0,groundMotionState,gTriMeshShape,btVector3(0,0,0.0));
groundRigidBodyCI.m_restitution = 0.3;
btRigidBody *groundRigidBody = new btRigidBody(groundRigidBodyCI);
dynamicsWorld->addRigidBody(groundRigidBody);
}

and finally my setPhysicsBodyForSphere method is..


-(void) addSphereToScene:(CC3MeshNode*)node{
CC3MeshNode *sphereNode = (CC3MeshNode*) [node

copyWithName:@"SomeOtherName"];
sphereNode.location = cc3v(0.0, 10.0, 0.0);
//for some reason, this copying is necessary.
[self addChild:sphereNode];

//remove the original node, since we have a replacement.
[self removeChild:node];

btCollisionShape *fallShape = new btSphereShape(0.5);
btDefaultMotionState *fallMotionState =

new btDefaultMotionState(btTransform(
btQuaternion(sphereNode.location.x, sphereNode.location.y
, sphereNode.location.z,1),
btVector3(sphereNode.location.x,sphereNode.location.y
, sphereNode.location.z)));
btScalar mass = 1.0;
btVector3 fallInertia(0,0,0);
fallShape->calculateLocalInertia(mass, fallInertia);
btRigidBody::btRigidBodyConstructionInfo fallRigidBodyCI(
mass,fallMotionState,fallShape,fallInertia);

fallRigidBodyCI.m_restitution = 1.0;
btRigidBody* fallRigidBody = new btRigidBody(fallRigidBodyCI);
fallRigidBody->setDamping(0.3,0.8);
dynamicsWorld->addRigidBody(fallRigidBody);
}

Now one thing, I need to explain is the constant VERTEX_SIZE used in setPhysicsBodyForCube method. When we take the vertex array in code using below line


float *gVertexData = (float*)((CC3VertexArrayMesh*)mesh.mesh)
.vertexLocations.vertices;


we get the vertex array, interleaved with



  • normals of each vertex

  • texture uv coordinates (if textures are there)


So where there is no texture in your model (cube in my case), the vertex arrays is arranged like this.


v1X, v1Y, v1Z, n1X, n1y, n1z, v2X, v2Y... so on..

The second vertex coordinate gets started at index 6 in vertex array. So in this case we need to set VERTEX_SIZE as 6.


#define VERTEX_SIZE 6


When there is texture in your model, the vertex array is aligned like this


v1X, v1Y, v1Z, n1X, n1y, n1z, uX, uY, v2X, v2Y... so on..

and in this case VERTEX_SIZE is 8


#define VERTEX_SIZE 8

I am sure there may be a way to get this offset value straight from the model. But I couldn't find one when I searched. If anyone knows this, please let us know.


Changes in the way I exported the POD


You can see I am creating a triangle Mesh to create the physics body. You can export the triangle mesh used to create the model in the first place along with your vertices when you export. So when I exported the model using PVRGeoPOD exporter in blender, I used



  primitive type -> indexed triangle list
export Materials -> checked
Export transformation as Matrices -> checked

Physics Debug


As user @Byte56 suggested in comments, you can draw debug lines to see where your physics body stand. I have already written an answer about bullet physics debugging. So I am not going to repeat. Thank you @Byte56 for the help.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...