Advanced Havok Xtra Issues
The Undocumented Havok Xtra
(Thursday, June 27, 2002)

This article is motivated by the need to explain the strange behaviors of the Havok physics simulation Xtra. Long story short: the Havok Xtra needs work. It's a great tool and sure beats rolling your own, but it's not yet a polished product. Hopefully the developers will take note of comments from the community and incorporate some improvements into the next version.


(1) Havok doesn't like models generated by cloneModelFromCastmember ()

The cloneModelFromCastmember () function lets us do tricky linking of modelresources between Shockwave 3D cast members. Havok doesn't appear to "see" models created in this way, so it will return void when trying to create rigid bodies from such models. One solution posted to the DIRECTOR-L mailing list was to call updateStage () just after calling addModifer (#meshDeform) on the copied model, before creating the rigid body. In essence, we do the following (assuming that "scene" is the cast member we're adding the model into, and "Crate 01" is the cast member containing the model we want to copy, named "TheCrateP"):

model = scene.cloneModelFromCastmember("TheCrate", "TheCrateP", member("Crate 01"))
model.addModifier (#meshdeform)
updatestage ()
hvk.makeMovableRigidBody(model.name, 10.0)

I got this to work only by setting a breakpoint in my script then closing the debug window and continuing execution. However, it wouldn't work under non-debug circumstances. I'm still working on this one, so I'll post a final solution soon. A possible workaround (but less flexible) is to fall back on loadFile () to load the Shockwave 3D file containing the modelresource. This doesn't work for me either, so I'm suspecting that it may be related to loading state, due to the fact that debugging adds a delay that may result in the code running properly.


(2) Collision detection doesn't report collisions between objects that interpenetrate before the system is initialized

If two rigid bodies interpenetrate when Havok is initalized, they will pass through each other without colliding. The only workaround is to look for strange collision behavior in the simulation then investigate the starting positions of objects acting strangely.


(3) Calling havok.removeInterest () in a collision handler crashes Director

It just plain crashes if you call it in the collision handler registered with havok.registerInterest (). Seems to work in any other context, though.


(4) havok.registerInterest () is additive

For example, add the following code (swap objects names to match your application) to the Shockwave 3D sprite:

hk.registerInterest ("TheBall", #all, 100, 0, #collision, me)
hk.registerInterest ("TheBall", #all, 10, 0, #collision, me)
hk.removeInterest ("TheBall")

You'd think that no collision events are generated, but the collision handler is *still* called! In addition, the frequency of collision events is 10 per second. From this we see that registerInterest () and removeInterest () act like queue operations, one pushes and the other pops. So if you want to make sure you're not generating any collision events, call removeInterest () the same number of times as you called registerInterest ().


(5) Can't determine if collision is first in a series of collisions

First impact is important for executing sound and particle effects to enhance the realism in a scene. If we can't distinguish between first impact and subsequent interactions, for example, when a bullet strikes an object, we'll end up with a rapid staccato of sound effects playing over and over.

There's no direct way of querying the Havok Xtra to determine whether a collision reported to a collision handler is the first collision between two objects or if it's a continuation of the first impact (e.g., pushing, sliding—nearly stationary forces). There are two viewpoints on the definition of "first collision":

  1. Impulse-based: First collision is distinguishable by a larger impulse than subsequent collisions. The downside is that we've got to define some arbitrary constants and analyze collisions over time.
  2. Temporal: Add colliding pairs to an already-colliding list, call first collision handler upon adding the pair. Check pairs in the list for collision after every time step, remove a pair when it's no longer colliding. This solution is clean and doesn't involve the use of arbitrary constants nor do we need a good understanding of impulse physics. The downside is a lot of list bookkeeping.

If you think about it, the impluse-based method is more physically-correct. Envision a bullet fired into a metal wall: when it ricochets a quick loud sound will result. Now imagine the same bullet is wedged between two metal walls which are slowly pushed together with increasing force. The magnitude of the force exerted on the bullet by the walls in the second case is the same as in the first case, except it takes longer to reach this magnitude in the second case. Thus, we must take into account the force over time.

We can emulate the impulse-based approach using the temporal approach: just analyze the impulse upon the first collision. This way we get a clean definition of first impact and we can use the impulse to modulate whatever special effect we execute. E.g., emit dust particles around the boundary of the collision manifold upon first impact, with the amount and speed of the particles depending on the impulse upon impact. But this still misses some special cases, like when a bullet resting against a wall is "smashed" into it by a mallet with enough force to emit a bullet sound effect.


(6) havok.collisionList only contains colliding pairs that have been registered with havok.registerInterest ()

Want to get a list of *all* colliding objects? Just look up havok.collisionList, right? Wrong. To see why, we'll do a simple test. I'll assume you've got a scene setup just like the Havok Quickstart Guide. So there's a Havok member and a Shockwave 3D member with a sphere model called TheBall, a cube model called TheBox, and another cube model called TheGround.

Then, to be *extra sure* we're not missing any in-between steps, we register a time-step callback:

hk.registerStepCallback (#stepper, me)

with the #stepper handler defined as follows:

on stepper me, timeStep
  if hk.collisionList <> [] then
	put hk.collisionList
  end if
end

Run this movie—look at the message window. Nothing! Now we try the same experiment, except we register interest in the models TheBall and TheCube:

hk.registerInterest ("TheBox", "TheBall", 10, 0, #collision, me)

Recompile the script and run the movie again. Now the message window displays the names of *only two* of the three objects colliding in the scene. Why? Because havok.collisionList only holds collisions between objects registered to be "of interest"*.

But we haven't exhausted every possibility for achieving what we want—what about havok.enableCollisions ()? Good guess—but it doesn't work either.


(7) havok.collisionList only contains colliding pairs for the timestep in which the collision callback is called

Yet another strange artifact of the Havok's collision detection system. To see what I'm talking about, register a time-step callback (as we did above)

hk.registerStepCallback (#stepper, me)

with the #stepper handler defined as follows:

on stepper me, timeStep
  if hk.collisionList <> [] then
	put hk.collisionList
  end if
end

Now setup some objects in your scene so that they'll collide with each other, and write a collision handler that prints a notification message every time it's called.

on collision me
  put "collision handler called"
end

Notice that the collision list printed in the message window is empty for time-steps that don't correspond to a call to the collision handler. This means that collisions effectively "don't occur" during any time-steps that run with higher frequency than the collision handler.

See Also:
Optimizing Real-time Physics
Lingo Pitfalls