 |
Havok
Xtra Technotes / FAQ
| 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
|
|