Tuesday 23 July 2013

Getting Started : Havok



Another very popular physics engine is Havok, and although I have not worked with professionally, here is how I went about getting a sample running.

0) Setting up the environment

In this case I will be using the hk2011_3_1_r1 version of Havok. Using Premake to generate the Visual Studio solution. Here I have also set havokSDK to point to the  hk2011_3_1_r1 directory, and we will need to include the following libraries. I have omitted some of the recommended ones for animation simply because they will not be necessary for this example.

--Common Libraries (hk)
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkBase' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkSerialize' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkSceneData' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkInternal' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkGeometryUtilities' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkVisualize' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkCompat' }

--Physics Libraries (hkp)
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkpCollide' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkpConstraintSolver' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkpDynamics' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkpInternal' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkpUtilities' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkpVehicle' }

-- Collision Libraries (hkcd)
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkcdInternal' }
   links { havokSDK .. '/Lib/win32_net_9-0/debug_multithreaded_dll/hkcdCollide' }

    configuration "Debug"
      flags { "Symbols" }
      defines { "WIN32", "_DEBUG", "HK_DEBUG", "_CONSOLE", "HK_CONFIG_SIMD=2" }

1) Including the Headers

There may be some alternative to include list, but it will become relatively obvious why we include each of these as we use classes and methods from each.

// Base
    #include <Common/Base/hkBase.h>
    #include <Common/Base/Memory/System/Util/hkMemoryInitUtil.h>
    #include <Common/Base/Memory/Allocator/Malloc/hkMallocAllocator.h>
    #include <Common/Base/Fwd/hkcstdio.h>

// Physics
    #include <Physics/Collide/Dispatch/hkpAgentRegisterUtil.h>
    #include <Physics/Dynamics/World/hkpWorld.h>
    #include <Physics/Dynamics/Entity/hkpRigidBody.h>
    #include <Physics/Collide/Shape/Convex/Box/hkpBoxShape.h>
    #include <Physics/Collide/Shape/Convex/Sphere/hkpSphereShape.h>
    #include <Physics/Utilities/Dynamics/Inertia/hkpInertiaTensorComputer.h>

    #include <Common/Base/System/Init/PlatformInit.cxx>

// Visual Debugger includes
    #include <Common/Visualize/hkVisualDebugger.h>
    #include <Physics/Utilities/VisualDebugger/hkpPhysicsContext.h>

2) Adding the namespace

No namespace necessary.

3) Initialization

Initialization is relatively simple, but we will need to define an error reporting method, usually written as follows.
    
    static void HK_CALL errorReport(const char* msg, void* userContext)
    {
        using namespace std;
        printf("%s", msg);
    }

Then we can proceed as normal, initializing the memory utility and the base system using the default memory allocator.

    hkMemoryRouter* pMemoryRouter = hkMemoryInitUtil::initDefault(
        hkMallocAllocator::m_defaultMallocAllocator, hkMemorySystem::FrameInfo( 500*1024));
    
    hkBaseSystem::init( pMemoryRouter, errorReport );

4) Creating the Scene
    
Havok varies from other physics engines in terminology by creating a physics 'world' instead of a 'scene' in which all actors, shapes, etc are placed. The world is created by first filling a descriptor class. Here we will keep the world as a global static pointer because it will be used frequently elsewhere.

    static hkpWorld* sgpWorld = NULL;

    hkpWorldCinfo kWorldInfo;
    kWorldInfo.m_gravity.set( 0, -9.81, 0.0);
    kWorldInfo.setBroadPhaseWorldSize( 150.0f );
    kWorldInfo.m_collisionTolerance = 0.01;
    kWorldInfo.setupSolverInfo( hkpWorldCinfo::SOLVER_TYPE_4ITERS_MEDIUM );
    sgpWorld = new hkpWorld( kWorldInfo);
    
    hkpAgentRegisterUtil::registerAllAgents(sgpWorld->getCollisionDispatcher());

5) Creating Actors

Rigid body actors, known as 'entities', are created in a manner similar to the world: first by filling a descriptor class used for the entity's constructor. A single entity can only contain a single shape

    hkVector4 halfExtent(1.0f, 1.0f, 1.0f);
    hkpShape* pBoxShape = new hkpBoxShape(halfExtent);

    hkpRigidBodyCinfo kRigidBoxInfo;
    kRigidBoxInfo.m_shape = pBoxShape;
    kRigidBoxInfo.m_mass = 1.0f;

This shape only represents the collision volume, and can have an alternative center of mass, and inertia tensor specified.

    void hkpRigid BodyCinfo::setMassProperties(const struct hkpMassProperties& mp);

The motion type for the rigid body is either fixed, MOTION_FIXEDor dynamic. Fixed motion the equivalent to static/kinematic in PhysX.

    kRigidBoxInfo.m_motionType = hkpMotion::MOTION_DYNAMIC;
    kRigidBoxInfo.m_position = hkVector4(0.0f, 10.0f,0.0f, 0.0f);
    kRigidBoxInfo.m_friction = 0.0;
    kRigidBoxInfo.m_restitution = 1.0;

Once the descriptor is filled the actor is created through its constructor. 

    hkpRigidBody* pRigidBox = new hkpRigidBody(kRigidBoxInfo);

The entity exists independently and can be added or removed from the the world without re-creation.

    sgpWorld->addEntity(pRigidBox);

The shape and entity are now also referenced by the world, so we decrement their internal reference counter.

    pRigidBox->removeReference();
    pBoxShape->removeReference();


6) Simulation

The simulation step is straightforward in this case. Just a single call to step a particular world forward some amount of time.
    
    sgpWorld->stepDeltaTime(fTimestep);

7) Shutdown/Cleanup

Keeping a list of the entities created, and removing them from the world when finished. 

    //hkArray<hkpRigidBody*> aEntityList;

    for(int i=0;i<aEntityList.getSize();i++)
    {
        sgpWorld->removeEntity(aEntityList[i]);
    }

Lastly, the base system and memory utility that where initialized at the start have their own static calls.

    hkBaseSystem::quit();
    hkMemoryInitUtil::quit();

This is fairly basic, but is copied from a working sample. I plan to provide those source files at a later time.
Until then, happy coding.