How to make a 3D tree model grow

I was given the problem of creating a shot for an animated short where a tree would growing up out of the ground. Not only just grow, but grow up in a way that an animator could control. Not having ever done this type of thing before, I started brainstorming solutions. What I eventually concluded was that the best way to go about this was to manipulate the joints of an underlying tree skeleton. But how?

An IK Spline Solver controls only the rotations of the joints it has in its influence. If you manipulate the translation value of a joint that is in direction of the main axis, you can make the joint travel along that curve path. If you do this for the whole joint chain, you make the whole chain "snake" up the curve. So, it should be possible to create expressions that would allow me to manipulate the translation axis value while having controls that move the IK curve that controls the rotations. But what kind of expression?


An IK Spline Solver with key framed translate Y
A little more research needed.

The best solutions for simulating natural phenomenon come from the study of nature. In this case we ask, "How does a tree grow?" When an animal grows, the whole creature mostly just gets bigger. Certain parts are grown into, but limbs get longer and wider in every direction. Plants don't work like that. Trees are more like buildings. As they get taller new tree material is added on to the ends of the tree. That's why you can nail a board to a tree trunk and as the tree gets bigger over say, 20 years the board will stay relatively stationary. The base gets wider, but it pretty much stops moving up after a certain point in its growth as the ends keep getting longer.

So, what do you we need to manipulate in our joint chain?

1. Translation.

Given a joint chain A, B, C. with corresponding lengths, 'a', 'b', 'c' where these are the final values of their lengths fully "grown".

Starting with A, B, C with 0 values for their lengths, first we want A joint to grow to 'a' length, then B joint to start growing to 'b' length, and finally C joint grows to 'c' length. We want to be able to control the "growth" of the joint chain so it fluidly extends from 0 to length 'a', to length 'a' + 'b', to finally length 'a'+'b'+'c'. I want to have a single attribute called "Grow" that has values 0 to 1 that will make this happen.

2. Scale.

As our joint chain grows, I also want the "thickness" to grow as well. When joint B has a length value of 0, then it's width should be 0 as well. Consequently, joint A should have a thickness that tapers and then increases as the other joints down the chain grow up.

3. Recursion.

Hello, Comp Sci background. It's a tree, I'm going to need to write a script that can traverse the tree and solve the problem recursively. For those of you who don't know what recursion is, basically, it is solving a big problem by breaking the whole into little versions of the problem that we solve for first and then work our way back. A tree is a popular vehicle for recursive problems because if you break off part of tree, it is, for all intents and purposes, just another tree. You keep snaping off pieces till we get to the single branch with no branching (a twig if you will), and that is your base problem.


A telescoping, or "growing" joint chain
The Solution

1. Storing information.

The solution to this problem relies on joint's self-awareness. As the script traverses the tree structure key bits of information are stored as extra attributes on each joint. All child joints get a "Final" and a "Summation" attribute. "Final" is the value of the translation axis (in this case Translate Y) when the tree is fully grown. "Summation" is the sum of the "Final" values of the joints that are up the chain to the root of the branch. Using our example, for joint C, "Final" is equal to 'c', and "Summation" is equal to the sum of 'a' + 'b'.

Branching root joints are the special cases. They receive the attributes "Branch Length", "Progress", and "Grow". "Branch Length" is the sum of all the "Final" values of the joints in this branch. "Progress" is a calculated attribute that is equal to the product of "Grow" and "Branch Length", since "Grow" will be a value from 0 to 1.

2. Translation expressions.

    A.Progress = A.Grow * A.BranchLength;

The main translation expressions are then pretty simple.

    B.translateY = clamp(0, B.Final, A.Progress - B.Summation);
    C.translateY = clamp(0, C.Final, A.Progress - C.Summation);

Essentially, A.Progress increase as A.Grow increases from 0 to 1. As A.Progress increases each joint down the chain subtracts its "Summation" value from the overall progress getting a negative number until the Progress is greater than its "Summation" value. The clamp function disregards values less than 0 and more than the joints "Final" value.

3. Tapering Growth.

The last big hurdle in solving this problem revolves around how to handle the width of the joints as they are "growing". Intuitively, we want the joint to start with a width of 0 when its translation is 0, and then grow to 1 as the translation reaches "Final" and the overall "Progress" of the joint chain. I want to have the widths taper down to the new growth zone. However, as "Grow" approaches 1, I want that tapering to diminsh till everything is its final width when "Grow" is equal to 1.

    B.scaleX = clamp(0, 1,(exp(10*A.Grow)/20000) + (-1 * pow((B.Summation+B.translateY)/A.Progress,3) + 1) - (1 - A.Grow));

Basically, I ended up with 3 functions that interact with each other. The pow function portion looks like this when graphed:


-x3 + 1

It is the main tapering function. The ("Summation" + "TranslateY") / "Progress" part returns a value between 0 and 1 depending on the position of the joint in the chain, i.e. the closer the joint is to the growth zone the closer to 0 the pow function returns.


e(10 * x)/20,000

The exponential function counteracts the pow() function by fading out the influence of the pow() function the closer to 1 the "Grow" property gets.


1 - x

Finally, the linear function (1 - A.Grow) simply adds an overall influence to the width as "Grow" approaches 1. I subtract the remainder of the "Grow" property to the whole chain beyond that of the tapering effect.

Putting it all together.
Developing the script began with writing a barebones script that would recursively print the values of the translations of each joint. Once I had that I began to add in code to add the additional properties and populating them with the summation and final values. Because this solution used a recursive function, it was necessary for me to use some global variables to hold information like the joint's root name, and a variable to hold the sum for calculating the branch lengths.

Using the script. Select the root joint. Source the script, and then run the function "TraverseTree". Let it do it's thing. This version relies on a specific joint architecture, namely that the main axis is set "Y", and that branching uses a double joint with 0 translations. If you open the maya file you'll see what I mean. The entire skeleton is defined only by the "Y" translation. You can add an IK spline to the skeleton and it will work just fine.


rigging is fun!
Download sample scene file: (shown above) TreeSkeleton.ma (maya file v 7.0)

Download mel script: GrowTree.mel

Final Output

Since the solution I developed is still part of a production that has not yet completed I can not show a final render with the model I used. However, I will try to model a simple tree very soon, so you can see what it looks like. For the shot I had to break some of the "Grow" expressions because, although I have an expression that starts "Grow" properties down the tree based on the root "Grow", I didn't like exactly when they happened and used a set driven key for them instead. Because of the complexity of the character rig, I had to construct two versions of the tree rig: one as a character rig with more robust animation controls; and, one as the "Growth" rig.