Thursday, December 15, 2011

Skeleton and pose character animation using AnimKit

Objective: How to implement pose and skeleton character animation in OpenGL ES 2.0?

I don't want to use gamekit with OGRE 1.8 rendering engine - need something lighter. Don't see this as "reinventing the wheel" - though there is nothing wrong with "reinventing the wheel" when it is fun, when you enjoy doing it. Hunter S. Thomson typed copies of Hemingway's books - he enjoyed doing it and he knew why he was doing it. I believe he wasn't having a feeling he would live for 1000 years - so, why not waste a bit of time... IMHO, doing this is the best/fastest way for me to learn and it is fun. Guess one could say that I could also type copy of Hunter S. Thomson articles, before continuing, to make this blog more fun to read.

Anyway, I think this is just what I need - gamekit's AnimKit. Briefly checked the code and run it on my desktop: it supports vertex (pose and morph) and skeleton animations, animation blending... inverse kinematics in the roadmap. Grepped for MATRIX_PALETTE - seems to have software (on CPU) animation implemented only. There is nice demo application AppAnimKitGL which looks quite promising. It is dependent on libGLU, libGLEW and libGLUT, and with fixed rendering pipeline. So, there is some effort needed to port it to iPhone and N9 - but I did this work in previous posts/examples so it should go faster now. Let's see. Plan to first focus on opengles 2.0.
...
Continuing:
Had some time during the weekend to check this. Here is how it looks on Nokia N9 running next to the AnimKit demo on Ubuntu:


Note that I did not port all of the demo helper features (bones, normal rendering, etc). Difference in texture color on N9 comes from using loaded BGR texture as RGB - if you plan to use the same code, you'll probably use different Blender model and different texture loader.
Code is available here: https://github.com/astojilj/astojilj_animkit/commit/df571339ab55d74681badf8c88cc468cedb3d372
To run it on Nokia N9, open Samples/AnimKitGL/AppAnimKitGl.pro with QtCreator and just build&deploy. If you intend to run original demo on desktop, note that you'll need to have Blu.blend in current directory.

Next thing I plan to do is add (copy from previous project) iPhone/iPad project support files.

...

iPhone version




Note that the fragment shader is different from N9 version - uncommented the code that is darkening the borders and doing the phong (giving the cartoon shading look).

Started from XCode, File->New Project->OpenGL ES Application (iPhone). After this, added all the sources to the project, replaced all "in project" project includes using <> with "" (e.g. #include "Mathematics.h") and added OPENGL_ES_2_0 to Preprocessor Macros item in Target Info dialog. Added also code to printout frames per second info.

This made the things compile, but when run I couldn't see anything rendered. Both use the same memory layout (little endian) so that wasn't a problem. Turned out that with 15000+ indices using glDrawElements with GL_UNSIGNED_INT indices doesn't work. GL_UNSIGNED_SHORT does.
Put a quick fix in the code with following comment:


// in iOS 4.1 simulator, glDrawElements(GL_TRIANGLES, cnt, GL_UNSIGNED_INT did not work - did not show anything on screen
// while GL_UNSIGNED_SHORT works. Using this as temporary workaround until trying with new sdk or anyway, change types
// in animkit code (no reason to use unsigned int anyway).


This got the scene rendered, and only thing left to do was to add depth buffer support (as default XCode SDK skeleton code doesn't have it on).

Code for the example running on iPhone (note that I was using iOS SDK 4.1 - yep, plan to update soon...) https://github.com/astojilj/astojilj_animkit/commit/5c11f660281bb8c8a6b5e3bba93938f703b6307c.  Few fixes added later here: https://github.com/astojilj/astojilj_animkit/commit/03545c7d1ea753c078aab4d441d4f12d91d462b8
Plan to merge it to master after verifying changes don't break anything on N9.

Next thing to try is scene with multiple actors with animated skeleton (I think I'll do cartoon animals) hardware (on GPU) matrix palette skeleton animation.

Tuesday, December 6, 2011

Collision detection for character - hit character falls down as rag-doll


Objective: how to implement collision detection for animated character? Character has bone system and it's position and movement is controlled by pose and animations but once it gets hit it needs to fall down like a rag-doll.

Get character model to ReadBlend: There is no code change needed for this part - if you have existing model with character, open project's blend file (PhysicsAnimationBakingDemo.blend) and use Blender's File->Append or Link command. Once you select blender file to "append" character model, select Objects and then selects meshes, armatures or constraints to import. I tried with this file and it worked - got the static character displayed in the demo. However, with ~300 triangles per each piece it was needed to reduce number of vertices not to let it affect frames per second count. If you hit an error "Error Not A Library" in Blender when appending, usually it is related to incompatible versions - open biped-rig blend file, save it (overwrite) and then try append from it again.  

Once I got biped_rig's character displayed next to the tree, it just didn't look nice. Decided to do my own made from boxes; it didn't take long to make head, spine, pelvis, upper and lower legs and arms, feet (and hands) from boxes used in the scene. When model is created, assuming all body pieces are marked as default "Static" (and not as "Rigid body") in Blender, character made of boxes gets displayed in the demo in standing position, not affected by gravity but with ball bouncing against it. If body parts would be marked as "Dynamic", ball would push them. So, for the character body parts, controlled by animation proper mode is Dynamic. Once the contact with bullet is detected, we will just "switch" them to be affected by gravity and they'll fall down.

Modeling joint constraints in Blender: to get "more realistic" rag-doll joint constraints, I need btConeTwistConstraint and btHingeConstraint and to set limits. Check the details for joint constraints example from Bulletphysics Ragdoll example code. I didn't set the limits in Blender 2.49, which you can tell from the demo bellow. When needed, constraints could be tweaked in C++ code (BulletBlendReaderNew::convertConstraints()). Anyway, just put joint constraints between body parts (this tutorial might help) to Blender file and they work also on phone.

Turn on the gravity for character body parts - when character is hit, it needs to fall down like a rag-doll: just after stepSimulation(), call processContactData() to check collision between ball and character:

void BulletBlendReaderNew::processContactData()
{
    //Assume world->stepSimulation or world->performDiscreteCollisionDetection has been called
    int numManifolds = m_destinationWorld->getDispatcher()->getNumManifolds();
    for (int i=0;i<numManifolds;i++)
    {
        btPersistentManifold* contactManifold =  m_destinationWorld->getDispatcher()->getManifoldByIndexInternal(i);
        btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0());
        btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1());


        int numContacts = contactManifold->getNumContacts();
        if (!numContacts)
            continue;


// check if one of colliding shapes is ball
        if (obA != ball && obB != ball)
            continue;


// this is simplified check if another collision shape is part of character's armature
        if (obA->getCollisionShape()->getUserPointer() == &(this->armature)
                || obB->getCollisionShape()->getUserPointer() == &(this->armature))
        {
            // convert armature -> ragdoll in case ball hits character
            turnToRagdoll(&(this->armature));
        }
    }
}


void BulletBlendReaderNew::turnToRagdoll(ArmNode *nodeList)
{
    ArmNode *next = nodeList;
    while(next) {
        if (next->piece)
            ((btRigidBody*)next->piece)->setAngularFactor(1.f);
        next = next->nextSibling;
    }
}



Note that all the body parts' meshes are marked as "Dynamic" in Blender's Logic panel (F4). Dynamic bodies have setAngularFactor set in ReadBlend set to 0.f initially in BulletBlendReaderNew::createBulletObject().

In case of complex sophisticated character meshes, it makes sense to use the same technique, and not only on constrained systems (like iPhone and N9) - don't add complete mesh to physics world for collision and gravity simulation, but create "shadow" model out of boxes only for physics and collision. Then, when rendering rigged mesh, use motionState to get location (transformation) of each box (bone in simplified armature in physics simulation world) and then apply transformation to complex rigged character's bones when rendering it. Simple physics model would not be rendered, it would just exist in physics world.