Monday, November 28, 2011

Tree collision demo - getting the texture and color to N9

I didn't use textures from demo http://opengles20study.blogspot.com/2011/01/cel-shading-toon-shading-silhouette-how.html after porting to N9: http://opengles20study.blogspot.com/2011/08/meego.html
To get the textures work if using jpeg library (if not using iPhone SDK to generate images - code under #ifdef USE_IPHONE_SDK_JPEGLIB) it is needed to change m_pixelColorComponents from GL_RGBA to GL_RGB here.
It seemed like a good idea not to texture the tree, but to supply color via uniform value when rendering the  tree per instance. I plan to check how the tree looks with very few leaves and for that ngPlant leaves group probably would be better to use textures (billboards). For example about how to use uniform color value in shader, just grep the code for u_Color - there are several available.
This is how it looks on N9 - sorry for the poor video quality.


Monday, November 7, 2011

Tree branches collision detection

I showed the example from previous post to cousin Miša and explained how I plan to add physics and collision detection for each branch and leaf of the tree; he noticed that the tree looks quite complex (several thousands vertices) and asked how much CPU that could consume, and how much it could affect performance and lower frames per second count...
In short, this is what the post, code examples and demo is about. There is another thing that could be interesting, animating camera position from behind the ball to the position orthogonal to ball direction while the ball is moving - not stopping or slowing down the simulation like in previous posts.

Have to apologize for the not so good quality of videos I made; I'll try to compensate it with detailed explanation and code examples about how to use related ngPlant and bulletphysics API.

The video above shows a heavy ball rolling down the hill to hit the tree, bounce back and hit low hanging tree branch.
Another video shows a rather heavy ball rolling down the hill, hitting the tree,... after it rolls away from the tree btRigidBody::applyCentralImpulse() is "applied" throwing the ball back to the tree. After it bounces back several times and get thrown again, eventually ball gets stuck between the branches of the tree.

This is simplified version of the code throwing the ball from the ground to the tree - impulse with angle of 45 degrees (500 horizontally and  500 vertically):

Vec3 vToTree;
MatrixVec3Normalize(vToTree, vTarget - vFrom); // vTarget is tree,
// vFrom is current ball position
((btRigidBody*)ball)->applyCentralImpulse(btVector3(0.,0.,500.) + 500. * btVector3(vToTree.x, vToTree.y, vToTree.z));


Camera animation while simulation is running - from behind the ball to orthogonal to ball rolling direction

For this, I'm using TransformInterpolator class designed in previous post about camera movement animation - start point in transform interpolation is defined as camera behind the ball:


MATRIX mTransformStart;
// take vFrom as previous position and vTarget as current, or in this case it is tree position
modelTransformWithCameraBehindTheBall(mTransformStart, vFrom, vTarget);
cameraAnimation = new TransformInterpolator(mTransformStart, mTransform, 75);


End point in camera animation (mTransform) is camera transformation to look at ball orthogonal to direction of ball movement, calculated as in this example:

Vec3 vBallDirection = vTarget - vBall;
Vec3 vViewDirection, vDirection;
// camera position on vector vViewDirection from the ball:
// it needs to be orthogonal to both ball movement direction (vBallDirection) and "up" vector z
// as in example coordinate system z means up.
MatrixVec3CrossProduct(vViewDirection, vBallDirection, Vec3(0,0,1));

MatrixVec3Normalize(vDirection, vViewDirection);
// center of the view would be between ball and the tree
Vec3 vTo = vBall + vBallDirection/ 2;

// we take some distance from "center of the view" - a point that lies on vector vViewDirection
// looking at vTo is then nice landscape view, orthogonal to "up" and ball direction.
MatrixLookAtRH(mTransform, vTo + 3.f * CameraDistanceFromBall * vDirection, vTo, Vec3(0,0,1));


It is important to update cameraAnimation endPoing after each dynamicsWorld->stepSimulation(); as the ball advances to the tree camera needs to follow - after animation is finished, camera will just follow the ball movement. Since the target is the tree, this would give a nice side view of ball hitting and bouncing of the tree, as on both videos above.

Physics (collision) for tree and all tree branches

ngPlant and bulletphysics btIndexedMesh and btTriangleMesh APIs fit together; I'm using:

void P3DHLIPlantInstance::FillVAttrBuffersI( const P3DHLIVAttrBuffers *VAttrBuffers, unsigned int GroupIndex) const

to load vertices, used to render the tree and for physics btIndexedMesh::m_vertexBase.


void P3DHLIPlantTemplate::FillIndexBuffer
(void *IndexBuffer,
unsigned int GroupIndex,
unsigned int PrimitiveType,
unsigned int ElementType,
unsigned int IndexBase) const

to load index of vertices in triangles, used to render the tree and for physics btIndexedMesh::m_triangleIndexBase

This is a code used to add mesh for a tree to btDynamicsWorld.


btVector3 scale(object->size.x,object->size.y,object->size.z);
btTransform worldTrans;
worldTrans.setIdentity();
worldTrans.setOrigin(btVector3(object->loc.x,object->loc.y,object->loc.z));
worldTrans.getBasis().setEulerZYX(object->rot.x,object->rot.y,object->rot.z);


btTriangleMesh* meshInterface = new btTriangleMesh();
// for now, modeling this way trees in blender - a mash with name OBplant_
if (!strncmp(object->id.name, "OBplant_", 6)) {

// get the plant data
PlantGraphicsObject *plant = (PlantGraphicsObject *)createGraphicsObject(object, 0);

// fill the mesh bullet interface with plant branch layer data
for (int i = 0; i < plant->plantData().branchLevels.size(); i++) { 

    btIndexedMesh mesh; 
    const PlantObjectData::BranchLevel &levelData(plant->plantData().branchLevels.at(i));
    mesh.m_numTriangles = levelData.IndexCount / 3;
    mesh.m_triangleIndexBase = (const unsigned char *)levelData.IndexBuffer;
    mesh.m_triangleIndexStride = 3 * sizeof(unsigned int);
    mesh.m_numVertices = levelData.vertexCount;
    mesh.m_vertexBase = (const unsigned char *)levelData.PosBuffer;
    mesh.m_vertexStride = sizeof(float) * 3;
    meshInterface->addIndexedMesh(mesh);
}

// create convex collision object based on mesh bullet interface
btCollisionShape* colShape = 0;
btBvhTriangleMeshShape* childShape = new btBvhTriangleMeshShape(meshInterface, true);

if (scale[0]!=1. || scale[1]!=1. || scale[2]!=1.) {
// I have scaled down the mesh in blender as it was too big
    colShape = new btScaledBvhTriangleMeshShape(childShape,scale);
} else {     

    colShape = childShape;
}

btVector3 inertia(0,0,0);
btRigidBody* colObj = new btRigidBody(0.f,0,colShape,inertia);
colObj->setWorldTransform(worldTrans);

// references ...
colObj->setCollisionShape(colShape);
plant->setCollisionObject(colObj);
colObj->setUserPointer(plant);

// add the physics object to the world
m_destinationWorld->addRigidBody(colObj);