Hey fellow coders, I’ve used Havok Physics at several game studios over the years and I thought I’d share some little-known but hopefully useful tips about this middleware.
For a rigid body, find all rigid bodies touching it
A recent game project involved a house interior scene, and I needed to know what objects were resting on a given table. I could have added a collision listener to every table and maintained lists of contacting objects, but there’s an easier way. The simulation already tracks these contacts every frame for all rigid bodies, so I dug into some undocumented internal classes (hkpLinkedCollidable and hkpSimpleConstraintContactMgr) and extracted what I needed. This function is based on some code in one of the Havok demo projects.
#include <Physics2012/Collide/Agent3/Machine/Nn/hkpAgentNnTrack.h> #include <Physics2012/Dynamics/Collide/hkpSimpleConstraintContactMgr.h> void GetContactingBodies(const hkpRigidBody* body, hkArray<hkpRigidBody*>& contactingBodies) { const hkArray<hkpLinkedCollidable::CollisionEntry>& collisionEntries = body->getLinkedCollidable()->getCollisionEntriesNonDeterministic(); for(int i = 0; i < collisionEntries.getSize(); i++) { const hkpLinkedCollidable::CollisionEntry& entry = collisionEntries[i]; hkpRigidBody* otherBody = hkpGetRigidBody(entry.m_partner); if (otherBody != HK_NULL) { if (entry.m_agentEntry->m_contactMgr->m_type == hkpContactMgr::TYPE_SIMPLE_CONSTRAINT_CONTACT_MGR) { hkpSimpleConstraintContactMgr* mgr = static_cast<hkpSimpleConstraintContactMgr*>( collisionEntries[i].m_agentEntry->m_contactMgr); if (mgr->m_contactConstraintData.getNumContactPoints() > 0) { contactingBodies.pushBack(otherBody); } } } } }
Identify objects in the Visual Debugger
The Havok Visual Debugger (VDB) runs alongside your game and shows a debug view of the physics scene. Often you’ll want to select an object in the VDB and learn more about it. If you have a code debugger attached to your running game (such as the Visual Studio debugger), you can inspect the selected object in your debug watch window.
First, double-click the object in the VDB to bring up Display Object Properties. The object’s id is (by default) the memory address of the associated hkpCollidable. Copy-paste this address into your code debugger’s watch window and do a bit of arithmetic to get the address of the parent hkpRigidBody or hkpShapePhantom (subtract 16 on 32-bit platforms; subtract 32 on 64-bit platforms). Cast to the appropriate pointer type.
You can also override the display id in your game code. Use whatever’s convenient for debugging, for example, the memory address of the parent game object:
void MyGameObject::SetRigidBodyDisplayID() { m_rigidBody->setProperty(HK_PROPERTY_DEBUG_DISPLAY_ID, this); }
Large heightfields in the Visual Debugger
Large heightfields are a problem for the VDB. It displays a 1000×1000 heightfield using approximately two million triangles, which brings the tool’s performance from 50 fps down to 5 fps on my PC. For heightfields larger than about 2400×2400, the VDB runs out of memory and crashes. Fortunately, you can override the display shape for a rigid body. Create an hkGeometry (an indexed triangle list) and assign it to the rigid body like so:
m_body->addProperty(HK_PROPERTY_OVERRIDE_DEBUG_DISPLAY_GEOMETRY_NO_DELETE, displayGeom);
I’ve written a helper function that inspects a source heightfield and overrides the display geometry if necessary. It reduces the display triangle count by downsampling (reducing resolution) by an appropriate factor. There’s also a function to help with cleanup–you need to delete the hkGeometry created earlier.
Click here for the helper functions.
Convex radius should be zero for your static environment
Okay, I lied! This last tip is actually in the Havok docs. I include it here because I’ve seen it frequently overlooked at game studios and even in the Havok demo projects.
Many Havok shape types feature an optional convex radius. This is an extra shell added to your shape, usually 5 cm, and it’s necessary for best performance. However, it’s inconvenient in the case of triangle-based shapes because Havok only uses the shell for shape queries—it’s ignored for raycasts. So, for example, if you position your character above the ground using a capsule query and then run foot IK using raycasts, you’ll get a 5 cm discrepancy.
Fortunately, triangle-based shapes are primarily used for static environments (fixed rigid bodies), and you can safely use a convex radius of zero for static environments. As the User Guide explains, “We only need to ensure that any two interacting bodies have some extra radius between them. As long as all dynamic bodies have an extra radius, the extra radius on a stationary landscape is not necessary.”
Here’s a breakdown of the relevant shapes–I suggest double-checking how you’re using these in your game:
Type | Default convex radius | How to set convex radius |
hkpCompressedMeshShape | 0.05 | Constructor or setRadius() |
hkpExtendedMeshShape | 0.05 | Constructor or setRadius() |
hkpSampledHeightFieldShape | n/a | (No convex radius) |
hkpTriSampledHeightFieldCollection | 0.05 | Constructor or setRadius() |
hkpBvCompressedMeshShape | 0 | hkpDefaultBvCompressedMeshShapeCinfo:: m_convexRadius |
hkpStaticCompoundShape | n/a | (No convex radius) |
Thanks for reading!