3
\$\begingroup\$

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.

\$\endgroup\$
8
  • \$\begingroup\$ BTW 2 bonus questions -- 1)what is the link to faq on this site, I couldn't find it. 2) I wanted to tag this question using cocos3D (instead of cocos2D). But there is no question using that tag, and I don't have enough reputation to create one. It is more of a cocos3D question \$\endgroup\$ Jul 2, 2013 at 10:09
  • 1
    \$\begingroup\$ The FAQ is now called help. Are the sphere and cube part of the same model? You might be running into some issues with that. Also, you can create a debugging environment that will help you with these physics issues. Try creating a wireframe mesh that uses the same vertices you're using to create the cTriangleMesh. This will allow you to see the shape of the physics bodies. \$\endgroup\$
    – House
    Jul 2, 2013 at 13:11
  • \$\begingroup\$ @Byte56 Thanks, it seems faq changed to help in SO too.. I didn't notice that before.. \$\endgroup\$ Jul 3, 2013 at 3:38
  • \$\begingroup\$ Yes, my sphere and cube are part of same model.. I am now trying to create a separate models for both.. Will post back the findings. \$\endgroup\$ Jul 3, 2013 at 3:39
  • \$\begingroup\$ Okay.. I created separate .blend and .pod files for sphere and cube, but the result is the same. @Byte56 just to know, using multiple objects from same model is bad? can you tell me why? \$\endgroup\$ Jul 3, 2013 at 3:51

1 Answer 1

2
\$\begingroup\$

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.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .