Cutter
Rman & RiMel Scripting


return to main index



Introduction

This tutorial explains how Cutter handles Pixar's RenderMan Studio (RMS) ".rman" documents. Rman files describe the UI that appear within the "Extra RenderMan Attributes" tab of a transform node or shape node - figure 1. Or the the UI can be assigned to the Geometric Settings panel of the RenderMan Controls window - figure 2.



Figure 1
A custom interface added to the shape node.



Figure 2
The same interface added to the "RenderMan Controls Window"


For example, listing 1 defines the interface shown above.


Listing 1 (runProgramUI.rman)


rman "-version 1" {
Declare param {string rp_helperApp} {
    label "Helper App Path"
    description "The path to the procedural helper app."
    subtype file
    range {*.py}
    }
Declare param {string rp_args} {
    label "Inputs"
    description "Inputs must be separated by at least one white space."
    }
Declare param {float rp_bboxScale} {
    label "BBox Scale"
    subtype slider
    range {1.0 10 .1}
    description "A scaling factor for the bounding box."
    }
Declare param {int rp_useBBox} {
    label "Use Proxy BBox"
    description "Use or ignore the proxy bounding box."
    subtype switch
    }
}

A .rman script does not create the interface in Maya. That task is accomplished by a MEL script that uses two Pixar MEL procedures, rmanGetAttrName() and rmanAddAttr(). Based on the information contained in each of the Declare param blocks the procs populate the panel of the "Extra RenderMan Attributes" with user interface widgets such as text fields, check boxes and sliders.


Naming Conventions

The tutorial "RMS: Ri Scripting" recommends the use of a strict naming convention for ".rman" files and their "associated" MEL scripts. For example, the MEL script that adds custom attributes to the RenderMan tab should have the same name as the ".rman" script. Likewise the MEL script that querries the interface and injects additional statements into the rib stream should have a name derived from the ".rman" file. For example,

    runProgramUI.rman (describes the interface)
    runProgramUI.mel (creates the interface)
    runProgramRI.mel (querries the interface and outputs extra rib statements)

The next section demonstrates how Cutter can assist in editing "xxxUI.rman" files and how it can generate their associated "xxxUI.MEL" and "xxxRI.MEL" scripts.


Example - An Interface for a Procedural Primitive

This section provides a step-by-step example of creating a ".rman" script and its associated MEL scripts from scratch. The work flow consists of three steps.
    Step 1, with the aid of Cutter, the .rman script is created.
    Step 2, with the aid of Cutter, the UI and RI .mel scripts are produced.
    Step 3, the .mel scripts are edited.

For the purposes of this tutorial the trio of files generated in steps 1 to 3 produce an interface that enables a procedural primitive, implemented by a python script, to render a large number of RenderMan points (particles) distributed in the vicinity of a proxy object.

Figure 3 shows a nurbs sphere acting as a proxy object. The interface shown in figure 1 was "assigned" to the sphere and as a consequence, as shown in figure 4, it has been "replaced" by a cluster of points.



Figure 3



Figure 4


The scripts shown in this section should be saved in the maya/projects/RMS_mel directory. The reader should review the suggestions in the tutorial, RMS: Setup for RiMel, Rman, Slim, Image Tool & Python before continuing.


Step 1 - Creating the .rman UI description script

Use the Templates menu to generate an "empty" rman document - figure 5.
Save it as "runProgramUI.rman".



Figure 5


Adding a File UI

Right mouse click in the document, immediately after the first open curly brace, and use the popup menu to insert a "string" (file) param block - figure 6. The text that will be inserted is shown below on the right.



Figure 6

    Declare param {string rp_path} {
        label "Helper App Path"
        description "No description."
        subtype file
        range {*.py}
        }

Change the name of the param to rp_path, the label to Helper App Path and the range to *.py. The prefix rp_ is derived from the name of the ".rman" document ie, runProgramUI


Adding a String UI

After the last param use the popup menu to insert a "string" (other) param block - figure 7.



Figure 7

    Declare param {string rp_args} {
        label "Args"
        description {Inputs must be separated by 
                    at least one white space.}
        }

Edit the param block so that it conforms to the text shown above.


Adding a Float Slider UI

Use the popup menu to insert a "float" (slder) param block - figure 8.



Figure 8

    Declare param {float rp_bboxScale} {
        label "BBox Scale"
        subtype slider
        range {0.1 10 .001}
        description "A scaling factor for the bounding box."
        }

Edit the param block so that it conforms to the text shown above.


Adding a Checkbox UI

Use the popup menu to insert a "int" (switch) param block - figure 9.



Figure 9

Declare param {int rp_useBBox} {
    label "Use BBox"
    description "Use or ignore the proxy bounding box."
    subtype switch
    }

Edit the param block so that it conforms to the text shown above.


Previewing the UI

The .rman script created with Cutter should look the same as the one shown in listing 1. To preview the interface defined by the ".rman" script, use the keyboard shortcut alt + e, control + e or Apple + e. A representation of the user interface defined by the .rman script will open in a browser - figure 9.



Figure 10
UI previewed in a web browser


Step 2.1 - Creating the UI MEL script

Right mouse click anywhere in the .rman document and from the popup menu select "UI PreShape script" - figure 11.



Figure 11


Listing 2 shows the MEL script that Cutter will generate. Save the script in the RMS_mel directory. In addition to writing the script Cutter will also append two additional blocks of information to the .rman script. The Collection and NodeOptions blocks added by Cutter enable the interface implemented by runProgramUI.mel to be assigned to the Geometric Pettings panel of the RenderMan Controls window.


Listing 2 (runProgramUI.MEL)


global proc runProgramUI() 
{
string $selected[] = `ls -sl`;
int $i;
  
for($i = 0; $i < size($selected); $i++ ) {
    string $shp[] = `listRelatives -shapes $selected[$i]`;
  
    // To add the UI implemented by this proc to the "Geometric
    // Settings" panel use the following mel commands,
    //        select rmanSettings;
    //        runProgramUI;
    // where "rmanSettings" might be rmanSettings1, rmanSettings2 etc.
    if(`nodeType $selected[$i]` == "RenderMan")
        $shp[0] = $selected[$i];
  
    string $shapeName = $shp[0];
    string $attr = `rmanGetAttrName "preShapeScript"`;
  
    // "Connect" to the mel script that calls 
    // Pixar's custom Ri mel procedures.
    rmanAddAttr $shapeName $attr "runProgramRI";
        
    $attr = `rmanGetAttrName "rp_path"`;
    rmanAddAttr $shapeName $attr "";
  
    $attr = `rmanGetAttrName "rp_args"`;
    rmanAddAttr $shapeName $attr "";
  
    $attr = `rmanGetAttrName "rp_bboxScale"`;
    rmanAddAttr $shapeName $attr "";
  
    $attr = `rmanGetAttrName "rp_useBBox"`;
    rmanAddAttr $shapeName $attr "0";
    }
}


Step 2.2 - Creating the RI MEL script

Use the RMS MEL Export menu to generate the RI Script - figure 12.



Figure 12


Listing 3 shows the MEL code that will be generated by Cutter. Save the script in the RMS_mel directory.


Listing 3 (runProgramRI.mel)


global proc runProgramRI()
{
// Get the name of the shape node
string $shapeNode = `rman ctxGetObject`;
string $parents[] = `listRelatives -parent $shapeNode`;
string $tformNode = $parents[0];
  
// The node may hava a number in its name that we can use 
// to set the random number generator
int    $nodeNumber = `match "[0-9]+" $shapeNode`;    
if($nodeNumber != "") {
    seed(int($nodeNumber));
    }
  
string $attr;
$attr = `rmanGetAttrName "rrp_helperApp"`;
$attr = `rmanGetFullSharedGeometricAttrName $shapeNode $attr`;
string $rrp_helperApp = `getAttr $attr`;
  
$attr = `rmanGetAttrName "rrp_args"`;
$attr = `rmanGetFullSharedGeometricAttrName $shapeNode $attr`;
string $rrp_args = `getAttr $attr`;
  
$attr = `rmanGetAttrName "rrp_bboxScale"`;
$attr = `rmanGetFullSharedGeometricAttrName $shapeNode $attr`;
float $rrp_bboxScale = `getAttr $attr`;
  
$attr = `rmanGetAttrName "rrp_useBBox"`;
$attr = `rmanGetFullSharedGeometricAttrName $shapeNode $attr`;
int $rrp_useBBox = `getAttr $attr`;
  
// Use of Pixar's custom RenderMan Studio procedures begins here.
// For example, here are a few of Pixar's RiMel commands.
RiTransformBegin();
    RiSphere(1, -1, 1, 360);
RiTransformEnd();
}

The last three lines of MEL are present only for the purposes of showing where the code that uses Pixar's RiMel commands should appear in the script.


Step 3 - Editing the MEL scripts

The runProgramUI.mel script requires very little editing. If the reader wishes to set a default value they can do so by inserting an appropriate value within the empty quotations of an attribute. For example,

    attr = `rmanGetAttrName "rrp_bboxScale"`;
    rmanAddAttr $shapeName $attr "";

might be changed to,

    attr = `rmanGetAttrName "rrp_bboxScale"`;
    rmanAddAttr $shapeName $attr "2.0";

The runProgramRI.mel script will require a moderate amount of editing. The three lines at the end of the runProgramRI.mel script should be replaced by the code snippet shown in listing 4.


Listing 4


//Append the number obtained from the shape name to the list of
// args. The helper app can use the number to set a seed value or
// simply ignore the arg.
$rrp_args = $rrp_args + " " + $nodeNumber;
    
float     $minX = -10000, $minY = -10000, $minZ = -10000, 
        $maxX = 10000, $maxY = 10000, $maxZ = 10000;
    
// Use the bounding box from the maya shape node
if($rrp_useBBox) {
    string $curUnit = `currentUnit -q -linear`;
    currentUnit -linear "cm";  // temporarily change to centimeters
    float $bbSize[3] = `getAttr ($shapeNode + ".boundingBoxSize")`;
    float $bbMin[3], $bbMax[3];
    $minX = -$bbSize[0]/2 * $rrp_bboxScale/2;
    $minY = -$bbSize[1]/2 * $rrp_bboxScale/2;
    $minZ = -$bbSize[2]/2 * $rrp_bboxScale/2;
    $maxX = $bbSize[0]/2 * $rrp_bboxScale/2;
    $maxY = $bbSize[1]/2 * $rrp_bboxScale/2;
    $maxZ = $bbSize[2]/2 * $rrp_bboxScale/2;
    currentUnit -linear $curUnit;
    }
  
// For debugging purposes we print the inputs
print("Helper: " + $rrp_helperApp + "\n");
print("Args: " + $rrp_args + "\n");
print("BBox: " + $minX + " " + $maxX + " " + 
                $minY + " " + $maxY + " " +
                $minZ + " " + $maxZ + "\n");
print("Seed obtained from the name of the proxy: " + $nodeNumber + "\n");
  
string $pycmd = "python "; // assume we are on Windows
if(`about -os` == "linux" || `about -os` == "mac")
    $pycmd = "/usr/bin/python ";
    
// Use the Pixar proc to output our call to the RIB
//   Procedural "RunProgram" "python SCRIPT" "ARGS" [BOUNDS]
RiProcedural("RunProgram",$pycmd + $rrp_helperApp,
$minX, $maxX,
$minY, $maxY,
$minZ, $maxZ,
$rrp_args);
// Turn "off" the visibility of the proxy geometry
RiAttribute "visibility" "int camera" 0 "int diffuse" 0 "int specular" 0 "int transmission" 0;

The code for the helper app (procedural primitive) is show in listing 5.


Listing 5 (addpoints.py)


import sys, random
from pnoise import pnoise
  
inputs = sys.stdin.readline()
while inputs:
    items = inputs.split()
    pixels = float(items[0])
    num = int(items[1])
    dia = float(items[2])
    freq = float(items[3])
    size = float(items[4])
    sed = int(items[5])
    
    random.seed(sed)
    print 'Points "P" ['
    for n in range(num):
        x = random.random() * freq * random.uniform(0.8, 1.3)
        y = random.random() * freq * random.uniform(0.8, 1.3)
        z = random.random() * freq * random.uniform(0.8, 1.3)
  
        x = pnoise(x,y,z) * size
        y = pnoise(y,x,z) * size 
        z = pnoise(z,x,y) * size         
        print '%1.3f %1.3f %1.3f' % (x,y,z)
    print '] "constantwidth" [%1.3f]' % dia    
    sys.stdout.write('\377')
    sys.stdout.flush()
    inputs = sys.stdin.readline()

The addpoints.py script makes use of a module named pnoise. The code for it can be found in the tutorial "Noise: Perlin Improved Noise" listing 1. The scripts can be saved into any location, however, both addpoints.py and pnoise.py must be in the same directory.


Using the Scripts

To add the interface to the shape node of surface follow these two steps.
Step 1 - shape node
    Create an object, say a nurbs sphere one unit in radius.
Step 2 - shape node
    In the script window run this command,
        runProgramUI;
The interface shown in figure 1 will appear in the attributes editor.

Alternatively, follow these steps to add the interface to the Geometric Settings panel of the RenderMan Controls window.
Step 1 - geometric settings
    Open the RenderMan Settings window and choose
    "Geometric Settings" from the View menu.
Step 2 - geometric settings
    In the script window run these commands,
        select rmanSettings;
        runProgramUI;
In both cases use the file browser to locate the addpoints.py script and, to get started, enter the arg values shown below in figure 13.



Figure 13







© 2002- Malcolm Kesson. All rights reserved.