• No results found

Procedural Primitives

In document The RenderMan Interface pdf (Page 98-103)

Section 5 GEOMETRIC PRIMITIVES

5.7 Procedural Primitives

Procedural primitives can be specified as follows:

RiProcedural(RtPointerdata,RtBoundbound,

RtProcSubdivFuncsubdividefunc,RtProcFreeFuncfreefunc )

This defines a procedural primitive. Thedataparameter is a pointer to an opaque data structure that defines the primitive. (The rendering program does not “look inside” data, it simply records it for later use by the procedural primitive.) bound

is an array of floats that define the bounding box of the primitive in object space.

subdividefunc is the routine that the renderer should call (when necessary) to have the primitive subdivided. A bucket-based rendering scheme can potentially save memory space by delaying this call until the bounding box overlaps a bucket that must be rendered. The calling sequence forsubdividefuncis:

(*subdividefunc)(RtPointerdata,RtFloatdetail )

wheredatais the parameter that was supplied in defining the primitive, anddetail

is the screen area of the boundof the primitive. Whensubdividefunc is called, it is expected to subdivide the primitive into other smaller procedural primitives or into any number of non-procedural primitives. If the renderer can not determine the true detail of thebound (e.g., if the geometric primitive data is being archived to a file),

subdividefuncmay be called with adetailvalue equal toRI INFINITY. This should be interpreted by thesubdividefuncas a request for the immediate full generation of the procedural primitive.

freefunc is a procedure that the rendering program calls to free the primitive when thedatais no longer needed. The calling sequence forfreefuncis:

(*freefunc)(RtPointerdata )

Note that the rendering program may call back multiple times with the same proce- dural primitive, so the data area should not be overwritten or freed until thefreefunc

is called.

The RenderMan Interface provides three standard built-in procedural primitives. Their declarations are:

RtVoid RiProcDelayedReadArchive(RtPointerdata,RtFloatdetail);

RtVoid RiProcRunProgram(RtPointerdata,RtFloatdetail);

RtVoid RiProcDynamicLoad(RtPointerdata,RtFloatdetail);

These built-in procedurals are thesubdivideroutines. All three may use the single built-infreefunction:

RtVoid RiProcFree(RtPointerdata);

TheRiProcFree procedure simply calls the standard Cfree()routine ondata. The meanings of the standard built-in procedural types are explained below.

RIB BINDING

Proceduralprocname[args] [bound]

The procedural nameprocnamemust be a built-in procedural, either one of the stan- dard ones described below or an implementation-specific procedural. Theargs pa- rameter is an array of strings supplying arguments to the built-in procedural. The expected arguments for each type of procedural are explained in the following sec- tions on those primitives.

EXAMPLE

RtStringargs[] ={”sodacan.rib”};

RiProcedural((RtPointer)args, mybound,RiProcDelayedReadArchive, RiProcFree);

Procedural”DelayedReadArchive” [ ”sodacan.rib” ] [ -1 1 -1 1 0 6 ] SEE ALSO

RiProcDelayedReadArchive,RiProcRunProgram,RiProcDynamicLoad

RiProcDelayedReadArchive(RtPointerdata,RtFloatdetail )

The built-in proceduralRiProcDelayedReadArchiveoperates exactly likeReadArchive, except that the reading is delayed until the procedural primitive bounding box is reached (unlike ReadArchive, which reads RIB files immediately during parsing). The advantage of the procedural primitive is that because the reading is delayed, memory for the read primitives is not used unless (or until) the contents of the bound- ing box are actually needed.

Thedataparameter consists of a pointer to anRtStringarray. The first element of the array (that is,((RtString*)data)[0]) is the name of a RIB file to read.

The file can contain any amount of valid RIB, although it is suggested that it either be “flat” (have no hierarchy) or have some balanced hierarchy (matchingBegin-End calls). As with all RIB parameters that are bounding boxes, the boundingbox is an array of six floating-point numbers, which arexmin, xmax, ymin, ymax, zmin, zmaxin the current object space.

RIB BINDING

Procedural”DelayedReadArchive” [filename] [boundingbox]

The argument string list contains a single string giving thefilenamethe file to read when the contents of theboundingboxare needed.

EXAMPLE

RtStringargs[] ={”sodacan.rib”};

RtBoundmybound ={-1, 1, -1, 1, 0, 6};

RiProcedural((RtPointer)args, mybound,RiProcDelayedReadArchive, RiProcFree);

Procedural”DelayedReadArchive” [ ”sodacan.rib” ] [ -1 1 -1 1 0 6 ] SEE ALSO

RiProcedural,RiProcRunProgram,RiProcDynamicLoad

RiProcRunProgram(RtPointerdata,RtFloatdetail )

The built-in proceduralRiProcRunProgramwill run an external “helper program,” capturing its output and interpreting it as RIB input. The dataparameter consists of a pointer to anRtStringarray. The first element of the array is the name of the program to run (including any command line arguments), and the second element is an argument string to be written to the standard input stream of the helper program.

The helper program generates geometry on-the-fly in response to procedural prim- itive requests in the RIB stream. Each generated procedural primitive is described by a request to the helper program, in the form of an ASCII datablock that describes the primitive to be generated. This datablock can be anything that is meaningful and adequate to the helper program, such as a sequence of a few floating-point numbers, a filename, or a snippet of code in an interpreted modeling language. In addition, the renderer supplies thedetail sizeof the primitive’s bounding box, so that the generat- ing program can decide what to generate based on how large the object will appear on-screen.

The generation program reads the request datablocks on its standard input stream and emits RIB commands on its standard output stream. These RIB streams are read into the renderer as though they were read from a file (as with ReadArchive) and may include any standard RenderMan attributes and primitives (including proce- dural primitive calls to itself or other helper programs). As long as any procedural primitives exist in the rendering database that require the same helper program for processing, the socket connection to the program will remain open. This means that the program should be written with a loop that accepts any number of requests and generates a RIB “snippet” for each one.

RIB BINDING

Procedural”ReadProgram” [programname datablock] [boundingbox]

The argument list is an array of two strings. The first element is the name of the helper program to execute and may include command line options. The second element is the generation-request data block. It is an ASCII printable string that is meaningful to the helper program and describes the children to be generated. Notice that the data block is a quoted ASCII string in the RIB file, so if it is a complex description that contains quote marks or other special characters, these must be escaped in the standard way. (Similar to C, using backslash metacharacters like\”and \n.) The

boundingbox is an array of six floating-point numbers, which isxmin, xmax, ymin, ymax, zmin, zmaxin the current object space.

EXAMPLE

RtStringargs[] ={”perl gencan.pl”, ””};

RtBoundmybound ={-1, 1, -1, 1, 0, 6};

RiProcedural((RtPointer)args, mybound, RiProcRunProgram, RiProcFree);

Procedural”RunProgram” [ ”perl gencan.pl” ”” ] [ -1 1 -1 1 0 6 ]

The example above presumes that you have a Perl script calledgencan.plthat will generate RIB for a model and write that RIB to its standard output stream.

SEE ALSO

RiProcedural,RiProcRunProgram,RiProcDynamicLoad

A more efficient method for accessing subdivision routines is to write them as dy- namic shared objects (DSOs)1, anddynamically loadthem into the renderer executable at run-time. In this case, you write your subdivision and free routines in C, exactly as you would if you were writing them to be linked into the renderer using the C

RiProceduralinterface. DSOs are compiled with special compiler options to make them run-time loadable and are specified in the RIB file by the name of the shared object file. The renderer will load the DSO the first time that the subdivision routine must be called, and from then on, it is called as if (and executes as fast as if) it were statically linked. DSOs are more efficient than external programs because they avoid the overhead of interprocess communication.

When writing a procedural primitive DSO, you must create three specific public sub- routine entry points, namedSubdivide,Free, andConvertParameters.Subdivideis a standardRiProcedural()primitive subdivision routine, taking a blind data pointer to be subdivided and a floating-point detail to estimate screen size. Freeis a standard

RiProceduralprimitive free routine, taking a blind data pointer to be released.Con- vertParametersis a special routine that takes a string and returns a blind data pointer. It will be called exactly once for eachDynamicLoadprocedural primitive in the RIB file, and its job is to convert a printable string version of the progenitor’s blind data (which must be in ASCII in the RIB file), into something that theSubdivideroutine will accept.

The C prototypes for these functions are as follows:

RtPointerConvertParameters(char *initialdata); void Subdivide(RtPointerblinddata,RtFloatdetailsize); void Free(RtPointerblinddata);

Note that if the DSOSubdivideroutine wants to recursively create child procedu- ral primitives of the same type as itself, it should specify a direct recursive call to itself, withRiProcedural(newdata,newbound,Subdivide,Free),notcall itself as aDy- namicLoadprocedural. The latter would eventually just call the former after wasting time checking for and reloading the DSO.

The conventions for how dynamic objects are compiled are implementation-dependent (and also very likely OS-dependent).

RIB BINDING

Procedural”DynamicLoad” [dsoname paramdata] [boundingbox]

The argument list is an array of two strings. The first element is the name of the shared object file that contains the three required entry points and has been com- piled and prelinked as described earlier. The second element is the ASCII printable string that represents the initial data to be sent to theConvertParametersroutine. The

boundingbox is an array of six floating-point numbers, which isxmin, xmax, ymin, ymax, zmin, zmaxin the current object space.

EXAMPLE

RtStringargs[] ={”mydso.so”, ””};

RtBoundmybound ={-1, 1, -1, 1, 0, 6};

RiProcedural((RtPointer)args, mybound, RiProcDynamicLoad, RiProcFree); 1on some systems called dynamically linked libraries, or DLLs

Procedural”DynamicLoad” [ ”mydso.so” ”” ] [ -1 1 -1 1 0 6 ]

SEE ALSO

RiProcedural,RiProcDelayedReadArchive,RiProcRunProgram

5.8

Implementation-specific Geometric Primitives

In document The RenderMan Interface pdf (Page 98-103)

Related documents