RenderMan: Dynamic Secondary Geometry
The procedural techniques presented in this project use Pixar's custom MEL procedures (part of RenderMan for Maya) to create secondary geometry at "render-time". Using the Pixar procedures, rmanGetAttrName() and rmanAddAttr(), a Mel script can conveniently create the widgets described by a .rman script and automatically add them to an "Extra RenderMan Attributes" panel. At render-time a second Mel script can easily query the widgets and use their values with one or more RiMel procedures to produce a final image.
Breakdown
Creating the Interface:
The interface is created through the rman and UI Scripts.
How The Interface Works: Offset From Mesh Origin
This offsets the RIB Archive object off of the base object.
How The Interface Works: Use Normals For Orientation
This aligns the RIB archive object to align to the "Normal" orientation of the vertices of the base object.
How The Interface Works: Min/Max Radius
This adjusts the Min and Max radius of the RIB Archive objects.
How The Interface Works: Number of Objects (Leaves)
This decreases the amount of, or density, RIB Archive objects on the vertices of the base object.
How The Interface Works: Random Y Rotation
This randomly rotates the RIB Archive objects in the Y direction.
How The Interface Works: Scattering Distance
This scatters the RIB Archive objects off the base object.
RMan File:
Declare param {string scatter_path} {
label "Rib Archive"
description "Leave empty if you wish to use spheres."
subtype file
range {*.rib}
}
label "Offset from Mesh Origin"
subtype slider
range {0 10}
description "Enables archives to zoom in and out of the mesh."
}
label "Use Normals for Orientation"
description "Aligns an archives Y axis to the mesh normal."
subtype switch
}
label "Min Radius"
subtype slider
range {0.001 5}
description "Min Radius of the vertex spheres."
}
label "Max Radius"
subtype slider
range {0.001 15}
description "Max Radius of the vertex spheres."
}
label "Number Of Leaves"
subtype slider
range {1 100 1}
description "Number of spheres or archives per vertex."
}
label "Random Y Rotation"
subtype slider
range {0 360}
description "Randomized axial rotation of each archive."
}
label "Scattering Distance"
subtype slider
range {0 10}
description "Radial scattering of spheres or archives."
}
UI.mel File
// The source document on which this mel script is based is,
// "/home/kvenem20/mount/stuhome/maya/projects/RfM_mel/scatter.rman"
// Cutter software by Malcolm Kesson (all rights reserved).
//
// Post Transform User Interface (UI) Mel Script
//
global proc scatterUI()
{
string $selected[] = `ls -sl`;
int $i;
for( $i=0; $i < size($selected); $i++ );
{
string $attr = `rmanGetAttrName "postTransformScript"`;
string $transformName = $selected[$i];
// "Connect" to the mel script that calls
// Pixar's custom Ri mel procedures.
rmanAddAttr $transformName $attr "scatterRI";
$attr = `rmanGetAttrName "scatter_path"`;
rmanAddAttr $transformName $attr "";
$attr = `rmanGetAttrName "scatter_offset"`;
rmanAddAttr $transformName $attr "";
$attr = `rmanGetAttrName "scatter_normals"`;
rmanAddAttr $transformName $attr "0";
$attr = `rmanGetAttrName "scatter_minR"`;
rmanAddAttr $transformName $attr "";
$attr = `rmanGetAttrName "scatter_maxR"`;
rmanAddAttr $transformName $attr "";
$attr = `rmanGetAttrName "scatter_num"`;
rmanAddAttr $transformName $attr "";
$attr = `rmanGetAttrName "scatter_yrot"`;
rmanAddAttr $transformName $attr "";
$attr = `rmanGetAttrName "scatter_scatterR"`;
rmanAddAttr $transformName $attr "";
}
}
RI.mel File
// The source document on which this mel script is based is,
//
"/home/kvenem20/mount/stuhome/maya/projects/RfM_mel/scatter.rman"
// Cutter software by Malcolm Kesson (all rights reserved).
//
// Post Transform Mel Script
//
global proc scatterRI() {
// Get the name of the transform node
string $tformNode = `rman ctxGetObject`;
// The node may hava a number in its name that we can use to set the random number generator
int $nodeNumber = `match "[0-9]+" $tformNode`;
if($nodeNumber != "") {
seed(int($nodeNumber));{
}{
group. string $children[] = `listRelatives -children $tformNode`;
string $shapeNode = $children[0];
float $bb_width = -1, $bb_height = -1, $bb_depth = -1;
if(size($children) == 1) {
$bb_width = `getAttr ($shapeNode + ".boundingBoxSizeX")`;
$bb_height = `getAttr ($shapeNode + ".boundingBoxSizeY")`;
$bb_depth = `getAttr ($shapeNode + ".boundingBoxSizeZ")`;
}
$attr = `rmanGetAttrName "scatter_path"`;
string $scatter_path = `getAttr($tformNode + "." + $attr)`;
$attr = `rmanGetAttrName "scatter_offset"`;
float $scatter_offset = `getAttr($tformNode + "." + $attr)`;
$attr = `rmanGetAttrName "scatter_normals"`;
int $scatter_normals = `getAttr($tformNode + "." + $attr)`;
$attr = `rmanGetAttrName "scatter_minR"`;
float $scatter_minR = `getAttr($tformNode + "." + $attr)`;
$attr = `rmanGetAttrName "scatter_maxR"`;
float $scatter_maxR = `getAttr($tformNode + "." + $attr)`;
$attr = `rmanGetAttrName "scatter_num"`;
int $scatter_num = `getAttr($tformNode + "." + $attr)`;
$attr = `rmanGetAttrName "scatter_yrot"`;
float $scatter_yrot = `getAttr($tformNode + "." + $attr)`;
$attr = `rmanGetAttrName "scatter_scatterR"`;
float $scatter_scatterR = `getAttr($tformNode + "." + $attr)`;
// Use of Pixar's custom RenderMan Studio procedures begins here.
// To use the shader of a "Custom Shading Group that can be
// assigned to the transform node of your geometry.
RiArchiveRecord("structure", "RLF Inject SurfaceShading");
vector $verts[];
getVertices($tformNode, $verts);
vector $norms[];
getNormals($tformNode, $norms);
if(size($verts) == 0 || size($norms) == 0)
return;
int $vertexCount = 0;
vector $vert, $norm;
float $rot[], $x, $y, $z, $yangle;
for($n = 0; $n < size($verts); ($n = $n + $scatter_num)) {
//Randomize size of objects
float $size = rand($scatter_minR, $scatter_maxR);
//Create Scatter
float $scatterx = rand(-$scatter_scatterR, $scatter_scatterR);
float $scattery = rand(-$scatter_scatterR,
$scatter_scatterR);
float $scatterz = rand(-$scatter_scatterR,
$scatter_scatterR);
$x = $vert.x;
$y = $vert.y;
$z = $vert.z;
// Apply position offset
$x += $vert.x * $scatter_offset;
$y += $vert.y * $scatter_offset;
$z += $vert.z * $scatter_offset;
RiAttributeBegin();
RiTranslate($x, $y, $z);
RiTranslate($scatterx, $scattery, $scatterz);
// Alignment to the mesh formal
if($scatter_normals) {
$norm = $norms[$n];
$rot = aimY($norm);
RiRotate($rot[1], 0,0,1);
RiRotate($rot[0], 1,0,0);
}
// Random Y Rotation
if($scatter_yrot > 0) {
$yangle = rand(-$scatter_yrot, $scatter_yrot);
RiRotate($yangle, 0, 1, 0);
}
// the randomized scaling
RiScale($size, $size, $size);
if(size($scatter_path) == 0) {
RiRotate(-90,1,0,0);
RiArchiveRecord("comment", "\nCone .2 0.05 360");
}
else
{
RiRotate($rot[0]+$rot[1], 0,1,0);
float $tint = float($vertexCount)/size($verts);
RiAttribute("user", "float tint", $tint);
RiReadArchive($scatter_path);
}
RiAttributeEnd();
}
}
// in the project directories "data" folder.
global proc string getArchivePath(string $nodename) {
string $projpath = `workspace -q -rootDirectory`;
string $datapath = $projpath + "data/";
string $scenename = `file -q -sceneName -shortName`;
int $frame = `currentTime -q`;
string $fstr = "0" + $frame;
if($frame < 10)
$fstr = "000" + $frame;
else if($frame < 100)
$fstr = "00" + $frame;
$ribpath = $datapath + $nodename + "." + $fstr + ".rib";
return $ribpath;
}