Short Contents

7.17 Using Ri Plug-in Filters

Ri plug-in filters are an important extension to the RenderMan interface, providing flexibility unknown to non RenderMan systems and pipelines. In short, a Ri plug-in is a component lying between the application that generates Ri calls (such as a RIB reader like renderdl) and the rendering core. Being in such a strategic place, the plug-in can alter or filter, in any desirable way, all Ri commands received by the renderer.

ri-plugins

Figure 7.7: Ri plug-in filters lie between the program that generates the Ri calls (such as the RIB reader, renderdl) and the 3DELIGHT renderer.

7.17.1 Ri Plug-in Anatomy

As pictured in Figure 7.7, a plug-in is a DSO(64) that is loaded prior to the scene description phase. This section explains the general ideas behind the structure and execution of Ri plug-ins, a formal description of all functions available to plug-ins can be found in Ri Filter Plug-ins.

Initialization

Each DSO provides a RifPluginManufacture entry point that creates an instance of the plug-in. Each such plug-in derives from the RifPlugin abstract class. At this stage, the plug-in decides which Ri commands will be filtered and what to do with Ri commands that are not processed by the plug-in: either continue execution of other plug-ins in the chain or terminate immediately in the chain of execution. Note however that a plug-in can decide to run in the "continue" mode but switch to the "terminate" mode later on, and vice-versa. As an example, Listing 7.18 shows the initialization code of a very simple plug-in filter that computes the total area of all spheres in some given scene.

#include "ri.h"
#include "rif.h"

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

class Example : public RifPlugin
{
public:
    Example()
    {
        m_filter.Filtering = RifFilter::k_Continue;
        m_filter.SphereV = myRiSphereV;
        m_area = 0.0f;
    }
    virtual ~Example()
    {
        fprintf( stdout, "Total RiSphere area: %f\n", m_area );
    }
    virtual RifFilter &GetFilter() { return m_filter; }
private:
    float m_area;
    RifFilter m_filter;
    static RtVoid myRiSphereV(
        RtFloat radius, RtFloat zmin, RtFloat zmax,
        RtFloat tmax, RtInt, RtToken[], RtPointer[]);
};

RifPlugin *RifPluginManufacture( int argc, char **argv )
{
    return new Example();
}

void RifPluginDelete( RifPlugin *i_plugin )
{
    delete i_plugin;
}
Listing 7.18: Example of a simple Ri plug-in filter.

In the constructor, we initialize the default filtering mode to `k_Continue' which means that all ignored Ri calls simply continue their normal execution path. Additionally, we set the SphereV member of RifFilter to point to our function since we desire to compute sphere's area. We also set m_area to zero since this is our sphere area counter.

Execution

When 3DELIGHT executes an Ri command, it will first process loaded plug-in filters. Filters will be executed in the order they were loaded. Here is a pseudo-code of the renderer's first action when it executes an Ri command:

00: while ( current filter )
01:    if( current filter catches this Ri command )
02:        call filter's entry point for Ri command
03:        return;
04:    else if( current filter has the terminate flag )
05:        /* current Ri command is not processed by the filter and
06:           the terminate flag is set: exit skipping all remaining
07:           filters in the chain */
08:        return;
09:    end if
10:
11:    /* current Ri command is not processed by the filter, but the
12:       continue flag is set, so continue with other filters in the
13:       chain */
14:
15:    current filter = next filter in chain;
16: end while
17:
18: ... continue execution with renderer's Ri command ...
19:
Listing 7.19: Pseudo code explaining what the renderer does when executing a Ri command.

As shown in lines 02 and 03, as soon as as the renderer encounters a plug-in filter that handles the Ri command, it calls it and exits from the Ri command. This means that if the filter doesn't call the Ri command explicitly, the execution will not continue "down the chain". Continuing Listing 7.18, the myRiSphere procedure would look like this if the wanted action is to continue with renderer's RiSphere code:

RtVoid Example::myRiSphereV(
    RtFloat radius, RtFloat zmin, RtFloat zmax,
    RtFloat tmax, RtInt n, RtToken tokens[] , RtPointer params[] )
{
    Example *current_plugin = (Example *)RifGetCurrentPlugin(); 
    current_plugin->m_area += 4.0f * M_PI * radius * radius;

    /* Call RiSphereV to continue in the filter chain */
    RiSphereV( radius, zmin, zmax, tmax, n, tokens, params );	
}

Note, in the code snippet above, how we obtain the current active plug-in using RifGetCurrentPlugin(). This is necessary since myRiSphereV() is static and only the renderer knows which instance of the `Example' plug-in is running. At the end of the function, calling the RiSphereV command insures that execution continues normally and that the sphere is rendered.

7.17.2 Using renderdl to Load Ri Plug-ins

The simplest way to load Ri plug-in filters is by specifying them as a command line argument to renderdl. As explained in Using the RIB Renderer - renderdl, renderdl accepts the `-rif', `-rifargs' and `-rifend' parameters to handle plug-in filters. If we want to run our example filter on a simple RIB:

% renderdl -rif example.so
Translate 0 0 10
WorldBegin
Sphere 1 -1 1 360
Sphere 0.5 -0.5 0.5 360
WorldEnd
^D  (This is a CTRL-D)
Total RiSphere area: 15.707964

Passing arguments to Ri plug-ins is straightforward using the `-rifargs' and `-rifend' parameters:

% renderdl -rif somerif.so -rifargs -arg1 -arg2 -arg3 -rifend  somerib.rib

This example will load `somerif.so' and pass three arguments to the RifPluginManufacture() function. Chaining multiple Ri plug-ins on the command line is accomplished by using the `-rif' option many times:

% renderdl -rif somerif.so -rigargs -s -rifend \
        -rif anotherrif.so -rigargs -p -rifend

In this case, two filters will be loaded and executed in sequence: the first one, `somerif.so', will receive `-s' as an option and the second one will receive `-p'.
It is sometimes useful to run a RIB through one or more filters and store the filtered RIB back on disk. This is accomplished using the `-catrib' command line option. As an example, lets modify the myRiSphereV function defined in Listing 7.18 so that it outputs RiDisks instead of spheres:

RtVoid Example::myRiSphereV(
    RtFloat radius, RtFloat zmin, RtFloat zmax,
    RtFloat tmax, RtInt n, RtToken tokens[] , RtPointer params[] )
{
    Example *current_plugin = (Example *)RifGetCurrentPlugin(); 
    current_plugin->m_area += 4.0f * M_PI * radius * radius;

    /* Call RiDisk instead for more fun ... */
    RiDisk( 0.0f, radius, tmax, RI_NULL );
}

When running renderdl on the following simple RIB scene:

% renderdl -catrib -rif example.so
Translate 0 0 10
WorldBegin
Sphere 1 -1 1 360
Translate 0 0 -1
Color 1 0 0
Sphere 0.5 -0.5 0.5 360
WorldEnd
^D

We obtain the following RIB:

Translate 0 0 10

WorldBegin  # {
  Disk 0 1 360
  Translate 0 0 -1
  Color [ 1 0 0 ]
  Disk 0 0.5 360
WorldEnd  # }

Spheres have been replaced by disks as intended.

Note

renderdl uses 3DELIGHT's RifPluginLoad() function to load plug-in filters. Plug-ins are unloaded using RifUnloadPlugins(). These entry points are available to all programs that link with 3DELIGHT and are further described in Ri Filter Plug-ins.

3Delight 10.0. Copyright 2000-2011 The 3Delight Team. All Rights Reserved.