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.

No comments:

Post a Comment