Monday, March 24, 2014

Havok Physics Engine Tutorial Series: Getting Started

Hi all,
I am starting a new tutorial series on Havok which is an industry standard physics engine. A couple of its components namely the Havok Physics Engine and the Havok Animation SDKs were recently released free (binary only) with sponsorship of Intel under the following terms and conditions.
Havok's Intel® sponsored free binary-only PC download can be used during development to evaluate, prototype, and commercially release any PC game. There are some basic licensing rules to follow:
  • PC titles sold for a retail value of less than $10.00 USD do not require a Havok distribution license to be executed.
  • PC titles sold for a retail value of more than $10.00 USD or more do require a Havok license to be executed but at no additional cost.

Details here: http://software.intel.com/sites/havok/en/

As always, here is the disclaimer, I am not a Havok employee nor do I represent Havok. I am a hobbyist programmer who is trying to fill the online void in the OpenGL world. I have noticed this in the Havok physics engine case as well. So I am trying to make it easier for other OpenGL programmers to get up and running with Havok Physics SDK. All information contained in these tutorials is based on concepts gained from, the two tutorials cited below, the excellent Havok physics user guide and sample demos. These tutorials are written with clarity in mind showing clearly what is required to get started with Havok Physics SDK in VisualStudio 2012 on Windows 7. Note that there might be better and more optimized paths for these tutorials and I hope users will spot those in the comments below the tutorials.

When I started out with Havok Physics SDK, I was really surprised with the detailed documentation given with the Havok Physics SDK. This includes a lot of sample demos which show a lot of varied real-time physics concepts. Unfortunately though, the Havok Physics SDK uses its own DirectX based framework. There are no OpenGL demos in there. Ofcourse hiding the details behind a framework is good but it makes understanding of minute details difficult and you have to dive in code to know what is really required. Therefore, I like other programmers started out but then was frustrated to get up. I went online to find some information and luckily got these two links

http://piotrpluta.opol.pl/programming/havok-physics/
http://dalyup.wordpress.com/2012/02/07/havok-tutorial-01-getting-started/

Both of these cover the basics really well including how to get started from scratch with Havok Physics. I will hope that the readers of this blogs will follow these two tutorials first before proceeding forward. The issue with these is that the Havok sdk has changed a bit and there are some more changes that are required to get up and running with the latest free Havok physics sdk, So here are the missing links.

For all of the tutorial series, I will assume that VisualStudio2012 is used and that you have downloaded the Havok sdk and freeglut libraries to some place on your harddisk. To make it  smoother to follow, I suggest you create two environment variables
  1. HAVOK_ROOT (pointing to the root folder of Havok sdk typically named by date for e.g. hk2013_1_0_r1)
  2. LIBRARIES_ROOT  (generic folder for e.g. E:\Libraries containing the freeglut root folder e.g. E:\Libraries\freeglut-2.8.1
Compiler Settings
OK once this is done, you need to add the following paths to the includes directory (
C/C++ General->Additional Include Directories)
 $(LIBRARIES_ROOT)\freeglut-2.8.1\include;$(HAVOK_ROOT)/Source;%(AdditionalIncludeDirectories)

In addition, add HK_CONFIG_SIMD=1 in preprocessor definitions (C/C++->Preprocessor->Preprocessor Definitions)

Also change the C/C++->Code Generation page so that it appears as shown in the figure below



Linker Settings
Change Additional Libraries Directories (Linker->General->Additional Libraries Directories) to $(LIBRARIES_ROOT)\freeglut-2.8.1\lib\x86\Debug\;$(HAVOK_ROOT)\Lib\win32_vs2012_win7\debug_dll;%(AdditionalLibraryDirectories)

In Linker->Input->Additional Dependencies, add
hkBase.lib
hkCompat.lib
hkGeometryUtilities.lib
hkInternal.lib
hkSerialize.lib
hkVisualize.lib
hkaInternal.lib
hkaAnimation.lib
hkaPhysics2012Bridge.lib
hkpConstraint.lib
hkpConstraintSolver.lib
hkpCollide.lib
hkpDynamics.lib
hkpInternal.lib
hkpUtilities.lib
hkpVehicle.lib
hkcdCollide.lib
hkcdInternal.lib

That's it for the compiler and linker settings now to the real code. First, the include files

Include Files
To get our very first tutorial up, we need to include the following headers


Havok Initialization
A lot of code is required to initialize Havok. I will go through these step by step. Basically all Havok codes need to initialize at least two main components (Havok Memory Settings and Havok Physical World Settings) and an optional third component (the Visual Debugger). I create an InitialzieHavok function which is defined as follows

Basically, this code is just calling the three initialization functions. Here we will look at each of these one by one.

(a) Initializing Havok Memory routines       
I create a simple function InitMemory which does the memory initialization.  Here is how the InitMemory function is defined.

Lets see the details of the function piece by piece. First is a macro call _MM_SET_FLUSH_ZERO_MODE 
which is used to ensure that there are no subnormal numbers (numbers that are very small close to zero) because they can lead to slower performance. If you want to know more about this macro, have a look at the wikipedia entry   
http://en.wikipedia.org/wiki/Denormal_number

The above calls initialize the memory subsystem by allocating 0.5MB for physics calculation with a memory router using the default allocator. We also call the Havok base system initialization function passing it our memory router and an error callback function. I just name this error callback OnError and I define it globally as follows which just dumps the passed msg to standard error stream (std::err)

Next, we create a thread pool with the given number of threads. We get the current device's capabilities to obtain the maximum number of parallel threads available on the multithreaded platform we are running on. 


We then create a job queue for Havok modules to run multithreaded work. Finally, the function ends with the creation of a stream of monitors for the thread pool.

  
(b) Initialize Physical World
The InitPhysicalWorld is defined as follows

In Havok, the physics simulation world is represented by a hkpWorld* object. This object is initialized by calling the hkpWorld constructor passing it the hkpWorldCInfo structure. This structure stores the global physics world settings like gravity etc. We first set the simulation type to a multi-threaded simulation. We then set the broadphase border behavior (which tells to the Havok physics engine to remove an entity if it goes out of the border). We pass the modified hkpWorldCInfo structure to the hkpWorld constructor to create our Havok physics world object. After this call, we set the deactivation flag of the hkpWorld to false to ensure that there is no deactivation of rigid bodies. At this point we have our physics world object created.

The next few calls modify the hkpWorld. To ensure that no two threads modify the shared hkpWorld instance at the same time, we issue a call to hkpWorld::markForWrite function. After this call we can issue all calls that modify the state of the physics world. We register collision dispatchers and the created job queue. Note that hkpWorld::markForWrite function call is paired with hkpWorld::unmarkForWrite call which is issued in the InitVDB detailed below. 

(c) Initialize Visual Debugger
Usually, you will need some mechanism to ensure that your physics world is behaving as expected. For debugging purposes or for checking physics simulation states, Havok provides a very useful application called the Visual Debugger in the SDK. We need to establish a connection to the running instance of the Havok Visual Debugger. This connection is established by creating an instance of the hkVisualDebugger object. This is done in the InitVDB function which is defined as follows.

We first create a Havok Physics context object (hkpPhysicsContext). We then call the static function registerAllPhysicsProcesses function. We then add the Havok physics world to the created hkpPhysicsContext by calling hkpPhysicsContext::addWorld function. We then store the hkpPhysicsContext pointer in an hkArray object. We then call the hkpWorld::unmarkForWrite function that was paired with the hkpWorld::markForWrite in the InitPhysicalWorld function. The hkArray containing the hkpPhysicsContext object is passed to the hkVisualDebugger constructor and then the hkpVisualDebugger::serve function is called to initialize the connection with the hkVisualDebugger. The connection will be established with the running instance of the visual debugger.

Stepping the Havok Physics Engine and the Havok Visual Debugger 
In order to mode the Havok physics engine and the visual debugger forward in time, we need to make a call to the step function. This call is made in each frame before calling the render function. I name this function StepHavok and it is defined as follows.

We first call hkpWorld::stepMultithreaded function passing it the job queue and the timestep value which is given a constant step size of 1/60. Next, if the visual debugger is enabled, we step visual debugger using StepVDB function which is implemented as follows.

The StepVDB function first syncs timers in the thread pool and then calls the hkVisualDebugger::step function passing it the time step value which is also a constant step size of 1/60. Finally, the hkMonitorStream is reset and the time data values in the thread pool are cleared.

Havok Shutdown
The Havok Physics engine shutdown is carried out in the ShutdownHavok function. This function is defined as follows.

We first ensure that the is thread safe deletion by calling hkpWorld::markForWrite function. Then, we call hkpWorld::removeReference which deletes the hkpWorld object. Since Havok internally keeps reference counts, the recommended approach to delete all Havok objects is to call removeReference on the object pointer instead of the delete function. Next, we delete the job queue and then call removeReference on the thread pool object. If the hkVisualDebugger is enabled, we call the ShutdownVDB function. Finally, we call hkBaseSystem and hkMemoryInitUtil interface's quit functions. The ShutdownVDB function is defined as follows.

We first call, hkVisualDebugger::removeReference function to delete the connection to the hkVisualDebugger instance. We then delete the context pointer again by calling hkpPhysicsContext::removeReference function. 

Output
Running this tutorial does not show anything interesting. We just get a simple 3D grid rendered on screen as shown below. 

The console output shows the Havok initialization messages as shown below. It should only display the messages shown in the following figure. 


If you get any other message like some errors or stacktrace information, you are probably doing something incorrectly.

That's it for the first getting started tutorial. You can get the full source code from my github repository here: https://github.com/mmmovania/HavokPhysicsTutorials

Thanks,
Mobeen




 





0 comments:

Popular Posts

Copyright (C) 2011 - Movania Muhammad Mobeen. Awesome Inc. theme. Powered by Blogger.