A procedural dragonfly rig

In this lesson we will be creating a procedural setup that will allow us to ease the animation of this dragonfly. This tutorial will explore procedural walk-cycles and conditional animation cycles. The model used in this rig is a hybrid from several free models from 3D Cafe but you can download this model here

A Procedural Walk Cycle

The first thing we would like to achieve with our model is that when its body moves forward in the Z direction, we would like to have each of its feet walking much the same way that an insect does.

Start Simple

Open the scene DragonFly_Simple.mb and note that a simple joint hierarchy has been set up for the Left Middle leg, with a SC IK handle and all finished off with a curve controller for the foot.

To develop our walk cycle we first need to understand what a walk cycle consists of. Standard keyframed walk cycles consist of;

1) Contact pose
2) Down extreme
3) Passing pose
4) Up extreme
5) Another contact.

To recreate this procedurally we need to first consider the behaviour of the translate X, Y & Z of the foot controllers.

The foot's translation in X, Y & Z remains constant from the point of contact up until the moment when the opposing foot contacts the ground. This means that for half of the cycle there is;

1) TranslateY = 0;
2) Translate X & Translate Z are unchanging. (ie the foot stays put)

For the other half of the cycle when the opposing foot is planted, the foot will be raising and advancing forward. That is to say;

1) TranslateY: will be increasing then decreasing (like a projectile)
2) Translate X & Translate Z will be increasing constantly to simplify things
(essentially this means the foot moves)

Step 1: Translate Y

Start by creating an expression for translateY for one of the feet to make it raise up and down like a projectile (a sine curve does a good job of that).

The problem we are faced with is that normally a sine wave is driven by an angular value. We can see from the graph that Y=0 when X=0, 180 & 360 degrees.

So if we would like each footstep to be 1 unit long, each unit step in Z would have to correspond to 180 degrees, ie;

180 degrees = 1 unit in Z

Which gives the expression;

LM_Foot_CON.translateY = sind(180*Thorax.translateZ);

The problem with this approach is that the foot continues through the floor for the second half of the cycle. To prevent this we can use a MEL Maximum command.

LM_Foot_CON.translateY = max(0,sind(180*Thorax.translateZ));

Now for the other foot. We want this foot to reflect the actions of the first foot. We can do this by inverting the function already applied to the left foot.

RM_Foot_CON.translateY = max(0,-sind(180*Thorax.translateZ));

Translate Z

As you can see now, the feet lift off the ground at the correct times in the cycle. The only problem is that the feet slide backwards and forwards. Currently the feet move with the Thorax so their TranslateZ equals that of the thorax. Instead we would prefer the situation illustrated in the figure to the right. As the feet are parented to the thorax we are dealing with a case of relativity. So to determine the correct expression for the feet we just need to understand that;

Desired global movement of feet =
movement of thorax +
feet movement relative to the thorax

So the required adjustment will be that of the desired footsteps minus the movement of the thorax.

This curve looks just like a sawtooth curve and can be recreated using a modulus function. The modulus function (written as % in MEL) is shown below. The example shows X with modulus 2.

by shifting this modulus function in Y by half its amplitude and in Z by 1 unit, then taking its absolute value, we can obtain an expression that equates to the required adjustment curve

LM_Foot_CON.translateZ = abs((abs((Thorax.translateZ-1))%2)-1);

To get the correct expression for the other foot we just simply invert the expression.

RM_Foot_CON.translateZ = 1-abs((abs((Thorax.translateZ-1))%2)-1);

Now that we have this working for two legs the next task is to get it working for six...

Open up the original dragonfly file and copy the following expressions onto the feet.

LF_CON.translateY = max(0,sind(180*Thorax.translateZ));
LM_CON.translateY = max(0,sind(180*Thorax.translateZ));
LB_CON.translateY = max(0,sind(180*Thorax.translateZ));
RF_CON.translateY = max(0,-sind(180*Thorax.translateZ));
RM_CON.translateY = max(0,-sind(180*Thorax.translateZ));
RB_CON.translateY = max(0,-sind(180*Thorax.translateZ));

and

LF_CON.translateZ = abs((abs((Thorax.translateZ-1))%2)-1);
LM_CON.translateZ = abs((abs((Thorax.translateZ-1))%2)-1);
LB_CON.translateZ = abs((abs((Thorax.translateZ-1))%2)-1);
RF_CON.translateZ = 1-abs((abs((Thorax.translateZ-1))%2)-1);
RM_CON.translateZ = 1-abs((abs((Thorax.translateZ-1))%2)-1);
RB_CON.translateZ = 1-abs((abs((Thorax.translateZ-1))%2)-1);

which essentially replicates what we discoverd above for all of the feet.

The insect now walks, all feet touch the ground in the correct manner. All we need to do now is add some character that will make it look more like an insect's walk. For starters the insect's walk needs to have each foot touching the ground at different times.

The easiest way from here is just to offset the front feet forward by half a unit in Z and backwards half a unit for the back feet;

LF_CON.translateY = max(0,sind(180*(Thorax.translateZ+0.5)));
LM_CON.translateY = max(0,sind(180*Thorax.translateZ));
LB_CON.translateY = max(0,sind(180*(Thorax.translateZ-0.5)));
RF_CON.translateY = max(0,-sind(180*(Thorax.translateZ+0.5)));
RM_CON.translateY = max(0,-sind(180*Thorax.translateZ));
RB_CON.translateY = max(0,-sind(180*(Thorax.translateZ-0.5)));
LF_CON.translateZ = abs((abs((Thorax.translateZ-0.5))%2)-1); ....... one half minus one
LM_CON.translateZ = abs((abs((Thorax.translateZ-1))%2)-1);
LB_CON.translateZ = abs((abs((Thorax.translateZ-1.5))%2)-1);
RF_CON.translateZ = 1-abs((abs((Thorax.translateZ-0.5))%2)-1);
RM_CON.translateZ = 1-abs((abs((Thorax.translateZ-1))%2)-1);
RB_CON.translateZ = 1-abs((abs((Thorax.translateZ-1.5))%2)-1);

And there we have it... our walking insect. This probably won't be perfect, but those who want to spend the time working out exactly the right amount of delay... good luck to you!

Making it fly

Now we can write an expression for the wings

L_Wing_Shoulder.rotateZ = 45*sind(1000*Thorax.translateZ);
R_Wing_Shoulder.rotateZ = 45*sind(1000*Thorax.translateZ);

Fly or Walk?

The last part we would like to achieve is to have a conditional switch that stops the dragonfly walking when it gets higher than say 1 unit off the ground.

To create a conditional expression we can use either the if...then...else notation or alternatively we can use the (condition)? <if true> : <if false> notation, which is much more compact. For example we can change the above expression for the fly to;

L_Wing_Shoulder.rotateZ = (Thorax.translateY>1)?45*sind(1000*Thorax.translateZ):0;
R_Wing_Shoulder.rotateZ = (Thorax.translateY>1)?45*sind(1000*Thorax.translateZ):0;

Similarly we can do the same for the walk.

LF_CON.translateY = (Thorax.translateY>1)?0:(max(0,sind(180*(Thorax.translateZ+0.5))));
LM_CON.translateY = (Thorax.translateY>1)?0:(max(0,sind(180*Thorax.translateZ)));
LB_CON.translateY = (Thorax.translateY>1)?0:(max(0,sind(180*(Thorax.translateZ-0.5))));
RF_CON.translateY = (Thorax.translateY>1)?0:(max(0,-sind(180*(Thorax.translateZ+0.5))));
RM_CON.translateY = (Thorax.translateY>1)?0:(max(0,-sind(180*Thorax.translateZ)));
RB_CON.translateY = (Thorax.translateY>1)?0:(max(0,-sind(180*(Thorax.translateZ-0.5))));

LF_CON.translateZ = (Thorax.translateY>1)?(-5):(abs((abs((Thorax.translateZ-0.5))%2)-1));
LM_CON.translateZ = (Thorax.translateY>1)?(-3):(abs((abs((Thorax.translateZ-1))%2)-1));
LB_CON.translateZ = (Thorax.translateY>1)?0:(abs((abs((Thorax.translateZ-1.5))%2)-1));
RF_CON.translateZ = (Thorax.translateY>1)?(-5):(1-abs((abs((Thorax.translateZ-0.5))%2)-1));
RM_CON.translateZ = (Thorax.translateY>1)?(-3):(1-abs((abs((Thorax.translateZ-1))%2)-1));
RB_CON.translateZ = (Thorax.translateY>1)?0:(1-abs((abs((Thorax.translateZ-1.5))%2)-1));

And in all its messy glory that is about it.