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)
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