RfM
Sample RiMel Scripts I
Various Techniques


return to main index


Topics

Introduction

This tutorial presents examples of scripts intended for use at Mel insertion points with Pixar's RenderMan for Maya plugin. For example, the Mel proc, polyMeshArchiveRI, implemented by the first script would be assigned as the value of a Pre-Shape MEL attribute of a shape node - figure 1.



Figure 1


As touched upon in the tutorial RfM: Introduction to Ri Scripting, Pixar's RenderMan for Maya adds many custom Mel procs to Maya's scripting environment. Most of the sample scripts in this tutorial call the very useful rman ctxGetObject proc to get a reference to the name of the shape node to which a Mel script is "attached" ie.

    string $shape = `rman ctxGetObject`;

Most of the sample Pre-Shape MEL scripts set a number of RenderMan visibility attributes to ensure the object to which a script is "attached" is not seen in the final render ie.

    RiAttribute "visibility" "int camera" 0;
    RiAttribute "visibility" "int transmission" 0;
    RiAttribute "visibility" "int diffuse" 0;
    RiAttribute "visibility" "int specular" 0;
    RiAttribute "visibility" "int photon" 0;

The reader should also refer to the tutorial "Mel: Secondary Geometry". It provides samples of code that are similar to those presented in this tutorial.


Sourcing a Sample Script

The sample scripts presented in this tutorial can be sourced in two ways. If the reader has followed the suggestions in the tutorial, RfM:Setup for Mel, Rman and Slim. then a Mel script, or scripts, can be saved to,

    maya/projects/RfM_mel

However, when testing a sample script on an ad-hoc basis it is probably best to copy and paste its text into Maya's script editor window and manually source it (by clicking on the enter key on the numeric keypad).



Using a Polymesh as a Proxy Surface for Archives


Figure 1


Figure 2


Listing 1 (polyMeshArchiveRI.mel)


global proc polyMeshArchiveRI(string $pathToArchive)
{
// Get the name of the shape node
string $shape = `rman ctxGetObject`;
int     $num[] = `polyEvaluate -v $shape`;
  
for($n = 0; $n < $num[0]; $n++) {
    string $vert = $shape + ".vtx[" + $n + "]";
    $pos = `pointPosition -local $vert`;
    RiTransformBegin();        
        RiTranslate($pos[0], $pos[1], $pos[2]);
        RiReadArchive($pathToArchive);
    RiTransformEnd();
    }
RiAttribute "visibility" "int camera" 0;
RiAttribute "visibility" "int transmission" 0; 
RiAttribute "visibility" "int diffuse" 0; 
RiAttribute "visibility" "int specular" 0; 
RiAttribute "visibility" "int photon" 0; 
}

This script queries a polymesh. It uses the xyz position of each vertex to locate a pre-baked rib.




Using Particles as Proxy Objects


Listing 3 (particleArchiveRI.mel)


global proc particleArchiveRI(string $pathToArchive)
{
// Get the name of the shape node
string $shape = `rman ctxGetObject`;
  
$nodes = `listRelatives -parent $shape`;
$tnode = $nodes[0];
$num = `particle -q -count $tnode`;
float $pos[];
  
for($n = 0; $n < $num; $n++) {
    string $name = $tnode + ".pt[" + $n + "]";
    $pos = `getParticleAttr -at position $name`;
    RiTransformBegin();
        RiTranslate($pos[0], $pos[1], $pos[2]);
        RiReadArchive($pathToArchive);
    RiTransformEnd();
    }
RiAttribute "visibility" "int camera" 0;
RiAttribute "visibility" "int transmission" 0; 
RiAttribute "visibility" "int diffuse" 0; 
RiAttribute "visibility" "int specular" 0; 
RiAttribute "visibility" "int photon" 0; 
}

This script queries a particle system. It uses the xyz position of each particle to locate a pre-baked rib.




Using Particles to Generate Curves

See also, "user interface for particles generating curve archives"



Figure 5


Figure 6
150 frames at 500 particles/second: 10.2MB rib archive


This examaple uses a Mel and a python script (listings 5 and 6) to bake an archive rib file containing the xyz coordinates of an animated particle system. The baked archive file contains a number of RenderMan Curves statements - one for each particle created by an emitter. The curves, in effect, correspond to the trajectories traced by the particles during an animation. Unlike the previous examples, the scripts in this section can not be used directly to render a sequence of images. However, the pre-baked rib file generated by the scripts shown in this section can, of course, be used as a digital asset in an animation.

Before the Mel/python scripts were developed, a "proof-of-concept" Mel script, listing 4, was written. Although the script is able to output an archive file it is forced to do so by the inefficient technique of caching particle positions in the form of text. Unfortunately, Mel does not allow the use of an efficient "data structure", such as,

    vector $data[][];

Consequently, listing 4 is forced to use the very inefficient technique of appending particle positions to an array of strings - refer to the proc named addToCache(). The script is able to bake an archive file but only for small numbers of particles.


Listing 4 (particlesToCurvesRI.mel [test version])


global string $cache[];
  
//================================
global proc addToCache(int $index, float $data[])
{
global string $cache[];
string $pnt = $data[0]+" "+$data[1]+" "+$data[2]+"\n";
$cache[$index] = $cache[$index] + $pnt;
}
//================================
global proc updateCache()
{
// Get the name of the shape node
string $shape = `rman ctxGetObject`;
  
$nodes = `listRelatives -parent $shape`;
$tnode = $nodes[0];
$num = `particle -q -count $tnode`;
float     $pos[];
for($n = 0; $n < $num; $n++) {
    string $name = $tnode + ".pt[" + $n + "]";
    $pos = `getParticleAttr -at position $name`;
    addToCache($n, $pos);
    }
}
//================================
global proc string getSceneName()
{
string $sceneName = `file -q -sceneName -shortName`;
$sceneName = substring($sceneName, 1, size($sceneName) - 3);
return $sceneName;
}
//================================
global proc string getRIB_ArchiveDir()
{
string $projPath = `workspace -q -rootDirectory`;
return $projPath + "RIB_Archive";
}
//================================
global proc particlesToCurves2RI(int $begin, int $end, float $width)
{
global string $cache[];
  
int $time = `currentTime -q`;
if($time == 1)
    clear($cache);
if($time >= $begin && $time <= $end)
    updateCache();
if($time == $end) {
    string     $pathToCurves = getRIB_ArchiveDir() + "/" + 
                            getSceneName() + ".rib";
    string    $dataStr, $buffer[];
    int     $fileid = `fopen $pathToCurves "w"`;
    
    fprint($fileid, "Basis \"catmull-rom\" 1 \"catmull-rom\" 1\n");
    for($n = 0; $n < size($cache); $n++) {
        $dataStr = $cache[$n];
        $num = tokenize($dataStr, "\n", $buffer);
        if($num >= 4) {
            fprint($fileid, "Curves \"cubic\" [" + $num + 
                            "] \"nonperiodic\" \"P\" [\n");
            fprint($fileid, "\t");
            for($i = 0; $i < size($buffer); $i++) {
                fprint($fileid, $buffer[$i]);
                fprint($fileid, "\n\t");
                }
            fprint($fileid, "\n\t] \"constantwidth\" [" + 
                            $width + "]\n");
            print("SAVED ARCHIVE \"" + $pathToCurves + "\"\n");
            }
        }
    fclose $fileid;
    }
}


The Mel script shown in listing 5 is a simplified version of listing 4 because it delegates most of the heavy lifting to a "sister" python script - listing 6. Other than ensuring the python script is used to accumulate the particle positional data into a cache, it is a matter of personal preference how the workload is "shared" between the Mel script and its associated python script.

Baking the rib file consists of the following steps.

  1. query the name of the transform node to which the Mel script has been assigned,
  2. at the start of the animation initialize the particle cache,
  3. for each frame, add each particle (xyz) position to the particle cache,
  4. at the end of the animation write an archive rib file.

The Mel script handles step 1 while steps 2, 3 and 4 are handled by the python script.


Listing 5 (particlesToCurvesRI.mel)


global proc particlesToCurvesRI(int $startAt, float $width)
{
string $shape = `rman ctxGetObject`;
$nodes = `listRelatives -parent $shape`;
$tnode = "\"" + $nodes[0] + "\"";
  
python("import particlesToCurvesRI as ptc");
$pycommand = "ptc.particlesToCurvesRI(" + $tnode + "," + 
                                          $startAt + "," +
                                          $width + ")";
python($pycommand);
}



Listing 6 (particlesToCurvesRI.py)


import maya.cmds as mc
    
class Cache:
    def init(self):
        self.data = []
    def add(self, index, pos):
        if len(self.data) > index:
            xyz = pos[0:3]
            self.data[index].append(xyz[0])
            self.data[index].append(xyz[1])
            self.data[index].append(xyz[2])
        else:
            self.data.append(pos[0:3])
    def get(self, index):
        return self.data[index]
    def length(self):
        return len(self.data)
  
pCache = Cache()
  
#-----------------------------------------
def getSceneName():
    name = mc.file(q = True, sceneName = True, shortName = True)
    if len(name) == 0:
        name = "untitled"
    else:
        name = name[:len(name) - 3]
    return name
#-----------------------------------------
def getRIBArchiveDir():
    projPath = mc.workspace(q = True, rootDirectory = True)
    return projPath + "RIB_Archive"
#-----------------------------------------
def updateCache(tnode):
    global pCache
    pnum = mc.particle(tnode, q = True, count = True)
    for n in range(pnum):
        pname = tnode + ".pt[%s]" % n
        pos = mc.getParticleAttr(pname,at = 'position')
        pCache.add(n, pos)
#-----------------------------------------        
def particlesToCurvesRI(tnode, startAt, width):
    global pCache
    currFrame = mc.currentTime(q = True)
    endFrame = mc.getAttr("defaultRenderGlobals.endFrame");
    
    if currFrame == 1:
        pCache.init()
    if currFrame >= startAt and currFrame <= endFrame:
        updateCache(tnode)
    if currFrame == endFrame:
        index = 0
        pathToCurves = getRIBArchiveDir() + "/" + getSceneName() + ".rib";
        
        fileid = open(pathToCurves, 'w')
        fileid.write('Basis "catmull-rom" 1 "catmull-rom" 1\n')
        for n in range(pCache.length()):
            xyz = pCache.get(n)
            if len(xyz)/3 >= 4:
                rib = 'Curves "cubic" [%d] "nonperiodic" "P" [\n' % (len(xyz)/3)
                fileid.write(rib)
                for i in range(len(xyz)):
                    fileid.write("%s " % (xyz[i]) )
                rib = '\n] "constantwidth" [%f] \n' % width
                fileid.write(rib)
        fileid.close()


Running the Scripts

Following the suggestions at the beginning of this tutorial the Mel code in listing 5 should be saved as particlesToCurvesRI.mel in,

    maya/projects/RfM_mel

The python code in listing 6 should be saved as particlesToCurvesRI.py in the corresponding directory,

    maya/projects/RfM_python

Notes on how to ensure that Maya can source the users custom python scripts can be found in the tutorial "Maya: Setup for Python". In the Pre-Shape Mel text field the reader should enter,

    particlesToCurvesRI(1, 0.02);

The start and stop frames of a animation must be set in the Render Setting dialog box. Since the scripts will be used to write a pre-baked rib file there is no need to do batch rendering. Instead, enter this command in the Mel script editor.

    rman genrib

This will cause the particle simulation to run without any images being rendered. The pre-baked rib file will be written to the current projects RIB_Archive directory - its name will be the same as the name of the scene. Once the pre-baked rib file has been produced it is best tested with a simple beauty pass rib file of the form shown in listing 7.

Side Note
Three lines of rib code are required in order to obtain good renders of the curves ie.

    Hider "stochastic" "int sigma" [1] "float sigmablur" [1.0] 
    Attribute "dice" "hair" [1]
    Attribute "stochastic" "int sigma" [1]

Listing 7 (curves_test.rib)


Hider "stochastic" "int sigma" [1] "float sigmablur" [1.0]
Display "untitled" "it" "rgba"
Format 400 400 1
Projection "perspective" "fov" 20
ShadingRate 1
  
Translate  0 0 20
Rotate 90 1 0 0
Rotate 0   0 1 0
Scale 1 1 -1
WorldBegin
    AttributeBegin
        Attribute "dice" "hair" [1]
        Attribute "stochastic" "int sigma" [1]
        ReadArchive "PATH_TO_MAYA_PROJ_DIR/RIB_Archive/SCENE_NAME.rib"
    AttributeEnd
WorldEnd



© 2002- Malcolm Kesson. All rights reserved.