Making DOOM 3 Mods : Articulated Figures

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/1The 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/1Set 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, angle3Specifies 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
  1. 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.

  2. 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.

  3. 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_showBodiesShow the collision models of the bodies
af_showBodyNamesShow the name of each body
af_showMassShow the mass of each body
af_showTotalMassShow the total mass of the articulated figure
af_showInertiaShow the inertia tensor matrix of each body
af_showActiveShow tree only if the articulated figures is not at rest
af_showVelocityShow a linear and angular velocity vector for each body
af_showConstraintsShow all constraints
af_showConstraintNamesShow the name of each constraint
af_showConstrainedBodiesShow the two bodies constrained by the constraint
af_showPrimaryOnlyShow primary constraints only
af_showTreesShow the tree structure(s) of the articulated figure
af_showLimitsShow 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.

Copyright © 2004 id software