Blog

How to Create Ragdolls

Aug 11

Written by:
Thursday, August 11, 2011  RssIcon

Manually creating good ragdolls in code can be tough. This blog post takes you on a hands-on ragdoll creation journey and provides several tips.

Sooner or later we will have a graphical ragdoll editor. But we are not there yet – and creating a ragdoll manually in code is a good exercise anyway ;-)

Get Familiar

Check out the documentation of DigitalRune Animation and DigitalRune Physics, and have a look at the included sample applications. To create a ragdoll, you need a solid understanding of skeletal animation and game physics.

Here is a video of the dude ragdoll included in the CharacterAnimationSample. The debug rendering of constraints is from the last blog post: Visualization of Ragdoll Joints. (This will be included in future releases of the DigitalRune Engine.)

I recommend to use the CharacterAnimationSample to create and test a ragdoll. The sample already has a working ragdoll. It also draws the rigid bodies, and you can shoot balls or grab the ragdoll. – Good starting conditions.

Rigid Bodies

Start with CollisionDetectionOnlyRagdollSample.cs from the CharacterAnimationSample. In this sample, a ragdoll is created for collision detection. Joints, limits, or motors are not used.

As a first step we only define the rigid bodies and the offsets (see properties Ragdoll.Bodies and Ragdoll.BodyOffsets):

First, replace the dude model with your own model. Comment out the dude-ragdoll specific code until you can run the sample.

Then add the debug rendering of the skeleton from the BindPoseSample.cs.  Run the sample and get familiar with the bone structure of the model. (While the sample is running, you can press <Space> to see the model in its bind pose.)

The dude ragdoll is created in DudeRagdollCreator.cs. I suggest that you also encapsulate the creation of your ragdoll in a separate class (or replace the code in this class).

Next, define the rigid bodies and the body offsets (similar to the code in the DudeRagdollCreator). Run the sample to control the shapes and the offsets of the rigid bodies. (Choosing the right mass is discussed below.)

Tweak the rigid bodies and offsets until you are satisfied with the result. At the end of this step you should have defined all rigid bodies of the ragdoll and they should move correctly with the animated model.

Rigid Body Mass

So far, the rigid body mass properties are not relevant – because the ragdoll is not yet used in a dynamic simulation. The mass will take effect when we use the ragdoll in the PassiveRagdollSample (see section “Joints”).

Choosing the right mass properties is important because: Game physics engines have difficulties with large mass differences. Ideally, the mass of the heaviest dynamic object in a game is no more than 10 times the mass of the lightest dynamic object in the game.

If we do not explicitly specify the mass of a rigid body, then the physics engine computes the mass properties automatically based on the shape. This is not optimal: A small box for a hand will be much lighter than a box from the ragdoll torso.

And game physics engines also have troubles with long thin objects, like capsules. Capsules have a low rotational inertia about the capsule axis and a much higher rotational inertia about all axes normal to the capsule axis. (Rotational inertia is the analog of mass for rotational movement.) This is again a problem of mass differences.

To start with mass properties that are better for the simulation, I suggest to treat each body of the ragdoll as if they are all spheres of equal size. The DudeRagdollCreator uses following code:

float totalMass = 80; 
int numberOfBodies = 17; int head = skeleton.GetIndex("Head"); int footLeft = skeleton.GetIndex("L_Ankle1"); var headPosition = skeletonPose.GetBonePoseAbsolute(head).Translation; var footPosition = skeletonPose.GetBonePoseAbsolute(footLeft).Translation; var headToFootDistance = (headPosition - footPosition).Length; var massFrame = MassFrame.FromShapeAndMass(new SphereShape(headToFootDistance / 8),
Vector3F.One, totalMass / numberOfBodies, 0.1f, 1);

The height of the model is estimated using the distance between the head and the ankle. Then a MassFrame is computed that is used for each body in the ragdoll. The total mass of 80 kg is evenly distributed over all bodies.

This is not realistic, but this trick helps to prevent problems caused by large mass ratios. Later we can still tweak the masses and try more realistic values.

Velocity Motors

For the next step, use the KinematicRagdollSample.cs. Replace the dude model/ragdoll with your own model/ragdoll and start the sample. The bodies should be in place, but they are not moving because the ragdoll motors are missing. The CollisionDetectionOnlyRagdollSample moves the bodies manually in each frame, whereas the KinematicRagdollSample uses motors to move the bodies.

Note: When a ragdoll should interact with other rigid bodies, it is important to use motors. Motors move the rigid bodies by setting a velocity and let the physics simulation compute the movement.

Adding motors is easy, as you can see in DudeRagdollCreator.cs.

After you have added motors, run the sample. The rigid bodies should now move together with the animated model.

Joints

Now, switch to PassiveRagdollSample.cs. Replace the dude model/ragdoll with your own model/ragdoll and start the sample: The ragdoll will fall apart because the bodies are not connect.

In this step, the joints will be added. First, set the MotionType of all rigid bodies to Kinematic. For example:

foreach (var body in _ragdoll.Bodies)   
if (body != null
)    
body.MotionType =
MotionType.Kinematic;

Also disable all ragdoll motors.

Run the sample: All bodies will stay in place.

Next, make the hand body Dynamic again. For example:

_ragdoll.Bodies[skeleton.GetIndex("L_Hand")].MotionType = MotionType.Dynamic;

Run the sample again: The hand body will fall to the floor.

Add a BallJoint between the lower arm and the hand (as in the DudeRagdollCreator.cs).

Run the sample again: Now, the hand is connected to the lower arm. While the sample is running, you can grab the hand body or shoot a ball at the hand body to test the joint. The hand rotation is not limited yet – we will take care of that later.

After that, add joints for all other bodies. Make the lower arm body dynamic, add a joint between lower arm and upper arm and test it. And so on. Work from the extremities (hand, feet, head) to the pelvis.

Finally, set all bodies to dynamic again and test the ragdoll. The limbs will rotate like crazy, but otherwise the ragdoll should be stable before you start to add joint limits. (Stable means that the ragdoll does not jitter or shake a lot and all bodies come to rest when the ragdoll lies on the floor.)

Additional Collision Filtering

In this phase of your ragdoll, instability could be caused by bodies that constantly collide with other bodies.

When two limbs are connected by a BallJoint, the collisions between the connected bodies are disabled (if BallJoint.CollisionEnabled is set to false). But often you need to disable additional collisions between limbs that are too close to each other. For example: In the dude ragdoll, the head is too close to the upper back body, the arms are too close to the torso and the upper legs are too close to each other. Therefore, collisions between these body pairs should be disabled in the CollisionFilter of the Simulation:

var filter = (CollisionFilter)simulation.CollisionDomain.CollisionDetection.CollisionFilter;
filter.Set(ragdoll.Bodies[backUpper].CollisionObject, ragdoll.Bodies[head].CollisionObject, false);
filter.Set(ragdoll.Bodies[armUpperRight].CollisionObject, ragdoll.Bodies[backLower].CollisionObject, false);
filter.Set(ragdoll.Bodies[armUpperLeft].CollisionObject, ragdoll.Bodies[backLower].CollisionObject, false);
filter.Set(ragdoll.Bodies[legUpperLeft].CollisionObject, ragdoll.Bodies[legUpperRight].CollisionObject, false);

In general: If two bodies penetrate each other during normal animations or in the bind pose, then it is best to disable collision between them.

Constraint Softness and Error Reduction

Constraints have two parameter called ErrorReduction and Softness. Choosing the right values for these properties must be done by experimentation: Tweak the values BallJoint.ErrorReduction and BallJoint.Softness and see what happens.

If you set the error reduction to 0, then the joints do not recover from errors. If you set the error reduction to 1, the error correction is very aggressive and can cause instability. Values like 0.2 - 0.4 are usually fine.

If you set the softness to 0, the ragdoll can become unstable because the joints are hard constraints that do not allow any constraint violation. If you set a higher value like 0.1, the joints become very soft and act like rubber bands. Use a small positive value for the softness, e.g. 0.0001.

Constraint Motors

After the joints are added, enable the motors again:

foreach (RagdollMotor motor in _ragdoll.Motors)
{   
if (motor != null
)  
{    
motor.Mode =
RagdollMotorMode.Constraint;    
motor.ConstraintDamping = 5;    
motor.ConstraintSpring = 0;  
} } _ragdoll.EnableMotors();

This time we use “constraint motors” instead of “velocity motors”. This type of motor adds a damping to the joint movement.

Next, try to set higher damping and spring values, like ConstraintSpring = 1000 and ConstraintDamping = 100. After the ragdoll was added to the simulation with Ragdoll.AddToSimulation, call

_ragdoll.DriveToPose(_skeletonPose, 0);

 

once to set the target orientation of the motors. Now, you should have a springy ragdoll that tries to stay in its bind pose.

Limits

Okay, we had a lot of fun up till now. But now we are going to add joint limits and this is serious business!

Defining joint limits without a graphical editor is a bit annoying, so lets concentrate:

Disable the motors again. Make all bodies kinematic again – except one hand. Add the debug visualization of the skeleton (from the BindPoseSample.cs) and add the constraint limit visualization of the last blog post.

Like in the DudeRagdollCreator class, add a TwistSwingLimit between the hand and the lower arm. (Use AngularLimits if you need hinge joints.)

Choosing the correct constraint anchor orientation is, of course, the most difficult part. You need to understand what the constraint anchor orientation means. Therefore, take a good look at the general chapter about constraints and the TwistSwingLimit and AngularLimit descriptions in the DigitalRune Physics documentation.

The debug rendering of the bones (and bone coordinate spaces) and the constraints should help you on your way. Choosing the correct constraint anchor orientation matrices might require some experimentation and a lot of patience.

It helps a lot to make only those bodies dynamic that you are currently testing. Another tip is to set the minimum and maximum angles of a limit to 0. This will show the relative limb orientation at which the constraint angle is 0.

Add one limit after the other. Test each limit and only add the next limit if you are satisfied with the current limits, and only if the ragdoll is stable.

Also experiment with the softness and error reduction of the limits. Giving limits a higher softness is ok because it makes the ragdoll appear more natural. (Joint limits in real humans are also soft.)

At the end make all bodies dynamic again and test your final ragdoll.

Conclusion

Since I have created my first ragdoll about 6 years ago, ragdolls have always been the most entertaining thing built with game physics. I think the steps above describe a good way to create a ragdoll in code. After you have created your first ragdoll it gets easier and a lot faster. With some experience you can start to experiment with different mass properties, error reduction/softness parameters, different constraint types for joint limits, automate parts of the ragdoll creation and, maybe, create your own ragdoll editor.

5 comment(s) so far...


Gravatar

Re: How to Create Ragdolls

Nice and informative blogpost.

I nearly finished my ragdoll editor and thanks to your debug visualisations (physic bodies, bones and limits) the response to value adjustments is very fast and looks good.

In my game engines winforms editor a ragdoll template can be created out of bodies (organised in a treeview = joint are build with the tree hirarchy), Limits and Body collisions. These templates can be assigned to NPCs.
I then use the gameui library for tweaking the various parameters of the bodies and limits until the ragdoll fits the model.
Works really nice this way ... ;-)
May I ask for one more little enhancement for the debug visuals? Please make an overload which excepts a limit or bone (index) that is heighlighted or the only thing drawn by the debug renderer. If it's available in source code like the new limit visuals then I can do this myself ... but I think there is no sourcecode for the bone drawer available ... (?)
For the bone renderer it would be good to be able to suppress the bone names ...

But anyway ... thats just a small add on ... it looks very cool so far ... keep up the good work!

By Laurentius on   Thursday, August 11, 2011
Gravatar

Re: How to Create Ragdolls

I will post a code snippet with the bone debug rendering in the next blog article.

By HelmutG on   Friday, August 12, 2011
Gravatar

Re: How to Create Ragdolls

Great!
I managed to extend the rigidbody render and the limits renderer to accept a body/limit currently in "designmode" to be highlighted. The ability to configure the bone renderer as well will be a nice addition. Thanks!

By Laurentius on   Friday, August 12, 2011
Gravatar

Re: How to Create Ragdolls

what program did you used

By diegotorres1233 on   Tuesday, June 26, 2012
Gravatar

Re: How to Create Ragdolls

@diegotorres1233: ?

By MartinG on   Wednesday, June 27, 2012

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 

Article Collection

A collection of the most useful blog articles can be found here:

Article Collection
(on Documentation page
)