Class 5: Slim templates; Slim MEL commands; RIB generators
Slim templates

Slim templates are the preferred way of creating new shading blocks to use inside Slim. Reasons:

In addition to the notes below, see the RAT documentation for more details on writing templates. Also, the <PRManDistribution>/rat/lib/slim directory contains a lot of slim templates for you to study and learn from.

Before you load anything, under Renderman -> Slim -> Preferences -> Expert, turn 'Expert Menus' on. You can also turn expert mode on using this MEL call:

slimcmd SetPref ExpertMenus 1

Here is a really simple but functional .slim template, called basic.slim. Assuming it is in C:/temp, you can use the following MEL command to load it into Slim [note - another way to load your templates is to specify them in a slim.ini configuration file - see the Slim docs for details].

// loads the nodes found in basic.slim - after executing this line of MEL, you will be able to
// access these nodes under File -> Create Appearance -> Preloaded in any Slim palette
slimcmd slim ReadSlimFile "C:/temp/basic.slim"; 
There is also a third way to load .slim files. Do 'Renderman -> Slim -> Console' to get a Slim console window, and in it, type:
slim ReadSlimFile C:/temp/basic.slim

After you load the basic.slim file, create a 'Const' appearance (File -> Create Appearance -> Preloaded -> Const) contained in the file. Open up the 'Const' appearance just created, then do File -> View RSL Source. You can see the Slim-generated .sl RSL source for this template. When you render, it is this source that gets compiled (using shader.exe) into a .slo. In principle, you could have typed up this RSL by hand.. It looks fancier than a typical human-created .sl. Eg. instead of

surface Ceramic_1()
{
we see
/* defines ----------------------------------*/
#define SLIM_TYPEID_surface 0
#define SLIM_TYPEID_displacement 1
#define SLIM_TYPEID_volume 2
#define SLIM_TYPEID_light 3
#define SLIM_VERSION 350
#define SLIM_SHADERTYPE surface
#define SLIM_SHADERTYPEID SLIM_TYPEID_surface
#define SLIM_INSTANCETYPE shadingmodel
#define SLIM_INSTANCENAME Ceramic_1

/* shader body ------------------------------*/
SLIM_SHADERTYPE
SLIM_INSTANCENAME (
)
{
Next, connect a 'Fractal' appearance to our Const's surface color ('Fractal' will feed a color to Const). Now view the RSL source again. You'll see that the code is now expanded to include Fractal's RSL too - Fractal's RSL gets called first (as a function), and its output color is in turn used as an input to Const. So what looks like arrow connections in the Slim palette are really RSL function calls that get chained.

The Const template in basic.slim can be simplified even more as follows:

### verybasic.slim - a minimal template definition
### the foll. template has a single input and two ouputs
slim 1 extensions gnomonsr {
 extensions gnomon gnosl {
  template shadingmodel Const {
       description "A barebones 'constant.sl' simplification"
       parameter color SurfaceColor {
   label "Surface Color"
   detail varying
   default ".5 .5 .5"
       }
   parameter color CI {
   detail varying
   default ".5 .5 .5"
   access output
   display hidden
       }
   parameter color OI {
   detail varying
   default ".5 .5 .5"
   access output
   display hidden
       }

       RSLFunction {
   void
   gnoslConst(
       color SurfaceColor;
               output color CI;
               output color OI;

           )
   {
     CI = SurfaceColor;
     OI = 1;
           } 
       } 
   } 
  } 
} 

Here's a workflow for developing .slim templates:
Now it is time to look at the contents of .slim files. There are two parts to any .slim template file:
In addition to templates being able to be hooked up to each other by the user, they can also be used inside other template definitions by the template writer. The way to do this is using 'detail mustvary". For example, in 'displacements.slim' is this parameter declaration:
parameter float Displacement {
	    detail mustvary
	    default 0
	}
The 'Displacement' float parameter (which will be used as a displacement amount) *has* to come from another 'float' template, eg. noise. The 'Displacement' parameter does not come with a preset float template hooked up to it, the user needs to supply one.

In the 'Warp' manifold template is this block of code:

template manifold Warp {
	    description "Returns points additively warped by vector-value
	    	Unless you choose to map the Surface Point parameter, the
		default behavior is to use ST coordinates."
	    parameter vector warper {
		label "Warp Function"
		detail mustvary "pixar,VBrownian"
		default 0
	    }
Here, the 'warper' vector-valued parameter will be supplied by a vector-valued template. But unlike the 'Displacement' above, here a default vector template is hooked up to get the user started. That is done by appending a template name (of the proper type) right next to 'detail mustvary'. In our example it is the string "pixar,VBrownian".

This way of being able to use pre-existing templates is very cool, since you can use existing vector providers, color providers etc. inside your own template definitions.

Examine as many .slim files as you can, to get ideas on how to write a variety of templates. As mentioned earlier, the RAT distribution includes a LOT of templates (for almost all the appearance choices available in the Slim menus), so that should be your starting point.

Here's a .slim file by ZJ, who's very active in the RMan/MEL community. It contains a bunch of GI-related templates.

This is an autogenerated .slim template from Malcolm Kesson's Cutter.

Slim MEL commands

All of the Slim functionality is available via MEL commands slimcmd and mtor. These two keywords contain dozens of subcommands dealing with all aspects of MTOR and Slim. See the documentation for details. For a quick example, try:


// Do Slim operations without accessing the RenderMan menu!
global proc slimCmdsSampler()
{

    file -f -new;
    setAttr "persp.tx" 4.7;
    setAttr "persp.ty" 3.5;
    setAttr "persp.tz" 4.7;
    setAttr "persp.ry" 45.0;


    cylinder;
    directionalLight;

    string $newPal = `slimcmd slim CreatePalette -new -edit`;      
    slimcmd $newPal SetLabel "FunPalette";
    // slimcmd slim ReadSlimFile "/soft/apps/rat/4.5.1m40/lib/slim/shadingmodels.slim";
    string $shdrHandle = `slimcmd $newPal CreateInstance -template "pixar,Constant"`;
    $shdrID = `slimcmd $shdrHandle GetID`;
    select -ne -replace nurbsCylinderShape1;
    mtor control attach "surface" $shdrID; 
    mtor control renderspool;
}// slimCmdsSampler()

RIB generation programs

It is often advantageous to call a separate program from a RIB file, to create specialized geometry, etc. This can be done as a DSO (shared object) or as a standalone program. Here's a small standalone program. Note that the program waits in an infinite loop till it receives a line of input containing (at least) a 'detail' parameter and additional params it needs. This input, when received, is used to emit RIB (to stdout). After this the program HAS to print a \377 character as a signal to the caller (PRMan, in our case) that it has completed its RIB outputting.


/* Call the following genRegPoly.c, and ompile it using:
gcc -o genRegPoly genRegPoly.c -lm
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>



void doPolyGen(int nSides)
{
    int i;
    float angStep, currAng;

    printf("Polygon \"P\" [");
    /* printf("Sphere 1 -1 1 360\n"); */
    if (nSides<3)nSides=3;
    angStep= 2.0*M_PI/nSides;
    for(i=0;i < nSides;i++)
    {
	float x,y,z;

	currAng = 0.5*M_PI + i*angStep;
	x = cos(currAng);
	y = sin(currAng);
	z = 0.0;

	printf("%f %f %f ",x,y,z);

    }
    printf("]\n");



}/* doPolyGen() */


int main()
{
    void doPolyGen(int nSides);
    char buffer[256];
    double detail;
    int nSides;
    
    while(fgets(buffer,256,stdin))
    {
	sscanf(buffer,"%f %d",&detail,&nSides);
	doPolyGen(nSides);
	fprintf(stdout,"%c", '\377');
	fflush(stdout);
    }
}/* main() */

The compiled 'genRegPoly' program can be invoked (by PRMan) using the following sample RIB:


Display "Procedural.tiff" "framebuffer" "rgb"
Imager "background" "color" [0.1 0.15 0.3]
Format 1000 1000 -1
ScreenWindow -2 2 -2 2
PixelSamples 3 3 

WorldBegin

Translate 0 0 1

TransformBegin
Translate -1 1 0
Scale 0.9 0.9 0.9
Color [1 0 0]
Procedural "RunProgram" ["genRegPoly" "3"] [-1 1 -1 1 -1 1]
TransformEnd

TransformBegin
Translate 1 1 0
Scale 0.9 0.9 0.9
Color [0 1 0]
Procedural "RunProgram" ["genRegPoly" "4"] [-1 1 -1 1 -1 1]
TransformEnd

TransformBegin
Translate -1 -1 0
Scale 0.9 0.9 0.9
Color [0 0 1]
Procedural "RunProgram" ["genRegPoly" "5"] [-1 1 -1 1 -1 1]
TransformEnd

TransformBegin
Translate 1 -1 0
Scale 0.9 0.9 0.9
Color [1 1 0]
Procedural "RunProgram" ["genRegPoly" "6"] [-1 1 -1 1 -1 1]
TransformEnd

WorldEnd
You can test 'genRegPoly' yourself (without a RIB file) like so:

If you enter an input such as '1.0 5' for example you can see that the program outputs
Polygon "P" [.....
followed by the end-of-output character ÿ