Helicoradian (Matrix)

I was amazed by the design of the plant species in the movie "Avatar" one of my favorite which is "Helicoradian". These plants are simple, yet attractive and fascinating. In this page it has been tried to explain about the methods I used to create these plants in Maya using MEL scripting.

First Things First! References:



As usual, the first step is gathering references. I found some good images from the movie. I needed to study the properties of the plants and behavior of their curves very carefully.

I found out that the surface of these objects is basically created out of a curve revolving around its axis. The main parameters that I figured out for this object are:

Number of Turns
Number of Curves
Base and Final radius
Base and Total Height
Curvature
Amount of Vertical and Horizontal Jaggedness


There are some additional parameters which can give more freedom to the user like and create more variety of plants:

Vertical and Radial Bias
Clockwise and Counterclockwise
Amount of Noise
Noise Scale.

Hell! It's all 'bout Math!

The process starts with a simple curve created on the center with 6 points. The size of the curve in all directions is important; we will use it later to change the base height, base radius and curvature. This curve is resampled to get a nice and even curve. Then we need to find out the amount of rotation increment for each curve around its Y axis. We have to keep in mind that the curvature can be clockwise or counterclockwise. The math is very simple here:

Rotation increment =clockwise* (number of turns * 360) / total number of curves

The second factor after rotation is the scale of the curves. To create such object, the curve reduce scale in XZ direction while increase scale in Y direction. The math is easy again until “Radial Bias” and “Vertical Bias” are introduced.

Bias functions are exponential functions which the tendency of their curve depends on their power. Therefore Radial Bias means how much curvature we have in the beginning or the end of the curve: If bias is zero, we have a linear increment on the rotation, if bias is more than zero the curve tends to have more turns toward the end and vice-versa. It is also correct for Vertical Bias. The problem is that MEL does not accept negative numbers as power sometimes for some reason. Therefore I need to define the Bias conditionally.

After considering the main scaling, we need to add jaggedness to the curves which is also forced to the scaling. To add jaggedness I used integer powers of “-1” because it constantly change direction. At the end I needed to add some noisiness to the plant so that it looks more natural. This was done by using noise function.

At the end all these curves are connected to each other and create a loft NURBS surface and all of them are deleted in order to clean up the scene.


Some Results: (click on images to enlarge)






...And the Code:
		
	/*
	                Helicoradian
	                Version 3.1
	                by Ali Seiffouri
	                1-16-2010
	                (code in MEL)
	*/
	  
	{    //    local scope
	  
	//    parameters-----------------------------------------------------
	int      $numberOfCurves = 200;		//    min 2 curves
	float    $baseRadius = 5;    		//    not zero or negative
	float    $finalRadius = 0.1;		//    min 0
	float    $turns = 4;			//    min 0
	float    $biasVertical = -100;   	//    min -100 ~ max 100 (better results)
	float    $biasRadial = 80;      	//    min -100 ~ max 100 (better results)
	float    $baseHeight = 3;        	//    not zero or negative
	float    $height = 10 ;          	//    greater than $baseHeight
	float    $zigzag = 0.01 ;        	//    can be negative
	float    $jagged = 0 ;          	//    can be negative
	float    $noise = 1;            	//    can be negative
	float    $noiseScale = 5;       	//    not zero
	int      $noiseSeed = 12346;    	//    any integer
	float    $curvature = .7 ;   		//    min -1 ~ max 1 (better results)
	int      $clockWise = 0;        	//    boolean 0 or 1
	  
	  
	//    initialization-------------------------------------------------
	string  	$curves[];
	int     	$count = 0;
	float  	$amountFloat = $numberOfCurves;
	  
	  
	//    main $i loop---------------------------------------------------
	for($i = 0; $i < $numberOfCurves ; $i++) 
	{
	    //    creating curve and initial curvature    
	    $myCurve = `curve    -d 3 
	                        -p 0 0 0 -p 0 0.75 0 
	                        -p 1 2 0 -p 2.7 2.7 -0.35 
	                        -p 4 3 -1.35 -p 4.25 3 -2.9`;
	                
	    rebuildCurve -rt 0 -s 10 $myCurve;
	    select -r $myCurve;                
	    scale ($baseRadius / 4.25) ($baseHeight / 3) $curvature;    
	  
	    
	    //    creating an array from the curves
	    $curves[$count] = $myCurve;
	    $count++;
	  
	    
	    //    difinging vertical and radial bias
	    float    $biasVerticalRemap;
	    float    $biasRadialRemap;
	    
	    if ($biasVertical <= 0)
	    {    
	        $biasVerticalRemap = pow ( (($i+1) / $amountFloat) , (abs($biasVertical / 100)) );
	    }
	    else
	    {
	        $biasVerticalRemap = 1 / pow ( (($i+1) / $amountFloat) , (abs($biasVertical / 100)) );
	    }
	    
	    if ($biasRadial <= 0)
	    {    
	        $biasRadialRemap = pow ( (($i+1) / $amountFloat) , (abs($biasRadial)) );
	    }
	    else
	    {
	        $biasRadialRemap = 1 / pow ( (($i+1) / $amountFloat) , (abs($biasRadial / 100)) );
	    }
	     
	    
	    //    defining jaggedness of the edges
	    float    $upDown = pow (-1 , $i) * $zigzag * (1 - (1 / $amountFloat * $i));
	    float    $jaggedHorizontal = pow (-1 , $i) * ($jagged / 10) * (1 -(1 / $amountFloat * $i));
	  
	  
	    //    defining the amount of scaling for each curve
	    float    $scaleIncrementH = ($height - $baseHeight) / ($baseHeight * $amountFloat);
	    float    $scaleIncrementR = ($baseRadius - $finalRadius) / ($baseRadius * $amountFloat);    
	    
	    float    $scaleHeight = 1 + ($scaleIncrementH * $i * $biasVerticalRemap) + $upDown;
	    float    $scaleRadius = 1 - ($scaleIncrementR * $i * $biasRadialRemap) - $jaggedHorizontal;
	  
	    
	    //    scaling the curves
	    scale -r $scaleRadius $scaleHeight $scaleRadius;
	  
	    
	    //    rotating each curve 
	    float    $myRot = (pow (-1 , $clockWise)) * (360 * $turns * $i / $amountFloat); 
	    rotate 0 $myRot 0 ;
	  
	    
	    //    adding zigzag
	    move 0 $upDown 0 ;
	  
	  
	    //    adding noise
	    float    $noiseX = $noise / 100 * (noise ($i/$noiseScale + $noiseSeed + 100)-0.5)
	                        * (1 -(1 / $amountFloat * $i));
	    float    $noiseY = $noise / 100 * (noise ($i/$noiseScale + $noiseSeed + 200)-0.5)
	                        * (1 -(1 / $amountFloat * $i));
	    float    $noiseZ = $noise / 100 * (noise ($i/$noiseScale + $noiseSeed + 300)-0.5)
	                        *  (1 -(1 / $amountFloat * $i));
	                        
	    scale -r (1+$noiseX) (1+$noiseY) (1+$noiseZ);
	  
	}    //    end $i loop
	    
	    
	//    creating a loft surface from the curves------------------------
	$myLoft = `loft -n "plant_0" -ch true -rn true -ar true $curves` ;
	  
	  
	//    cleaning up the scene------------------------------------------
	$curveGroup = `group $curves` ;
	select -r $curveGroup;
	delete ;
	  
	} //    end local scope
	
	

Final Thoughts

Scripting is talking to software directly while working with the interface is similar to communicating with it with sign language! Writing codes gives the abiliy to create things that if not impossible, is really difficult and time consuming to create. Also creating lots of random objects is one of the most demanded tasks in visual effects which if done manually, it is almost impossible.
Usually in serious production environments, clients ask for a lot of changes very often and they need it fast. Therefore working procedurally (which mainly requires writing codes) saves a lot of time and budget.
I personally enjoy coding a lot as it can translate the language of nature, "Mathematics" to machine codes.