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) return $name; global proc vector rotatePnt(float $alpha, vector $p) global proc vector[] subdivide(int $div,
vector $B,
vector $E )
for($n = 0; $n < $div + 1; $n++)
|
|
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)
|
||
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) |
||
|
global proc vector[] wind (vector $pnts[], float $mag, float $magrand, float $exp)
|
|
for ($i=0; $i<$numcopies; $i++)
{ $leaf = subdivide($numpoints, $origin, $ends[$n]); |
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) |
global proc palmer (int $numpoints, int $numleaves, float $leavesrand,
float $depthspread, float $leaflength, float $lengthrand,
float $leafthickness, float $branchlength,
{ $leaf = subdivide($numpoints, $origin, $ends[$n]); // RenderMan stuff... *$thicknessrand; float $randu, $randv; //set initial values if using NURBS surface while ($c[1] ==0 || $c[0] > .2 || $c[2] >.2 || $c[2] > $c[1] || $c[0] >$c[1]) //break from current grouping to create a position for new group select $original; //mtor RenderSpool; } |
||
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 ()
-message \"Please have at least one valid Nurbs surface selected when you press the Create Palms button\" -button \"OK\"" useNurb; -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; rowColumnLayout -nc 4; `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`)"; } |
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
|
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. |
tokenize($handles, $palettes); global proc string SlmPaletteUtils_GetPaletteLabel(string $handle) global proc SlmPaletteUtils_ShowPalette(string $name) global proc string SlmPaletteUtils_AddShaderToPalette(string $shadername, string $pltHandle) $path = "$RMANTREE/lib/shaders/" + $shadername + ".slo"; $shadername += "*"; global proc string SlmPaletteUtils_CreatePalette(string $name) string $handle = `slimcmd slim FindPalette $name`; $handle = `slimcmd slim CreatePalette -edit -new`;
global proc string[] SlmParamUtils_GetPropertyHandles(string $appHandle) global proc string[] SlmParamUtils_GetParamNames(string $appHandle) for($n = 0; $n < size($handles); $n++) global proc string SlmParamUtils_GetPropertyNamed(string $appHandle, string $name) for($n = 0; $n < size($handles); $n++) global proc string[] SlmParamUtils_GetPropertyTypes(string $appHandle) for($n = 0; $n < size($handles); $n++)
|
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. |