Tornado Tutorial

One create a surface to emit particles from using a nurbs surface in the shape of a tornado funnel. Animate this so to achieve the 'twisting' effect.

Create a particle emitter using the funnel object. Make sure to check the Use Parent UV option.

Select the particle and then the funnel and select -> particle-> goal ?. Set the goal weight to 1.0.

Next add goalU and goalV PP attributes to the tornado particle shape (TC). These are found under the Particle tab of the Add Attribute dialog.

Add two lines to the creation expression setting the goal U/V's to the parent objects U/V's

goalU = parentU;
goalV = parentV;

also add an line for the ;
lifespanPP = rand(0.1,0.5);

Now the particles will stick to the funnel as the funnel moves. However, we want to offset the particles from the funnel.

Add the float attribute dustDepth and a couple of lines to the creation expression;
float $xOffset = gauss(dustDepth);
float $zOffset = gauss(dustDepth);

This will cause the particles to be distributed about the surface of the tornado funnel. A value of 0 will cause the particles to stick to the funnel while a greater number will cause them to be distributed away form the surface. The gauss function returns a random value distributed around the mean value (the value supplied). The shape of this distribution forms a bell curve, with the greatest number of values clustering around the mean value and then the number falling off away from the mean.

Add the PP attribute goalOffset then add this line to our creation exp.:
goalOffset = <<$xOffset,0,$zOffset>>;

We'll deal with the Y value in a bit.

To get the particles to spin around the funnel we need to create another PP attribute and add a line to our runtime expression and creation expressions;

Create a PP attribute called goalUacc. In our creation exp. define the value for goalUacc:
goalUacc = rand(0.1,0.5);
Then in our runtime exp. add the line goalU += goalUacc;
Note that by using += the particles rotate in the anti-clockwise direction.

To enable us to control the amount of spin without editing the expression we can add another multiplier to the last line using another attribute value. Create a float attribute (non PP) called dustSpin. This will appear in the channel window to enable easy access to the spin amount. Then modify the last line to read:
goalU += goalUacc*dustSpin;

The particles now spin with a uniform speed top to bottom. To add more detail we will create some attributes to control the rate of spin and rise at different heights.

To do this we need to add two new PP attributes, VInc and UInc and add an attribute dustRise to the particle shape (TC). Also add these float attributes to the Particle object Tornado (not the Particle shape TC), giving them a min / max values of 0.0 and 1.0: UpSpeed0, UpSpeed1, UpSpeed2, RoundSpeed0, RoundSpeed1, RoundSpeed2, Postion0, Postion1 and Position2.

You can add more controls if you wish, but we will use these three levels in this example.
In our runtime exp. add the lines;
Vector $pos = position;
Float $pos0 = Position0*100;
Float $pos1 = Position1*100;
Float $pos2 = Position2*100;

If ($pos.y < $pos1)
{
VInc = RoundSpeed0 + ((RoundSpeed1-RoundSpeed0)*(linstep($pos0,$pos1,$pos.y)));
UInc = UpSpeed0 + ((UpSpeed1-UpSpeed0)*(linstep($pos0,$pos1,$pos.y)));
}
Else if ($pos.y >= $pos1)
{
VInc = RoundSpeed1 + ((RoundSpeed2-RoundSpeed1)*(linstep($pos1,$pos2,$pos.y)));
UInc = UpSpeed1 + ((UpSpeed2-UpSpeed1)*(linstep($pos1,$pos2,$pos.y)));
}

TC.goalV += TC.goalUacc*TC.dustSpin*TC.VInc;
TC.goalU += TC.dustRise*TC.Uinc;

As we may want to create different height tornado clouds, but don't want to adjust the Position[n] attributes we set the ranges for these attributes to be between 0 and 1 and then multiply them by the height of the tornado to give the position (in this case 100).

The lines in the if/else function control the rate of spin / rise depending on the particles position between the control points. As the particle moves between the control points it's spin/rise rate is adjust so that at the level of the control point it matches.

NOTE: if UpSpeed1 is greater than UpSpeed0 it will cause the particles to rise at a greater rate than UpSpeed0 from position0.

Now add a line to kill of the particles as they reach the top of the funnel
If ($pos.y >= 100)
LifespanPP = 0;