|
Articulated figures are called "rag dolls" by most people. The
following is an excellent document explaining how they work,
typed up by the most excellent MrElusive.
1. Articulated Figures & Skeletal Models
An articulated figure is a collection of rigid bodies and constraints that hold
these bodies together. A dynamic simulation based on Newtonian mechanics is used
to move the bodies through the world under the influence of forces and collisions.
Each body of an articulated figure modifies one of the joints of a skeletal model.
The change in rotation of a body relative to it's initial position is applied to
a joint of the skeletal model.
To create an articulated figure an md5 mesh with skeleton is required (.md5mesh).
Furthermore an initial animation (.md5anim) is required with a single animation
frame. This animation frame sets the model in a pose which is used to create the
articulated figure. For human like characters this is usually a T-pose. The single
frame of the initial animation also specifies the initial joint positions as they
are modified by the bodies of the articulated figure.
Each articulated figure is specified in a separate .af file, stored in the base/af/
folder. To load and test an articulated figure in the game an entityDef is created
in one of the .def files stored in the base/def/ folder. Such an entityDef typically
looks like the following:
entityDef env_ragdoll_fatty {
"editor_color" "1 .5 0"
"editor_mins" "-8 -8 -8"
"editor_maxs" "8 8 8"
"editor_usage" "Ragdoll for monster_zombie_fat\n"
"spawnclass" "idAFEntity"
"bleed" "1"
"sleep" "1"
"skin" "skins/models/monsters/fatty.skin"
"model" "models/md5/monsters/zfat/zfat.md5mesh"
"anim af_pose" "models/md5/monsters/zfat/initial.md5anim"
"articulatedFigure" "monster_zombie_fat"
}
The "spawnclass" always points to "idAFEntity". The key "sleep" is set to 1 so the
articulated figure will not immediately start moving when spawned. The "model" key
points to the md5 mesh. The "anim" key with "af_pose" extension points to the file
with the initial animation. The "articulatedFigure" key points to the .af file to
be used.
A .af file first stores a section with general settings followed by a collection of
bodies and constraints.
2. Settings
The articulated figure settings are specified with a structure as follows:
settings {
key params
}
mesh "md5mesh" | The md5 mesh used for the articulated figure |
anim "md5anim" | The initial animation (t-pose) |
skin "skin" | A skin to use |
friction <linear>, <angular>, <contact> |
The default friction for all bodies.
The linear and angular friction values specify the translational and rotational air
friction. The contact friction value specifies the friction with contact surfaces.
The friction values are usually in the range [0, 1].
|
selfCollision 0/1 | The default setting for self collision detection |
suspendSpeed <linear velocity> <angular velocity> <linear acceleration> <angular acceleration> |
The speed at which the articulated figure comes to rest
The specified linear and angular velocity is the maximum velocity at which the
articulated figure may come to rest. The specified linear and angular acceleration
is the maximum acceleration at which the articulated figure may come to rest.
|
totalMass <mass> | The total mass of the articulated figure.
If the total mass is set to a value greater than zero then the mass of each body
is scaled such that the total mass of the articulated figure equals the given mass.
|
3. Bodies
A body is specified with a structure as follows:
body "name" {
key params
}
joint "joint name" | The joint the body modifies
There is always one body which modifies the origin joint of the skeletal model.
The position and orientation of this body are used to move the whole skeletal
model through the world. The joint key for this body is set to "origin".
|
model [collision model] |
A body can use several different collision models. Such a collision model is
specified with the model key:
model box( (min_x, min_y, min_z), (max_x, max_y, max_z) )
model octahedron( (min_x, min_y, min_z), (max_x, max_y, max_z) )
model dodecahedron( (min_x, min_y, min_z), (max_x, max_y, max_z) )
model cone( (min_x, min_y, min_z), (max_x, max_y, max_z), numSides )
model cylinder( (min_x, min_y, min_z), (max_x, max_y, max_z), numSides )
model bone( (start_x, start_y, start_z), (end_x, end_y, end_z), width )
model custom( "name" )
|
origin ( x, y, z ) |
The collision model has to be placed at the right position relative to the joint
which is modified by the body. This key specifies the position of the center of the collision model.
|
angles ( pitch, yaw, roll ) | The collision model can be rotated about it's origin with the angles key |
density <value> | The density of the collision model. The mass of the body equals the density times the volume of the collision model. |
friction <linear>, <angular>, <contact> |
Different friction properties can be specified for each body.
The linear and angular friction values specify the translational and rotational
air friction. The contact friction value specifies the friction with contact
surfaces. The friction values are usually in the range [0, 1].
|
selfCollision 0/1 | Set self collision detection per body |
containedjoints "jointlist" |
Projectiles collide with the visual mesh of the skeletal model. For an impact
position on the mesh the nearest joint of the skeletal model is taken. From this
joint the impact is transferred to one of the bodies of the articulated figure.
Each body has a list with joints that are more or less contained by the body. The
impact near a joint is transferred to the body that contains the joint. A joint
can only be contained by one body and each joint must be contained by a body. The
joints contained by a body are specified with the containedjoints key. |
4. Constraints
Several constraints can be used to connect the bodies of an articulated figures.
A constraint is specified with a structure as follows:
constraintType "name" {
key params
}
The constraintType is one of the following constraint types:
- ballAndSocketJoint
- universalJoint
- hinge
- spring
For each constraint two bodies are specified which are connected by the constraint.
These bodies are specified with the body1 and body2 key.
body1 "name"
body2 "name"
The body2 key may point to the special body called "world" and in this case the
constraint will connect body1 to the world.
Other than the two connected bodies, each constraint has a number of settings specific
to the constraint type as listed below.
4.1 Ball and Socket Joint
anchor ( x, y, z ) | Specifies the center of the ball (required) |
conelimit ( axis_x, axis_y, axis_z ), <angle>, ( shaft_x, shaft_y, shaft_z ) |
Specifies a cone shaped limit.
The cone axis is specified by the first vector. The angle of the cone at the
top follows. The cone is attached to body2. Next a shaft is specified which is
attached to body1 and is constrained to always stay within the cone.
|
pyramidlimit ( axis1_x, axis1_y, axis1_z ), <angle1>, <angle2>, <angle3>, ( shaft_x, shaft_y, shaft_z ) |
Specifies a pyramid shaped limit.
The pyramid axis is specified by the first vectors. The first and second angle specify the
angles at the top of the pyramid. The third angle specifies the rotation of the pyramid
about the pyramid axis. The pyramid is attached to body2. Next a shaft is specified which
is attached to body1 and is constrained to always stay within the pyramid.
|
friction <value> | Specifies joint friction. The friction value is usually in the range [0, 1]. |
4.2 Universal Joint
An universal joint works very similar to a ball and socket joint. However the rotation
about two shafts attached to the bodies is also constrained.
anchor ( x, y, z ) | Specifies the anchor of the universal joint (required) |
shafts ( dir1_x, dir1_y, dir1_z ), ( dir2_x, dir2_y, dir2_z ) | Specifies the two shafts (required)
The first shaft is attached to body1 and points into or towards body1. The second shaft
is attached to body2 and points into or towards body2. The rotation of body1 relative to
body2 about these shafts is constrained.
|
conelimit ( axis_x, axis_y, axis_z ), angle |
Specifies a cone shaped limit
The cone axis is specified by the first vector. The angle of the cone at the
top follows. The cone is attached to body2. The first shaft attached to body1 is
constrained to always stay within the cone.
|
pyramidlimit ( axis1_x, axis1_y, axis1_z ), angle1, angle2, angle3 |
Specifies a pyramid shaped limit.
The pyramid axis is specified by the first vectors. The first and second angle specify
angles at the top of the pyramid. The third angle specifies the rotation of the pyramid
about the pyramid axis. The pyramid is attached to body2. The first shaft attached to
body1 is constrained to always stay within the pyramid.
|
friction <value> | Specifies joint friction. The friction value is usually in the range [0, 1]. |
4.3 Hinge
A hinge is specified by an anchor and a hinge axis about which the two bodies are
allowed to rotate relative to each other.
anchor ( x, y, z ) | Specifies the anchor |
axis ( axis_x, axis_y, axis_z ) | Specifies the hinge axis |
limit angle1, angle2, angle3 | Specifies a V-shaped limit
The first angle specifies the center of the V-shaped limit. The angle which specifies
the width of the V-shaped limit follows. The V-shape is attached to body2. Next a shaft
is specified which is attached to body1 and is constrained to always stay within the
V-shape. The orientation of this shaft is specified with the third angle. |
friction <value> | Specifies joint friction. The friction value is usually in the range [0, 1]. |
4.4 Spring
A spring is specified by two anchors, one on each constrained body.
anchor1 ( x, y, z ) | Specifies the first anchor |
anchor2 ( x, y, z ) | Specifies the second anchor |
friction <value> | Specifies joint friction. The friction value is usually in the range [0, 1]. |
stretch <value> | Spring constant when the spring is stretched |
compress <value> | Spring constant when the spring is compressed |
damping <value> | Spring damping |
restLength <value> | Rest length of the spring |
minLength <value> | Minimum length of the spring |
maxLength <value> | Maximum length of the spring |
5. Additional script features
3D vectors are used to specify positions, directions and orientations of bodies and
constraints. There are four ways to specify such a vector in a .af file
( x, y, z ) | Position relative to the md5 origin |
joint( "joint name" ) | Position of a joint |
bonecenter( "start joint name", "end joint name" ) | Center of a bone between two joints |
bonedir( "start joint name", "end joint name" ) | Direction of a bone from one joint to another |
6. Optimizing articulated figures
- Minimize the number of bodies and constraints
- Use low complexity collision models
- Create one tree structure
- Don't use joint friction where possible, although joint friction might add stability
- Don't use self collision detection on bodies that don't need it
7. Problems
- The articulated figure behaves weird or explodes.
Make sure the mass of each body is within a reasonable range. Use af_showMass 1
to show the mass of each body. A body mass in the range [1, 100] usually works
well. Connecting a very heavy body with a lightweight body often causes problems.
It's usually also wise not to make the articulatd figure too heavy. The forces
required to keep heavy bodies together can grow quite large and might not be
represented accurately in the physics engine.
- The articulated figure behaves weird or explodes.
Make sure the inertia tensor of each body describes a reasonable balanced object.
Use af_showInertia 1 to show the inertia tensor of each body. Wildly different
diagonal elements of the inertia tensor may cause instability. The per body
inertia scale may be used to scale the inertia tensor of a body such that the
diagonal elements end up in a reasonable range relative to each other.
- The articulated figure is jumpy and energy seems to enter the system.
Make sure there are no contact points fighting with joint like constraints. When for
instance three bodies are connected with two universal joints, and the bodies are
relatively close to each other. The outer two bodies might be considered in contact.
The figure will be pulled apart at the contact points while the universal joints try
to keep the bodies together. Jumpy behaviour is often the result. Disabling self
collision on the whole figure or on certain bodies can fix the problem.
8. Ingame editing of articulated figures
Several cvars and console commands are available ingame to (re)load and visualize
articulated figures.
Once an entity def is created for an articulated figure, the 'spawn' console
command followed by the name of the entity def can be used to load the articulated
figure in the game. The 'reloadAFs' console command can be used to reload all
articulated figures. This command causes all articulated figures to snap back to
the pose specified by the initial animation.
To visualize several physical properties of the articulated figures the following
cvars can be used:
af_showBodies | Show the collision models of the bodies |
af_showBodyNames | Show the name of each body |
af_showMass | Show the mass of each body |
af_showTotalMass | Show the total mass of the articulated figure |
af_showInertia | Show the inertia tensor matrix of each body |
af_showActive | Show tree only if the articulated figures is not at rest |
af_showVelocity | Show a linear and angular velocity vector for each body |
af_showConstraints | Show all constraints |
af_showConstraintNames | Show the name of each constraint |
af_showConstrainedBodies | Show the two bodies constrained by the constraint |
af_showPrimaryOnly | Show primary constraints only |
af_showTrees | Show the tree structure(s) of the articulated figure |
af_showLimits | Show joint limits |
The skeleton of an md5 model can be visualized by setting the cvar 'r_showSkel' to 1.
To test articulated figures ingame it is useful to be able to pick them up and drag
them around. When the cvar g_dragEntity is set to 1, an articulated figures can be
selected and moved through the world. To select an articulated figure, the crosshair
is pointed at a position somewhere on the visual mesh, and the attack button is
pressed. While the attack button is pressed the articulated figure can be dragged
through the world by walking around (or flying in noclip mode) and looking around.
The last articulated figure selected for dragging is contained in a yellow bounding
box. This articulated figure can be removed from the game with the console command:
'deleteRagdoll'. Once articulated figures are positioned in a map they can be saved
to the .map file by using the console command: 'saveRagdolls'. Articulated figures
saved to a .map file are not deleted from the .map file with the 'deleteRagdoll'
command. Any articulated figure can ofcourse still be deleted from a .map file in
the level editor.
|