MEL Scripting: Serenoa repens


 

An continuation of Maya Embedded Language (MEL) through learning how to set up several procedures to generate palm branch shapes. This more advanced process will involve generating end points for leaves, subdividing the vector between the origin and each point, adding environmental factors such as wind and gravity, and once again creating a UI to control these elements. Extra challenges include the ability to scatter these palms randomly across a surface, the ability to cluster groups of them to create "plants" learning MTOR commands to attach a curve width to the curves so they render in PRman, and learning basic SLIM scripting to implement shaders and assign them to the branches as they are created...........



Our first task was to make a series of visual studies of the palmetto plant's structure in order to have a better understanding of how it could be procedurally generated in MEL. Here are a few quick sketches I did, focusing on various aspects of the plant's design:





			

 



In order to create the leaves, we began by finding a point a given length above the xz plane, then rotated it a set number of degrees in either direction to create the end point of each leaf. From there, a curve can be drawn between each end point and the origin to establish fundamental leaves (top). The next step is to take each of these vectors and subdivide them into several points. now we can use the curve function to draw degree 3 curves through each of these points (bottom). While initially no different visually, this gives us the power to alter these points to create acutal curves later.


			

global proc string drawCurve (vector $pnts[], 
                              int $degree)
{
//begin making our command string
string $cmdStr = "curve -d "+ $degree + " ";

//Concatenate data onto it
int $n;
for($n = 0; $n < size($pnts); $n++)
$cmdStr += " -p " + $pnts[$n];
//finally, use the eval() function to pass the
//command string into the MEL interpreter
string $name = eval($cmdStr);

return $name;
}

global proc vector rotatePnt(float $alpha, vector $p)
{
return rot($p, <<0,0,1>>, deg_to_rad($alpha));
}
//____________________________________________________
global proc vector[] fan(float $angle, float $len, int $num)
{
vector $out[];
// find the angle between each leaf
float $a = $angle/($num - 1); // find the first leaf position
vector $axial_leaf = <<0,$len,0>>;
$out[0] = rotatePnt($angle/2, $axial_leaf); // loop around to find the other leaf positions
for($n = 1; $n < $num; $n++)
$out[$n] = rotatePnt(-$a * $n, $out[0]);
return $out;
} //____________________________________________________
global proc vector[] subdivide(int $div, 
                               vector $B, 
                               vector $E )
{
vector $end = $E - $B;
vector $pnt[];
float $u;
int $n;

for($n = 0; $n < $div + 1; $n++)
{
// Find the parametric distance along the vector
// Note the cast, or promotion, to a float
$u = (float) $n/$div;

// find the xyz at the parametric location and
// offset by $B so that we have the xyz relative
// to the true beginning of the line defined by
// locations $B and $E.
$pnt[$n] = $u * $end + $B;
//print ("$pnt = " + $pnt[$n] + "\n");
}
return $pnt;

}

 
    

 



The next challenge was to be able to manipulate these extra points in order to simulate gravity. Some considerations included: applying gravity more to points further out from the center, and applying it more to branches lower to the ground (xz plane). These were addressed by the function to the right. $u becomes a parametization value because it represents a fraction between 0 to 1, 0 being the center of the plant, 1 being the point furthest from the center on the leaf. By multplying the magnitude of "gravity" (really a set translation downward in y) by a power of $u greater than 1 (in order to prevent a linear translation of each successive point) a curve in the leaf is produced (above).

 

The next factor is addressed by the variable $angle which takes the dot product between the angle of each leaf (before curvature is created) and a standard vector in the x direction <<1,0,0>>. The dot product of two vectors returns the cosine of the angle between them. This ranges in value from -1 to 1, the extremes coming when the tested leaf is horizontal, and 0 when it is vetical. This is a perfect number to multiply into the magnitude of gravity because it is at its full value when the leaf is horizontal and 0 when it is vertical, which cancels out all gravity. Of course, the absolute value abs() of $angle was taken to prevent the leaves from floating upwards instead of down if the $angle happened to be negative.

 

Finally, to prevent a uniform look, some randomness was added to the magnitude each time a point was recalculated. (The user can control the amount of this randomness for several variables later on.)



			


global proc vector[] gravitizer (vector $pnts[], float $mag, 
                                 float $magrand, float $exp)
{
vector $out[];
int $n;
int $length = size($pnts);
// float $magnitude=$mag*$magrand;
// float $exponent= $exp*$exprand;
float $exprand = rand($exp-1, $exp+1);
for($n = 0; $n < $length; $n++){

float $angle = dot($pnts[$n], <<1, 0, 0>>);
$angle=abs($angle);
vector $temp = $pnts[$n];
float $u = (float) $n/$length;
float $ygrav = $temp.y -($mag*pow($u, ($exp*$exprand)) *$angle)+rand(-.5,.5)*$magrand*$u*$angle;
$temp= << $temp.x, $ygrav, $temp.z >>;
$out[$n]= $temp;
}

return $out;
}
       

A series of palms of the same leaf number and angle spread, but increasing amounts of gravity and random gravity.

 

 

 


As an alternative to always rotating leaves uniformly, a second function to randomly generate points on a sphere- sphrand() was used. These end points were then constrained to the xy plane and made a uniform length. the result is very similar to the uniform growth (especially as the number of leaves increases) but the spacing is much more random. This process could also be useful in generating other types of plants such as grass.


			

global proc vector[] palmgen(float $leaflength, 
                  int $numleaves, float $depth)
{
vector $out[];
for($n = 0; $n < $numleaves; $n++)
{
vector $e = sphrand($leaflength);
float $yFix = abs($e.y);
float $zFix = $e.z*$depth;
$e = << $e.x, $yFix, $zFix>>;
$e = unit($e)*$leaflength;
$out[$n] = $e;
}
return $out; }
 

			
       

 

 

 

 

 

 


Having a function to offset points for gravity made it easy to create a function to generate wind effects on the branches. The modifications were: to affect a point's x and z translations rather than y, and to take 1-$angle rather than $angle, thus allowing the wind to affect branches that are higher off the ground more. The code for this procedure is shown at right. The screen shot above shows the same plant, with and without wind. Only x and z values have been altered (the plant appears taller because it is coming toward us). This is an example of "Hurricane Force" wind and would probably be unsuitable for normal purposes.



		
global proc vector[] wind (vector $pnts[], float $mag, float $magrand, float $exp)
{
vector $out[];
int $n;
int $length = size($pnts);
// float $magnitude=$mag*$magrand;
// float $exponent= $exp*$exprand;
float $exprand = rand($exp-1, $exp+1);
for($n = 0; $n < $length; $n++){

float $angle = dot($pnts[$n], <<1, 0, 0>>);
$angle= 1 - abs($angle);
vector $temp = $pnts[$n];
float $u = (float) $n/$length;
float $zmag = $temp.z -($mag*pow($u, $exp*$exprand)*$angle)+rand(-.5,.5)*$magrand*$u*$angle;
float $xmag = $temp.x -($mag*pow($u, $exp*$exprand)*$angle)+rand(-.5,.5)*$magrand*$u*$angle;
$temp= << $xmag, $temp.y, $zmag >>;
$out[$n]= $temp;
}

return $out;
}

 

 

The next logical step was to nest the palm creation loop inside an outer loop in order to generate multiple palm branches. Initially, these were randomly scattered across the default grid and given random rotations and scales (these can all be controlled through the user interface, shown below).

for ($i=0; $i<$numcopies; $i++) 

{
vector $origin = <<0,0,0>>;
vector $leaf[];

string $shape[] = `listRelatives -s`;
addAttr -ln mtorCurveWidth
-dv $branchthickness
-k true $shape[0];
int $leafrandomizer = rand((-$numleaves/2),($numleaves/2))*$leavesrand;
float $anglerandomizer= rand((-$leafangle/2),($leafangle/2))*$anglerand;
float $combinedAngle = $leafangle + $anglerandomizer;
int $combinedNumber= $numleaves + $leafrandomizer;
vector $ends[];
if ($uniformspacing){

$ends= fan($combinedAngle, $leaflength,$combinedNumber);

}
else{
$ends= palmgen($leaflength, $combinedNumber, $depthspread);
}
for($n = 0; $n < $numleaves; $n++)
{

$leaf = subdivide($numpoints, $origin, $ends[$n]);
$leaf = gravitizer($leaf, $gravity, $gravityrand, $gravityexp);
$leaf = wind($leaf, $wind, $windrand, $windexp);

string $current = drawCurve($leaf, 3);
parent $current $original;

// RenderMan stuff...
// mtor control attach surface `slimcmd apph GetID`;
string $shape[] = `listRelatives -s`;
float $thickrandomizer=rand((-$leafthickness/2),($leafthickness/2))*$thicknessrand;
$thickrandomizer += $leafthickness;
addAttr -ln mtorCurveWidth
-dv $branchthickness
-k true $shape[0];
}
pickWalk -d up;
// pickWalk -d up;
//hilite -r;



$randmoveX = rand(-12,12);
$randmoveZ = rand(-12,12);
// $randmoveY = rand(-2,2);
}

}
$palmgroupcounter=$palmgroupcounter-1;
float $randroty = rand (-180,180)*$rotrand;
float $randrotxz = rand (-60,60)*$rotrand;
float $randscale= 1+(rand (-.75,3)*$scalerand);
select $original;

move -r $randmoveX $randmoveY $randmoveZ;
rotate $randrotxz $randroty $randrotxz;
if ($randscale>0)
scale $randscale $randscale $randscale;

 

			

 

 

 


To ground the palm branches (so they weren't just floating heads) A simple branch was added extending down from the base of the leaves. All the leaves were then parented to this branch and it's bottom was more the pivot point for future scales and rotations. The final palm generator code is shown at right.

 

 

In addtion the wind function had to be specially modified to only affect the middle points (leaving the outer point alone allows a sense of balance to be maintained while still creating a nice curve):

 

 

 

global proc vector[] branchwind (vector $pnts[], float $mag, float $magrand, float $exp)
{
vector $out[];
int $n;
int $length = size($pnts);
// float $magnitude=$mag*$magrand;
// float $exponent= $exp*$exprand;
float $exprand = rand($exp-1, $exp+1);
for($n = 0; $n < ($length-1); $n++){

// float $angle = dot($pnts[$n], <<1, 0, 0>>);
// $angle=abs($angle);
vector $temp = $pnts[$n];
float $u = (float) $n/$length;
float $zmag = $temp.z -$mag*$u+rand(-.5,.5)*$magrand*$u;
float $xmag = $temp.x -$mag*$u+rand(-.5,.5)*$magrand*$u;
$temp= << $xmag, $temp.y, $zmag >>;
$out[$n]= $temp;
}
$out[($length-1)]=$pnts[($length-1)];
return $out;
}


global proc palmer (int $numpoints, int $numleaves, float $leavesrand, 
                   float $depthspread, float $leaflength, float $lengthrand,
                   float $leafthickness, float $branchlength,
float $branchthickness, float $thicknessrand, int $uniformspacing, float $leafangle, float $anglerand, float $gravity, float $gravityrand, float $gravityexp, float $wind, float $windrand, float $windexp,int $numcopies, int $usenurbs, int $usecolor, float $scalerand, float $rotrand, int $palmspergroup)
{

int $palmgroupcounter = $palmspergroup ;
int $i,$n;
string $dagnodes[];
float $randmoveX=0;
float $randmoveZ=0;
float $randmoveY=0;
$dagnodes = `selectedNodes -dagObjects`;
string $palH= SlmPaletteUtils_CreatePalette("mine_all_mine");
string $appH= SlmPaletteUtils_AddShaderToPalette("plastic",$palH);
string $kdH= SlmParamUtils_GetPropertyNamed($appH,"Kd");
string $csH= SlmParamUtils_GetPropertyNamed($appH,"Cs");


//We'll use the apporpriate slim commands directly ourselves
slimcmd $kdH SetValue "1.0";
slimcmd $csH SetValue "{0.0 0.6 0.0}";
slimcmd $appH PreviewRender;
for ($i=0; $i<$numcopies; $i++)

{
vector $origin = <<0,0,0>>;
vector $leaf[];
vector $branch[];
$branch = subdivide($numpoints, $origin, << 0,(-1*$branchlength), 0 >> );
$branch = branchwind($branch, $wind, $windrand, $windexp);

string $original = drawCurve($branch, 3);

//mtor control attach surface `slimcmd apph GetID`;
string $shape[] = `listRelatives -s`;
addAttr -ln mtorCurveWidth
-dv $branchthickness
-k true $shape[0];
int $leafrandomizer = rand((-$numleaves/2),($numleaves/2))*$leavesrand;
float $anglerandomizer= rand((-$leafangle/2),($leafangle/2))*$anglerand;
float $combinedAngle = $leafangle + $anglerandomizer;
int $combinedNumber= $numleaves + $leafrandomizer;
vector $ends[];
if ($uniformspacing){

$ends= fan($combinedAngle, $leaflength,$combinedNumber);

}
else{
$ends= palmgen($leaflength, $combinedNumber, $depthspread);
}
for($n = 0; $n < $numleaves; $n++)
{

$leaf = subdivide($numpoints, $origin, $ends[$n]);
$leaf = gravitizer($leaf, $gravity, $gravityrand, $gravityexp);
$leaf = wind($leaf, $wind, $windrand, $windexp);

string $current = drawCurve($leaf, 3);
parent $current $original;

// RenderMan stuff...
// mtor control attach surface `slimcmd apph GetID`;
string $shape[] = `listRelatives -s`;
float $thickrandomizer=rand((-$leafthickness/2),($leafthickness/2))

*$thicknessrand;
$thickrandomizer += $leafthickness;
addAttr -ln mtorCurveWidth
-dv $branchthickness
-k true $shape[0];
}
pickWalk -d up;
// pickWalk -d up;
//hilite -r;

float $randu, $randv;
int $whichsurface = rand (0,size($dagnodes)-1);

if ($usenurbs && !$usecolor && $i==0) {

//set initial values if using NURBS surface
float $m[] = `pointOnSurface -u $randu -v $randv $dagnodes[$whichsurface]`;
$randmoveX=$m[0];
$randmoveZ=$m[2];
$randmoveY=$m[1];

}
else if ($usecolor && $i==0) //set initial values if using Colored surface
{
float $c[] = `colorAtPoint -o RGB -u .5 -v .5 "ramp1"`;

while ($c[1] ==0 || $c[0] > .2 || $c[2] >.2 || $c[2] > $c[1] || $c[0] >$c[1])
{
$randu=rand(0,1);
$randv=rand(0,1);
$whichsurface = rand (0,size($dagnodes)-1);
$c = `colorAtPoint -o RGB -u $randu -v $randv "ramp1"`;


}
$randmoveX=$c[0];
$randmoveZ=$c[2];
$randmoveY=$c[1];
}
if ($palmgroupcounter == 0) {

//break from current grouping to create a position for new group
$palmgroupcounter = $palmspergroup;
if ($usenurbs && !$usecolor) {
$randu=rand(0,1);
$randv=rand(0,1);

int $whichsurface = rand (0,size($dagnodes)-1);
float $m[] = `pointOnSurface -u $randu -v $randv $dagnodes[$whichsurface]`;

$randmoveX=$m[0];
$randmoveZ=$m[2];
$randmoveY=$m[1];
}

else if ($usecolor)
{
int $whichsurface = rand (0,size($dagnodes)-1);
// float $c[] = `colorAtPoint -o RGB -u .5 -v .5 $dagnodes[$whichsurface]`;
// createNode checker;
float $c[] = `colorAtPoint -o RGB -u .5 -v .5 "ramp1"`;
while ($c[1] ==0 || $c[0] > .2 || $c[2] >.2 || $c[2] > $c[1] || $c[0] >$c[1])

{
$randu=rand(0,1);
$randv=rand(0,1);
$whichsurface = rand (0,size($dagnodes)-1);
$c = `colorAtPoint -o RGB -u $randu -v $randv "ramp1"`;


}
$randmoveX=$c[0];
$randmoveZ=$c[2];
$randmoveY=$c[1];
}
else{
$randmoveX = rand(-12,12);
$randmoveZ = rand(-12,12);
// $randmoveY = rand(-2,2);
}

}
$palmgroupcounter=$palmgroupcounter-1;
float $randroty = rand (-180,180)*$rotrand;
float $randrotxz = rand (-60,60)*$rotrand;
float $randscale= 1+(rand (-.75,3)*$scalerand);
select $original;
xform -os -piv 0 (-$branchlength) 0;
move -r 0 $branchlength 0 $original;
move -r $randmoveX $randmoveY $randmoveZ;
rotate $randrotxz $randroty $randrotxz;
if ($randscale>0)
scale $randscale $randscale $randscale;
slimcmd slim Clear;

select $original;
mtor control attach surface `slimcmd $appH GetID`;

//mtor RenderSpool;

}
defaultDirectionalLight(1,1,1,1,"0",0,0,0);
rotate -45 0 0;
move 0 1 0;

}

       

 


A UI was created allowing the user to control all aspects of the creation of palms built into the code. The brach and leaf thickness attributes control how thickly MTOR renders each curve. The Use Nurbs and Use Color checkboxes control the way the palms are scattered. With both unchecked the palms are scattered across the default grid. With Use Nurbs the script selects random points in u and v space on a selected Nurbs surface and finds the world space coordinates of these, then translates the branch to this point. Use Color should do the smae thing for the color on the surface, but unfortunately is not wokring at present. The other interesting feature (Number Palms per Cluster) is explained below.


			
global proc palmerUI () 
{
string $myWindow;
$myWindow = `window -mnb true -in "Palm Generator"
-title "Palm Generator 1.0" -rtf true`;
columnLayout;
frameLayout -collapsable true - label "Detail Properties";
columnLayout;
intSliderGrp -min 3 -max 10 -v 3 -field true -label "Number Points" numPts;
intSliderGrp -min 3 -max 50 -v 3 -field true -label "Number Leaves" numLvs ;
floatSliderGrp -min 0 -max 1 -v 0 -pre 3 -field true -label "Leaf Randomness" lfRnd;
floatSliderGrp -min 0 -max .5 -v 0.05 -pre 3 -field true -label "Depth Spread" dpSpr;
setParent ..;
setParent ..;
frameLayout -collapsable true - label "Leaf Properties";
columnLayout;
floatSliderGrp -min .5 -max 10 -v .5 -pre 3 -field true -label "Leaf Length" lfLngth;
floatSliderGrp -min 0 -max 1 -v 0 -pre 3 -field true -label "Length Rand" lngthRnd;
floatSliderGrp -min 0.001 -max 0.1 -v .005 -pre 5 -field true -label "Leaf Thickness" lfThk;
floatSliderGrp -min 0 -max 10 -pre 3 -v 1 -field true -label "Branch Length" brLngth;
floatSliderGrp -min 0.002 -max .2 -pre 5 -v .01 -field true -label "Branch Thickness" brThk;
floatSliderGrp -min 0 -max 1 -pre 3 -v 0 -field true -label "Thickness Rand" thkRnd;
setParent ..;
setParent ..;


frameLayout -collapsable true - label "Construction Properties";
columnLayout;
checkBox -label "Uniform Spacing" uniSpc;
floatSliderGrp -min 5 -max 180 -v 90 -pre 3 -field true -label "Leaf Angle" lfAng;
floatSliderGrp -min 0 -max 1 -pre 3 -v 0 -field true -label "Angle Rand" angRnd;
setParent ..;
setParent ..;

frameLayout -collapsable true -label "Gravity and Wind";
columnLayout;
floatSliderGrp -min 0 -max 5 -pre 3 -v 2 -field true -label "Gravity Magnitude" grvMag;
floatSliderGrp -min 0 -max 1 -pre 3 -v 0 -field true -label "Gravity Variance" grvRnd;
floatSliderGrp -min 1 -max 5 -pre 3 -v 2 -field true -label "Stiffness" grvExp;
floatSliderGrp -min 0 -max 5 -pre 3 -v 1 -field true -label "Wind Magnitude" wndMag;
floatSliderGrp -min 0 -max 1 -pre 3 -v 0 -field true -label "Wind Variance" wndRnd;
floatSliderGrp -min 1 -max 5 -pre 3 -v 2 -field true -label "Stiffness" wndExp;
setParent ..;
setParent ..;

frameLayout -collapsable true - label "Multiplicity Properties";
columnLayout;
intSliderGrp -min 1 -max 200 -v 1 -field true -label "Number Copies" numCpy ;
checkBox -label "Use Nurbs Surface(s)" -onCommand "confirmDialog -title \"WARNING\"

-message \"Please have at least one valid Nurbs surface selected when you press the Create Palms button\"

-button \"OK\"" useNurb;
checkBox -label "Use Color Texture" -onCommand "confirmDialog -title \"WARNING\"

-message \"Please have at least one valid Nurbs surface selected with texture information when you press

the Create Palms button\"

-button \"OK\"" useCol;

floatSliderGrp -min 0 -max 1 -pre 3 -v 0 -field true -label "Scale Randomness" sclRnd;
floatSliderGrp -min 0 -max 1 -pre 3 -v 0 -field true -label "Rotation Randomness" rotRnd;
intSliderGrp -min 1 -max 20 -v 3 -field true -label "Number Palms per Cluster" numPpc ;

setParent ..;
setParent ..;
setParent ..;

rowColumnLayout -nc 4;
button -label "Make Palm(s)" -align "center" -command "palmer(`intSliderGrp -q -v numPts`,

`intSliderGrp -q -v numLvs`,`floatSliderGrp -q -v lfRnd`, `floatSliderGrp -q -v dpSpr`,`floatSliderGrp -q -v lfLngth`,

`floatSliderGrp -q -v lngthRnd`,`floatSliderGrp -q -v lfThk`,`floatSliderGrp -q -v brLngth`,`floatSliderGrp -q -v brThk`,

`floatSliderGrp -q -v thkRnd`, `checkBox -q -v uniSpc`, `floatSliderGrp -q -v lfAng`,`floatSliderGrp -q -v angRnd`,

`floatSliderGrp -q -v grvMag`, `floatSliderGrp -q -v grvRnd`, `floatSliderGrp -q -v grvExp`,`floatSliderGrp -q -v wndMag`,

`floatSliderGrp -q -v wndRnd`,`floatSliderGrp -q -v wndExp`,`intSliderGrp -q -v numCpy`,`checkBox -q -v useNurb`,

`checkBox -q -v useCol`, `floatSliderGrp -q -v sclRnd`, `floatSliderGrp -q -v rotRnd`,`intSliderGrp -q -v numPpc`)";
button -label "Undo Palm(s)" -align "center" -command "undo";
button -label "Delete All" -align "center" -command "select -all \; delete";
button -label "Close" -align "center" -command ("deleteUI -window " + $myWindow);

showWindow $myWindow;

}

 

 

 


To group the palm branches, a counter was used within the loop to generate branches. Each time a branch is created the group number is decremented by one. Until it reaches zero no additional transformations are applied to the branch, only rotations and scales resulting in a cluster of branches aroung the same fixed point. When it reaches zero a new position is calculated and that branch is moved to the new point. At the same time the group counter is reset back to its original value, resulting in all subsequent branches being frozen at the new point until to counter reaches zero and the translations are calculated once again. Above a relatively unaltered group to show how clustering works.
	if ($palmgroupcounter == 0) {//break from current grouping to create a position for new group
$palmgroupcounter = $palmspergroup;
if ($usenurbs && !$usecolor) {
$randu=rand(0,1);
$randv=rand(0,1);

int $whichsurface = rand (0,size($dagnodes)-1);
float $m[] = `pointOnSurface -u $randu -v $randv $dagnodes[$whichsurface]`;

$randmoveX=$m[0];
$randmoveZ=$m[2];
$randmoveY=$m[1];
}

else if ($usecolor)
{
int $whichsurface = rand (0,size($dagnodes)-1);
// float $c[] = `colorAtPoint -o RGB -u .5 -v .5 $dagnodes[$whichsurface]`;
// createNode checker;
float $c[] = `colorAtPoint -o RGB -u .5 -v .5 "ramp1"`;
while ($c[1] ==0 || $c[0] > .2 || $c[2] >.2 || $c[2] > $c[1] || $c[0] >$c[1])

{
$randu=rand(0,1);
$randv=rand(0,1);
$whichsurface = rand (0,size($dagnodes)-1);
$c = `colorAtPoint -o RGB -u $randu -v $randv "ramp1"`;


}
$randmoveX=$c[0];
$randmoveZ=$c[2];
$randmoveY=$c[1];
}
else{
$randmoveX = rand(-12,12);
$randmoveZ = rand(-12,12);
// $randmoveY = rand(-2,2);
}

}
$palmgroupcounter=$palmgroupcounter-1;

 

 

 

The same grouping with some more extreme variations shows how this behavior can generate a fairly realistic plant (allbeit this one also appears to be in a hurricane).

 

 


This shot illustrates three randomly generated clusters of palms...the randomizing capabilities of the system begin to show their power.

 


This wireframe demonstrates the placement of palm groups relative to the surface points of the Nurbs plane (as explained above).

 

 

 


All the following are renders in PRman of the curves to show how MTOR applies thicknesses to them. The code to automatically create a SLIM plastic shader and assign it to each branch as it was created was developed in its entirety by Professor Malcolm Kesson and appears at right.

global proc string[] SlmPaletteUtils_GetPaletteHandles()
{
string $palettes[];
string $handles = `slimcmd slim GetPalettes`;

tokenize($handles, $palettes);
return $palettes;
}

global proc string SlmPaletteUtils_GetPaletteLabel(string $handle)
{
return `slimcmd $handle GetLabel`;
}

global proc SlmPaletteUtils_ShowPalette(string $name)
{
slimcmd slim WindowCmd Show palettes;
}

global proc string SlmPaletteUtils_AddShaderToPalette(string $shadername, string $pltHandle)
{
$path = `slimcmd workspace ResolvePath shaders "plastic slo"`;
$path += ("/" + $shadername + ".slo");

$path = "$RMANTREE/lib/shaders/" + $shadername + ".slo";
slimcmd $pltHandle CreateInstance -file $path;

$shadername += "*";
return `slimcmd $pltHandle GetAppearances -name $shadername`;
}

global proc string SlmPaletteUtils_CreatePalette(string $name)
{

string $handle = `slimcmd slim FindPalette $name`;
if($handle != "")
return $handle;

$handle = `slimcmd slim CreatePalette -edit -new`;
slimcmd $handle SetLabel $name;
return $handle;
}

 

 

 

global proc string[] SlmParamUtils_GetPropertyHandles(string $appHandle)
{
string $all = `slimcmd $appHandle GetProperties`;
string $out[];
tokenize($all, $out);
return $out;
}

global proc string[] SlmParamUtils_GetParamNames(string $appHandle)
{
string $handles[] = getPropertyHandles($appHandle);
string $names[];

for($n = 0; $n < size($handles); $n++)
$names[$n] = `slimcmd $handles[$n] GetLabel`;
return $names;
}

global proc string SlmParamUtils_GetPropertyNamed(string $appHandle, string $name)
{
string $handles[] = SlmParamUtils_GetPropertyHandles($appHandle);
string $names[];

for($n = 0; $n < size($handles); $n++)
{
$names[$n] = `slimcmd $handles[$n] GetLabel`;
if($names[$n] == $name)
return $handles[$n];
}
return "";
}

global proc string[] SlmParamUtils_GetPropertyTypes(string $appHandle)
{
string $handles[] = SlmParamUtils_GetPropertyHandles($appHandle);
string $types[];

for($n = 0; $n < size($handles); $n++)
$types[$n] = `slimcmd $handles[$n] GetType`;
return $types;
}


      

 

 

A lonely plant has been randomly placed on the very edge of the NURBS surface, teetering between life and death as we know it....

 

The colored ramp on the leaves and branch was applied by hand to add some visual interest... Hopefully this can eventually be done within the script.

 

 

 

An additional render of several palm clusters.... The placement of each is perfectly on the surface of the Nurbs plane, which sometimes causes oddities (like the branch that is sinking into the ground on the plant on the right because it has been rotated too far).

 

 

A wider field of plants illustrates the potential for cg application, especially since renderman renderers curves very quickly (this render took about 8 seconds).

 

 


Several clusters of palms scattered across a NURBS surface

 

 

Well overall I'm happy with the progress made on this project. An obvious weakness is the inability to vary the curve width in MTOR (something we as a class will correct later). I was also unable to get the colorAtPoint function to work correctly (it is designed to return the RGB values of a texture at a given u and v coordinate but I could only get it to return 0 0 0). This would have allowed the user the ability to scatter palms across a surface based on it's texture information (i.e. paint their location). I will continue to work on this functionality.

 

The source code in its entirety can be viewed here.

 

As a plant generating system, I feel this one provides a great deal of user control. It has obvious flaws as well. without varying thickness it is hard to simulate the actual shap of the palm fronds. As of right now, it also cannot be animated (which would be a cool feature) and the branches don't have any trunks attached to them (which they very often do in real life). Also the code makes no allowance for creating the colored ramp that gives the palms on this page their gradient. I added this to the already existing shader (that WAS generated from code) by hand. These are all issues I would like to address in the future.