TechnotesHavok Xtra Technotes / FAQ

TechNote#001: What is the Havok Xtra?
TechNote#002: What is a physics engine?
TechNote#003: How do I add Havok physics to a Shockwave 3d sprite?
TechNote#004: What is an HKE file?
TechNote#005: What is a rigid body?
TechNote#006: How do I handle a Havok rigid body?
TechNote#007: What is a proxy object?
TechNote#008: What is the difference between fixed, movable and pinned objects?
TechNote#009: What is a Dashpot?
TechNote#010: Director crashes when I create a rigid body plane?
TechNote#011: How can I get have certain objects not be effected by gravity?
TechNote#012: Director crashes immediately on running a movie with a Havok HKE file?
TechNote#013: My rigid bodies jitter when simulated - what can I do?
TechNote#014: How do I get my Shockwave Movie to download the Havok Xtra if it is not already installed?
TechNote#015: Repositioning 'Fixed' Rigid Bodies
TechNote#016: Repositioning using InterpolatingMoveTo or AttemptMoveTo (Center Of Mass Issue)
TechNote#017: Exporting from Plasma to Director (Pinning/Dashpot issue with Euler ODE)
TechNote#018: Exporting from Plasma to Director ( Strange Behaviour )
TechNote#019: shiftCenterOfMass and centerOfMass
TechNote#020: Collision Lists & Collision Interest
TechNote#021: Disabling and Enabling Collisions

 

TechNote#001: What is the Havok Xtra?

To Top

The Havok Xtra is a fully integrated rigid body physics simulation engine for Macromedia Director. It enables you to assign properties such as mass and elasticity to physical objects, apply forces, impulses or torques and set velocities and momentum. You can also register interest in specific rigid body collisions or even disable them completely. Support is provided for importing Havok HKE (Havok Export) files, which allow full physics scenes to be constructed in a 3D modeler (like 3ds max) without requiring additional Lingo scripts.

 

TechNote#002: What is a physics engine? To Top

A physics engine like Havok has 3 basic tasks to perform:

1. Collision detection: track the movements of all the objects in a simulated world and detect when any of them have collided.

2. Update world: determine an appropriate response for objects that have collided by resolving the collision according to the object properties and for all other (non-colliding) objects update them according to the forces acting on them.

3. Interface with display: once the new positions of all objects have been determined we usually need to display them so the physics engine will report the updates to a 3D display system e.g. a Shockwave 3D sprite.

Note: a physics engine knows and cares nothing about how the objects it is simulating are displayed. It simulates the motion and interaction of these objects based on a physical (and not a necessarily a graphical) description of the objects, This approach with proxy information may be used to simulate a simpler object while display a much more detailed display version. For a more complete description on physics engines in general please refer to the Havok Primer document. All the above tasks are handled automatically by the Havok Xtra via a single Lingo command pHavok.step(). Full details on this function are available in the Havok Xtra Lingo Reference.

 

TechNote#003: How do I add Havok physics to a Shockwave 3d sprite? To Top

There are three approaches to creating physical simulations using the Havok Xtra.

1. Using W3D and HKE files: export, from an external 3D modeler, both the 3D scene as a W3D file and also the physics data for that scene as a Havok HKE file. In this way, no additional Lingo scripts are needed.

2. Using a W3D file: use a Shockwave 3D cast member, created in an external 3D modeler and saved as a W3D file, Havok rigid bodies may then be associated with the 3D models in the Shockwave 3D scene.

3. Through Lingo: all information for the dynamic 3D world is created through Lingo.

For a full description on each method for setup of a Havok controlled dynamic world please refer to the Havok Quick Start Guide.

 

TechNote#004: What is an HKE file? To Top

HKE is a Havok Export file used by the Havok Xtra. In the same way as a Shockwave 3D W3D file contains all of the information necessary for the display system, a HKE file contains all rigid body information needed to dynamically simulate the same virtual world.

The Havok Xtra links rigid body information (stored within a HKE file) to a display geometry (in a W3D file) via their assigned names. This allows a rigid body to have a different representation from that displayed while still retaining the same name. An example of this technique, used via HKE file, is shown in the car tutorial.

 

TechNote#005: What is a rigid body? To Top

The Havok Xtra assumes that all the objects in the simulated world are perfectly rigid (i.e. can never change shape). Hence a rigid body simulation. If all bodies are rigid then we can take advantage of the fact that the geometry of the objects do not vary from step to step and we can memorize these shapes and previous collision results to speed up the next collision test we perform using those objects. This naturally means that objects made out of cloth, liquids or any deformable material cannot be simulated directly.

Deformable objects can be simulated to a certain degree using rigid bodies and spring networks. See the rbCloth demo for an example of a basic cloth spring network. Also it is worth seeing the virtual creature demo.

 

TechNote#006: How do I handle a Havok rigid body? To Top

The setup of a Havok rigid body can be done in two ways: either via the max Havok plug in or via the Lingo scripting language. Detailed information on the max Havok plugin is available with its distribution. We will focus here on the creation and handling of rigid bodies using the Lingo scripting language.

Rigid bodies within the Havok Xtra are intrinsically linked to a model displayed within a Shockwave 3D scene. We must first give Havok access to this displayed geometry information. This is achieved by adding a #meshdeform modifier to the object.

pHavok = member( "Havok")
pWorld = member("World" ) 
if( pHavok.initialized ) then 

  -- Give access to the geometry of a display object 
  mdl = pWorld.model("Object01") 
  mdl.addModifier( #meshdeform ) 

  -- Create required rigid body from display object 
  -- Link via object name 
  if( bWantMovableRigidBody ) then 
    if( bUseBoundingSphere ) then 
      pHavok.makeMovableRigidBody( mdl.name, fMass, bWantConvexBody, #sphere          ) 
    else 
      if( bUseBoundingBox ) then 
        pHavok.makeMovableRigidBody( mdl.name, fMass, bWantConvexBody, #box )          
      else 
        pHavok.makeMovableRigidBody( mdl.name, fMass, bWantConvexBody ) 
      end if 
    end if 
  else 
  … 
  pHavok.makeFixedRigidBody( mdl.name, bWantConvexBody ) 
  … 
  end if 
end if 

Once geometry information is accessible from the display object it is possible to make a rigid body. As can be seen from the above code sample it is only possible to create a rigid body after the physics simulation has been initialized. Hence the created code has been surrounded by a protective if statement which examines the pHavok.initialized boolean flag.

There are a number of other options for creating rigid body. These include whether an body is to be fixed or movable and how the geometry is simulated (bounding sphere, box, convex hull or concave).

After a rigid body has been created it is still accessible via the initialized Havok member. Upon creation a rigid body is assigned the same name as its associated display object. This name can be used to link a variable to a rigid body e.g.

rb = pHavok.rigidBody("Object01" ) 
put rb.mass 
rb.friction = 0.5 

A handle to the rigid body allows it's various properties to be examined and altered. The handle may also be acquired without the need to remember a name. The rigid body access function will also accept integer index values as in the following example script repositions the second rigid body created to the center of the world:

rb = pHavok.rigidBody( 2 )
rb.position = vector(0,0,0)

Finally, if a rigid body is no longer wanted in the system then is a simple matter to remove it as per the following whereby we delete all objects from the simulation:

repeat with idx = 1 to pHavok.rigidBody.count 
  pHavok.deleteRigidBody( idx ) 
end repeat 

Be careful to remove a rigid body from the simulation before its associated display object is ever deleted from the 3D scene. While the simulation will not break you may experience some unexpected results such as an object bouncing of a second supposedly delete object.

For more details on creating, deleting and playing with rigid bodies see the Havok Xtra Lingo reference and the associated Havok Xtra behaviors and demos.

 

TechNote#007: What is a proxy object? To Top

Even though an displayed object in a simulation may appear complex, it need not be simulated with this complexity. For example, a car chassis displayed with detailed panels and including wing mirrors and bumpers etc. might be simulated as a box. This is one of the main ways to reduce the CPU costs of a simulation. These simplified objects representing the more complex display geometry are known as proxy objects or proxy geometries. Artifacts resulting from the use of proxies may be visible and in general you should attempt to make the proxy object as close a fit to the real display geometry as possible. The final choice of proxy will represent a tradeoff between speed of collision and accuracy required and is typically very context specific.

In the proxy example the effect of the choice is plainly visible; a sofa geometry (with a pretty high polygon count) is being represented by 2 different proxies, a sphere and a collection of boxes. The collection of boxes is more expensive to compute collisions for but the sphere has a tendency to roll around, so is probably unsuitable in this case. The linkage between proxy geometry and display is done in Lingo by simply assigning the transform of one object onto another e.g:

on exitFrame me 

  scene = member("World" )
  gHavok = member("Havok")

  m = s.model("ChairForSphere")         
  pm = s.model("ProxySphere")         
  m.transform = pm.transform  
       
  m = s.model("ChairForMesh")         
  pm = s.model("ProxyMesh")         
  m.transform = pm.transform  
        
end
TechNote#008: What is the difference between fixed, movable and pinned objects? To Top

Fixed rigid bodies never move after creation. For the purposes of the Havok Xtra fixed rigid bodies have a mass of zero (in fact in the 3ds max plug in, this is the way you specify an object being fixed). If you think of the momentum of an object being mass * velocity, then you can see that an object of mass zero can never have a non-zero momentum, thus it's fixed. See havok.makeFixedRigidBody() in the Havok Xtra Lingo Reference manual for more details.

Movable rigid bodies are free to do just that - move. Their position and orientation will change depending on the collision detection and resolution within each simulation step. A movable rigid body may also be pinned in place. This effectively turns the movable rigid body into fixed one if only temporarily.

The physics engine can make extra assumptions and optimizations for fixed rigid bodies, hence try to use fixed objects rather than pinning movable objects.

See havok.makeFixedRigidBody() and havok.rigidBody(name).pinned in the Havok Xtra Lingo Reference manual for more details.

 

TechNote#009: What is a Dashpot? To Top

A real spring, such as a coil of wire, regains its original shape after being compressed or extended. The Havok Xtra provides built in support for springs (see havok.makeSpring() in the Havok Xtra Lingo Reference for more details). For the purposes of illustration we can also describe the action of a spring in Lingo. This ultimately involves applying forces to rigid bodies. The on enterFrame section of a spring behavior is shown below. Taking two models and positions on those models the script applies forces of a specified strength to ensure that a desired distance is kept between the them.

-- apply the Spring Behavior 
on enterFrame(me) 

  if( 0 = pStrength ) then return 

  pa = pMember.model( pModelA ).transform * pModelAPoint 
  -- Are we attached to a second model? 
  if offset( "None", pModelB ) then 
    pb = pModelBPoint 
  else 
    pb = pMember.model( pModelB ).transform * pModelBPoint 
  end if 
  d = pa - pb l = d.length 

  if( 0 > l ) then return 
  if( not pActOnCompression and (l < pRestLength) ) then return 
  if( not pActOnExtension and (l > pRestLength) ) then return 

  d.normalize()
 
  rbA = pHavok.rigidBody( pModelA ) 
  rbB = pHavok.rigidBody( pModelB ) 

  va = rbA.linearVelocity 
  if not rbB then 
    vb = vector( 0, 0, 0 ) 
  else 
     vb = rbB.linearVelocity 
  end if 
  v = vb - va 

  f = pStrength * (l - pRestLength) 
  f = f - pDamping * v.dot(d) 
  f = d * f 

  if( not rbA.pinned ) then 
    rbA.applyForceAtPoint( -f, pa ) 
  end if 

  if not voidP(rbB) then 
    if( not rbB.pinned ) then 
      rbB.applyForceAtPoint( f, pb ) 
    end if 
  end if 

end enterFrame 

Whereas springs work with forces (therefore changing acceleration of a rigid body), dashpots use impulses and hence change an objects velocity. Dashpot are analogous to a real world piston in a cylinder of oil, such as the shocks in the suspension of a car.

Dashpots can be thought of as restrictors. There are two built-in types of dashpots within the Havok Xtra - linear and angular. Linear dashpots restrict the position of a rigid body. The position may be restricted to a specific point in the world space or to a point offset relative to another rigid body. This latter restriction, for example, would allow a chain of rigid bodies to be created. (see the mace demo as an example of this) Angular dashpots attempt to restrict a rigid body's rotation. This means that a box, for example, could have its orientation restricted so that it will always try to be balancing a corner.

For reference, the relevant sections of the Lingo representing linear and angular dashpot behaviors are shown below.

-- apply the Linear Dashpot Behavior 
on enterFrame(me) 

if( 0 = pStrength ) then return 

  pa = pMember.model( pModelA ).transform * pModelAPoint 
  if offset( "None", pModelB ) then 
    pb = pModelBPoint 
  else 
    pb = pMember.model( pModelB ).transform * pModelBPoint 
  end if 

  rbA = pHavok.rigidBody( pModelA ) 
  rbB = pHavok.rigidBody( pModelB ) 

  va = rbA.linearVelocity 
  if not rbB then 
    vb = vector( 0, 0, 0 ) 
  else
    vb = rbB.linearVelocity 
  end if 

  imp = pStrength * (pa-pb) + pDamping * (va-vb) 
  if( not rbA.pinned ) then 
    rbA.applyImpulseAtPoint( -imp, pa ) 
  end if 

  if not voidP(rbB) then 
    if( not rbB.pinned ) then 
      rbB.applyImpulseAtPoint( imp, pb ) 
    end if 
  end if 
end enterFrame
         
-- apply the Angular Dashpot Behavior 
on enterFrame(me) 

  if( 0 = pStrength ) then return 

  rbA = pHavok.rigidBody( pModelA ) 
  rbB = pHavok.rigidBody( pModelB ) 

  va = rbA.angularVelocity 

  tRotation = transform() 
  tRotation.axisAngle = [pAxis, pAngle] 

  rbARotation = transform() 
  rbARotation.axisAngle = rbA.rotation 

  if not rbB then 
    tRotation.invert() 
    vb = vector( 0, 0, 0 ) 
  else 
    rbBRotation = transform() 
    rbBRotation.axisAngle = rbB.rotation 
    tRotation = rbBRotation * tRotation 
    tRotation.invert() 
    vb = rbB.angularVelocity 
  end if 

  tRotation = rbARotation * tRotation 
  if not (0 = tRotation.axisAngle[2]) then 
    axis = tRotation.axisAngle[1] 
  else 
    axis = vector( 0, 0, 0 ) 
  end if 

  imp = pStrength * axis + pDamping * (va-vb) 

  if( not rbA.pinned ) then 
    rbA.applyAngularImpulse( -imp ) 
  end if 

  if not voidP(rbB) then 
    if( not rbB.pinned ) then 
      rbB.applyAngularImpulse( imp ) 
    end if 
  end if

end enterFrame          

Springs and Dashpots have a wide range of uses. For a basic introduction see the Springs and Things demo and the Havok Xtra Lingo reference manual. For a more examples of the uses of springs and dashpots check out warehouse, pinball and character control demos.

 

TechNote#010: Director crashes when I create a rigid body plane? To Top

gHavok.makeFixedRigidBody(gPlane.name, true, #box)

This is a known bug: when you use #box it attempts to create a bounding box for the primitive. In this case it's a plane which yields a box with zero height (or width/length) which currently causes a fatal error in the Xtra (divide by zero). This bug has been logged, but for now the workaround is either:

a) don't use a #box proxy geometry: gHavok.makeFixedRigidBody(gPlane.name, true)

b) use concave geometry: gHavok.makeFixedRigidBody(gPlane.name, false)

Each of these gives similar results (you could also use a #sphere proxy). Actually on a point of speed, when you attempt to create a convex body out of a planar primitive, the convex hull can be very complex, making it ever slower than concave. For planes, it is recommended to use the second constructor above.

 

TechNote#011: How can I get have certain objects not be effected by gravity? To Top

Gravity is simply a force applied to every rigid body. It is equivalent to the following section of script with a force variable value of vector( 0, 0, -9.81 ) assuming a scaling in meters. A specific gravity property has been built into the Havok Xtra (see havok.gravity in the Havok Xtra Lingo reference manual). Hence, if the gForce property is used with a value of -havok.gravity, all rigid bodies will experience weightlessness.

However it may not necessarily be enough to call this method every frame.

global gForce 
global gHavok 
         
on enterFrame me 
  gForce = -gHavok.gravity 
  me.applyGlobalForce() 
end 
         
on applyGlobalForce me 
  repeat with i = 1 to gHavok.rigidBody.count do 
    rb = gHavok.rigidBody[i] 
    rb.applyForce( gForce * rb.mass ) 
  end repeat 
end 

The rigid bodies may still fall. The result would be correct if the number of substeps being simulated by the engine were set to one. With substeps greater than one the physics simulation takes more steps per frame of the movie. By default the behaviors are only called each frame, so the applyForce method would be called less often than the physics simulation step is invoked.

To get around this it is necessary to use the gHavok.registerStepCallback mechanism to call your applyForce at each substep.

on beginFrame me 
  gForce = -gHavok.gravity 
  gHavok.registerStepCallback( #applyGlobalForce, me ) 
end 

For more information on this see the Havok Xtra Behavior reference manual.

 

TechNote#012: Director crashes immediately on running a movie with a Havok HKE file? To Top

The HKE file format is not limited to use with the Havok Xtra. Hence it is possible to export information, via the max Havok plug in or Discreet's reactor product, that is not supported by the Xtra. Such information includes: soft, cloth, rope, water and display information. Wherever possible the Xtra simply skips any unsupported features during import.

 

TechNote#013: My rigid bodies jitter when simulated - what can I do? To Top

The jitter is due to numeric tolerances during collision detection, which are largely unavoidable using the fast rather than accurate approach that is required for real time physics. However, the way around this is careful tuning of :

1. collision tolerance (see havok.tolerance in the Havok Xtra Lingo reference)

2. time step and sub steps (see havok.timeStep and havok.subSteps)

3. the energy manager (see havok.deactivationParameters)

Objects at rest ideally "turn off" and are removed from the simulation until hit by other objects. You have control over how aggressive this removal process. You should never see the jitter in a typical simulation. More details on this can be found in the Havok Physics Primer. .

 

TechNote#014: How do I get my Shockwave Movie to download the Havok Xtra if it is not already installed? To Top

For Every Movie you create that uses the Havok Xtra you must make sure that you have the "Download if needed" check box checked for the Havok Xtra. To do this go to Modify->Movie->Xtra in the main menu. Click on "Havok.x32". The "Download if needed" checkbox will now become active. Make sure it is checked. Note: You must do this for every movie that uses the Havok Xtra.

 

TechNote#015: Repositioning 'Fixed' Rigid Bodies To Top

From time to time, you may wish to reposition a pinned rigidbody, and to do this we unpin the body, move it, and then repin it. After each, we need to step the engine as the 'pinned' command only comes into effect executing the step() command. The following code will reposition the ridigbody "tile1" to the vector 'NewPosition'

pRB = gHavok.rigidbody("tile1")

pRB.pinned = 0
pHavok.step(0,1)

pRB.position = NewPosition
pHavok.step(0,1)

pRB.pinned = 1
pHavok.step(0,1)

This could be used for example to reposition tiles of landscape to create a larger world out of a number of smaller more managable tiles.

 

TechNote#016: Repositioning using InterpolatingMoveTo or AttemptMoveTo
(Center Of Mass Issue)
To Top

Both InterpolatingMoveTo and AttemptMoveTo take the center of mass of a rigidbody into account when repositioning. Typically this isn't a problem because in most cases the center of mass of a rigidbody will be located at the origin of the rigidbody (in localspace). But in some cases (where the rigidbody has an non-symetrical geometry, for example) this isn't the case. To get InterpolatingMoveTo or AttemptMoveTo to ignore the center of mass, we can subtract it from the new position, so the code would look like so:

-- move the RB to the same position but with an incremental rotation of 5
-- degrees as a test for interpolatingMoveTo

newPos = coachRB.position - coachRB.centerOfMass
newRot = [coachRB.rotation[1], coachRB.rotation[2] + 5]
coachRB.interpolatingMoveTo(newPos, newRot)

This fixes the discrepency that the center of mass brings into play with regard to postioning, but not rotation. Firstly we need to add these lines if we wish to handle rotations of greater the 180 degrees:

if newRot[2] > 180 then
newRot[2] = newRot[2] - 180
end if

We notice however that upon reaching 180 degrees the axis around which we are rotating will switch, and so even though previously we have been rotating around y for example, now we will be rotating around x. To combat this we must remeber which axis we are rotating around and specify it explicitly on each rotation. So the entire code for incrementally rotating a rigidbody by five degrees, is as follows:

-- first time only: (initialization)
axis = coachRB.rotation[1]
.
.
.

-- each time a 'rotation' is requested
-- specify the axis of rotation
newPos = coachRB.position - coachRB.centerOfMass
newRot = [axis, coachRB.rotation[2] + 5]

if newRot[2] > 180 then
newRot[2] = newRot[2] - 180
end if

coachRB.interpolatingMoveTo(newPos, newRot)

 

TechNote#017: Exporting from Plasma to Director (Pinning/Dashpot issue with Euler ODE) To Top

By default Plasma uses the Euler integrator when exporting files which may cause some trouble when pinning rigid bodies which form a part of a dashpot. Because of speed-accuraccy trade-offs used when employing the Euler Integrator, some rigid bodies will not act as expected when pinned and placed in a dashpot with another rigid body. In fact they may resume
movement instead of remaining fixed*. To alieviate the issue we advise you to export your files from Plasma with 'Runge-Kutta' chosen as the prefered integrator in the RigidBody Collection->Advanced tab.

*Alternatively you could use a point in worldspace instead of a second object for the dashpot.


TechNote#018: Exporting from Plasma to Director ( Strange Behaviour ) To Top

By default when you export from Plasma, a drag action is not exported. 3dsMax however exports the drag action by default and as such, you can get different behaviours from hke's that were exported from Plasma as oppose to 3dsMax. The drag parameters can effect how soon bodies come to rest, and is particularly noticable when using (implicit) sphere's, which have an infintismally small point of contact with a 'touching' body and therefore a neglible friction force. Without drag they won't come to rest, which might make for a freakish pool game, but is generally not desirable. However, the drag Parameters can be set in Lingo, which we would advise you to do if you are exporting from Plasma. See the Havok Lingo Reference Manual for further details.

 

TechNote#019: shiftCenterOfMass and centerOfMass To Top

You can determine the centerOfMass of any rigidbody by using the.centerOfMass property. You can move the centerOfMass of any rigidbody by using the shiftCenterOfMass(vector) function. These methods are available from version 1.2.0.0 onwards.

The centerOfMass property returns a vector and the shiftCenterOfMass function takes a vector as it's sole parameter. Here's an example:

originalCOM = myRigidBody.centerOfMass

myRigidBody.shiftCenterOfMass(someTranslationVector)

newCOM = myRigidBody.centerOfMass

TechNote#020: Collision Lists & Collision Interest To Top

A few points on the working of the collision list:

- The collision list only reports collisions between rigidbodies that you have told havok you are specifically interested in, by calling 'registerInterest'.

- The function 'registerInterest' works in a queue like fashion i.e. if we registerInterest in 'MyBall' twice during setup and then call rmeoveInterest once, then the collisionHandler function will still be called on a collision with MyBall! This is because we have only removed one of our interests in collisions with MyBall. Any which one have we removed? Well, because it is a queue we've removed the first interest - it's a First-In First-Out (FIFO) system.

To clarify, having executing the following few lines:

gHavok.registerInterest ("MyBall", #all, 100, 0, #collisionHandler, me)
gHavok.registerInterest ("MyBall", #all, 10, 0, #collisionHandler, me)
gHavok.removeInterest ("MyBall")

we still have an interest in all collisions with MyBall, and the frequency of collision events is 10.

- When a collision occurs in which we have registered an interest, our specified function will be called. Attempting to removeInterest within a callback script will crash Director.


TechNote#021: Disabling and Enabling Collisions To Top

There are two separate means by which you can disable specific collisions within the havok engine.

The first is simple pairwise-collision filtering:

gHavok.enableCollision(rbA,rbB)
gHavok.disableCollision(rbA,rbB)

When we disable collisions between two bodies, what actually happpens is that the bodies are added to a doNotDisturb List, and although collisions still arise in the engine internally we choose not to resolve them if they
are the doNotDisturb list.

The second method of disabling collisions is much more extreme, the following commands:

gHavok.disableAllCollisions(rbA)
gHavok.enableAllCollisions(rbA)

actually remove or restore the specified body to the collision detector. As a result, if we execute the follwoing two lines:

gHavok.disableAllCollisions(rbA)
gHavok.enableCollision(rbA,rbB)

then rbA does not collide with rbB even though we have just called enableCollision(rbA,rbB)! because rbA isn't present in the collision detector.

To avoid confusion the best way to use think of these commands is as such:
gHavok.disableAllCollisions(rbA) -> remove from collision detection
gHavok.enableAllCollisions(rbA) -> restore to collision detection


Last edited January 2, 2003 11:23 AM