In addition to the display drivers supplied with PRMan, users can extend the system by writing their own custom drivers. This allows for new image or geometry types or framebuffers to be directly supported for rendering.
Display drivers are written as dynamic shared objects (DSOs), which are also known as dynamic link libraries. (You should consult the documentation for your compiler on how to compile and link these objects.) You may compile your display DSOs either with a 'C' or 'C++' compiler, but you must use 'C' linkage (i.e. if you use C++, you need to use extern "C" to guarantee C-style linkage). A header file has been provided, ndspy.h, which contains function prototype declarations that should be used when writing display drivers, as these prototypes should guarantee that the correct linking and exporting of symbols occurs across various platforms.
See the RenderMan SDK documentation for details on compiling and linking these plug-ins.
Display driver DSOs must export and implement at least four required functions:
DspyImageOpen
DspyImageQuery
DspyImageData
DspyImageClose
Optionally, they may also implement:
DspyImageDelayClose
DspyImageDeepData
DspyImageActiveRegion
Data to the display drivers will be provided in a variety of formats and byte orders. The display driver can then choose which formats and byte orders it would prefer that data in. Constants for those formats are defined in ndspy.h. The names should be self explanatory: PkDspyFloat32, PkDspyUnsigned32, PkDspySigned32, PkDspyUnsigned16, PkDspySigned16, PkDspyUnsigned8 and PkDspySigned8.
These types are defined for optional parameters: PkDspyString, PkDspyMatrix.
If byte orders aren't specified, the machine default is assumed. Byte order can be specified by "or"-ing in one of two values:
PkDspyByteOrderHiLo
big endian, most significant bytes come first
PkDspyByteOrderLoHi
little endian, least significant bytes come first
PkDspyByteOrderNative
just an alias for whichever is the native order on this architecture
All functions return one of the following enumerated error codes:</p>
PkDspyErrorNone
successful completion
PkDspyErrorNoMemory
unable to allocate memory
PkDspyErrorUnsupported
unsupported operation requested
PkDspyErrorBadParams
bad parameters
PkDspyErrorNoResource
no resource available, file not found, etc.
PkDspyErrorUndefined
no other error messages appropriate
PtDspyError DspyImageOpen(PtDspyImageHandle * image, const char *drivername, const char *filename, int width, int height, int paramCount, const UserParameter *parameters, int formatCount, PtDspyDevFormat *format, PtFlagStuff *flagstuff);
This opens an image called filename and puts a handle to any local data into *image. If width or height is 0, it chooses an appropriate default value for them.
drivername is the name before the /display/dso or /display/dsomapping translation. This allows a single driver to change its behavior based on the name used in the RIB file.
Image types that can include various other tag information might investigate the parameters, which are passed through from the RIB Display line. A list of the commonly interpreted parameters appears later in this document. Two important points:
formatCount and format describe the data as it will be sent to the driver. Display drivers should reorder the format array into the order it expects to deal with data (it is important that the format.name fields point to the original strings!), and set format[x].type to types and byte orders that it can deal with.
Geometry drivers (those that have responded to the PkPointCloudQuery) will be supplied information about the format of the extended data they will receive in DspyImageDeepData. The format.name field will contain data types and names formatted with the syntax:
(uniform|varying) float|point|vector|normal|color|matrix name.XXX
The trailing .XXX information exists to ensure a unique channel * identifier, and can be ignored.
Finally, flagstuff allows the driver to request that data be sent in different ways. Three flags can be "OR"-ed into flagstuff->flags:
PkDspyFlagsWantsScanLineOrder
Specifies that that data be sent only in scanline order. If this flag is not set, then calls to DspyImageData may provide data for random portions of the image. While this is fine for image buffers and other drivers which already have data for the entire image allocated, it makes it difficult to build drivers for formats like TIFF, which want the data a scan line at a time. Setting this bit ensures that all data will arrive in scan line order. Note that this does not mean that your ``DspyImageData`` function will get a full scanline at a time, it will get portions of scanlines. If you have to process full scanlines (for channel separated RLE, for instance) you'll need to wait for the complete scanline to arrive.
PkDspyFlagsWantsEmptyBuckets
If PkDspyFlagsWantsScanLineOrder isn't set, then not all of the image will necessarily be filled in. Areas of the image in which there is no geometry may be left unfilled. Setting this flag makes the renderer send the highest possible value for the data type in any channel labeled "z", 0 in all other channels, for those regions which otherwise wouldn't get data.
PkDspyFlagsWantsNullEmptyBuckets
Much like PkDspyFlagsWantsEmptyBuckets, but this flag calls DspyImageData with NULL in the data argument for those regions.
If you don't use PkDspyFlagsWantsScanLineOrder the renderer won't call DspyImageData for regions of the image where it doesn't draw anything. Don't assume that the entire image (or even any of the image, if none of the geometry is visible) will be filled in.
This example will implement a trivial image format. It makes no allowances for differences between platforms. The format is just an "int" that is the number of channels being sent to this image, that many strings, which name them all, and that many floating point values per pixel for the rest of the image.
#include <ndspy.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { FILE *file; int channels; int width, height; } *MyImageType; PtDspyError DspyImageOpen(PtDspyImageHandle *pvImage, const char *drivername, const char *filename, int width, int height, int paramCount, const UserParameter *parameters, int formatCount, PtDspyDevFormat *format, PtFlagStuff *flagstuff) { PtDspyError ret; MyImageType image; /* We want to receive the pixels one after the other */ flagstuff->flags |= PkDspyFlagsWantsScanLineOrder; /* Do stupidity checking */ if (0 == width) width = 640; if (0 == height) height = 480; image = NULL; ret = PkDspyErrorNone; image = (MyImageType) malloc(sizeof(*image)); if (NULL != image) { int i; image->channels = formatCount; image->width = width; image->height = height; image->file = fopen(filename, "wb"); if (image->file) { fwrite(&formatCount, sizeof(formatCount), 1, image->file); for (i = 0; i < formatCount; i++) { format[i].type = PkDspyFloat32; fwrite(format[i].name, strlen(format[i].name) + 1, 1, image->file); } } else { free(image); image = NULL; ret = PkDspyErrorNoResource; } } else ret = PkDspyErrorNoMemory; *pvImage = image; return ret; }
PtDspyError DspyImageQuery(PtDspyImageHandle pvImage, PtDspyQueryType type, size_t size, void *p);
DspyImageQuery is used to communicate state information about the features of the display driver and the image the display driver can produce. Each query is made by calling DspyImageQuery with a query-type and corresponding query-structure passed as p. DspyImageQuery fills values in the query-structure and returns a value indicating the sucess or failure of the query.
The following query-types may be passed to DspyImageQuery:
PkSizeQuery
Asks for the size of the image, or a default size of no image is supplied (pvImage may be NULL!). It will be called with an image handle when the DspyImageOpen was called with a 0 width and height. It will also be called without an image handle for applications which want to query for a fixed image size (as might happen with a framebuffer).
PkOverwriteQuery
Asks whether the driver in question actually overwrites the file name given, and is used to keep the user from accidentally overwriting an image. The response structure, PtDspyOverwiteInfo, has two fields. The .overwrite field tries to keep the user from doing damage when using tools like sho for image conversion, or when writing to the "framebuffer" display device which may be mapped to something destructive. The .interactive field isn't yet used in any tools, the hope was that it would make batch tools a little bit smarter.
PkSupportsCheckpointing
Asks whether the display driver supports image checkpointing. Image checkpointing allows the render to skip a portion of a render if the "-recover 1" option is passed to render. The PkSupportsCheckpointing is first issued by the render to all display drivers that are used in the render. If all respond positively to the PkSupportsCheckpointing query, the display drivers are initialized with DspyImageOpen, and the PkRenderingStartQuery is issued to determine where restart the render. PkSupportsCheckpointing is issued with a NULL pvImage. Any data that needs to be communicated to the display driver before the DspyImageOpen method needs to be initialized upon receiving PkSupportsCheckpointing. If the display driver supports image checkpointing, DspyImageOpen needs to determine where the render should start.
PkPointCloudQuery
Asks whether the display driver supports geometry (can be used by the bake3d() shadeop). A display driver that sets the supportsPointsClouds field of the PtDspyPointCloudQuery to 1 indicates that it has implemented the DspyImageDeepData routine and is able to handle a collection of point data. The driver will be supplied information about the format of this point data in the DspyImageOpen routine.
PkGridQuery
Asks whether the display driver supports geometry with topological information (can be used by the bake3d() shadeop). A driver that supports this query must also support the PkPointCloudQuery and set the supportsPointClouds field therein to 1. A display driver that sets the supportsGrids field of the PtDspyGridQuery to 1 indicates that it has implemented the DspyImageDeepData routine and is able to handle a collection of point data along with topological information. The driver will be supplied information about the format of this point data in the DspyImageOpen routine.
PkMultiResolutionQuery
Asks whether the display driver supports multi-resolution images. A driver that supports this query must set the supportsMultiResolution field therein to 1. A multi-resolution display driver will receive multiple DspyImageOpen calls, one for each level of detail. The first is the highest resolution. Subsequent calls will have ":lod:#" appended to the image name, where # is an integer indicating the level of detail. 1 is the first decimation, 2 the second, and so on.
PkMemoryUsageQuery
Asks the display driver for its current memory usage. This query is made with a valid pvImage, indicating the target of the query. The response structure, PtMemoryUsageQuery, has two fields. The .currentkB field should be set to the amount of memory the driver currently has allocated for the given pvImage, in kilobytes (1024 bytes). The .peakkB field should be set to the peak amount of memory the driver has allocated during execution for the given pvImage. The results are aggregated across displays and presented as "Display driver memory" in the "Memory usage summary" section of the statistics.
PkElapsedTimeQuery
Asks the display driver for the elapsed time processing an image. This query is made with a valid pvImage, indicating the target of the query. The response structure, PtElapsedTimeQuery has a single field, .seconds. This field should be set to the elapsed time, in seconds, that the driver has spend processing the given pvImage.The results are aggregated across displays and presented as "Display driver time" in the "Time summary" section of the statistics.
PkRenderingStartQuery
Asks where the display driver can recover to. After issuing a PkSupportsCheckpointing query, the render knows if it is possible to recover. The render needs to know how far along in the image the display driver can recover. The PtDspyRenderingStartQuery return structure indicates, in image space, where the display driver contains its last piece of valid image data. The render will compile all of the rendering start locations from all the display drivers to determine where to start rendering. PkRenderingStartQuery is called with a valid pvImage so any information that needs to be passed to the DspyImageData needs to be initialized here. If the display driver supports image checkpointing, the display driver must ignore all data passed to DspyImageData that is less than the value returned in the PtDspyRenderingStartQuery structure.
PtDspyError DspyImageQuery(PtDspyImageHandle pvImage, PtDspyQueryType querytype, int datalen, void *data) { PtDspyError ret; MyImageType image = (MyImageType )pvImage; ret = PkDspyErrorNone; if (datalen > 0 && NULL != data) { switch (querytype) { case PkOverwriteQuery: { PtDspyOverwriteInfo overwriteInfo; if (datalen > sizeof(overwriteInfo)) datalen = sizeof(overwriteInfo); overwriteInfo.overwrite = 1; overwriteInfo.interactive = 0; memcpy(data, &overwriteInfo, datalen); break; } case PkSizeQuery : { PtDspySizeInfo sizeInfo; if (datalen > sizeof(sizeInfo)) { datalen = sizeof(sizeInfo); } if (image) { if (0 == image->width || 0 == image->height) { image->width = 640; image->height = 480; } sizeInfo.width = image->width; sizeInfo.height = image->height; sizeInfo.aspectRatio = 1.0f; } else { sizeInfo.width = 640; sizeInfo.height = 480; sizeInfo.aspectRatio = 1.0f; } memcpy(data, &sizeInfo, datalen); break; } case PkRenderingStartQuery : { PtDspyRenderingStartQuery startLocation; if (datalen > sizeof(startLocation)) datalen = sizeof(startLocation); if (image) { /* * initialize values in startLocation */ memcpy(data, &startLocation, datalen); } else { ret = PkDspyErrorUndefined; } break; } default : ret = PkDspyErrorUnsupported; break; } } else { ret = PkDspyErrorBadParams; } return ret; } PtDspyError DspyImageData(PtDspyImageHandle pvImage, int xmin, int xmax_plusone, int ymin, int ymax_plusone, int entrysize, const unsigned char *data) { PtDspyError ret; MyImageType image = (MyImageType )pvImage; int oldx; oldx = xmin; for (;ymin < ymax_plusone; ymin++) { for (xmin = oldx; xmin < xmax_plusone; xmin++) { fwrite(data, sizeof(float), image->channels, image->file); data += entrysize; } } return ret; } PtDspyError DspyImageClose(PtDspyImageHandle pvImage) { PtDspyError ret; MyImageType image = (MyImageType )pvImage; fclose(image->file); free(image); ret = PkDspyErrorNone; return ret; }
PtDspyError DspyImageData(PtDspyImageHandle image, int xmin, int xmax_plusone, int ymin, int ymax_plusone, int entrysize, const unsigned char *data);
Write data for the image area described. entrysize is the skip in bytes to the next pixel in data. Unlike the old dspy system, xmax_plusone and ymax_plusone are 1 more than the last pixel to be written; that is, the width of the region to be written is xmax_plusone-xmin and not xmax-xmin+1.
PtDspyError DspyImageData(PtDspyImageHandle pvImage, int xmin, int xmax_plusone, int ymin, int ymax_plusone, int entrysize, const unsigned char *data) { PtDspyError ret; MyImageType image = (MyImageType )pvImage; int oldx; oldx = xmin; for (;ymin < ymax_plusone; ymin++) { for (xmin = oldx; xmin < xmax_plusone; xmin++) { fwrite(data, sizeof(float), image->channels, image->file); data += entrysize; } } }
PtDspyError DspyImageActiveRegion(PtDspyImageHandle image, int xmin, int xmax_plusone, int ymin, int ymax_plusone);
During display of a multi-resolution image the display driver is responsible for scaling the low resolution images before they are displayed in a framebuffer. During re-rendering, the user may specify a crop window other than the one used to originally define the image; we call this an active region. Only buckets within the active region will be sent to the display driver. Active regions are specified in units of pixels in the highest-resolution image; therefore, the boundary of the region may cross through an up-scaled pixel of a lower resolution image. To ensure display of the multi-resolution does not affect pixels outside of the active region the display driver must clip the up-scaled images. DspyImageActiveRegion will be called every time a new region is defined, which may happen multiple times during a re-rendering session. Like DspyImageData, xmax_plusone and ymax_plusone are 1 more than the last pixel to be written, and the width of the region to be written is xmax_plusone-xmin.
PtDspyError DspyImageDeepData(PtDspyImageHandle image, int xmin, int xmax, /* npoints, 0 */ int ymin, int ymax, /* ignored (0) */ char *data, /* filename and point data */ int totalsize, /* total size of all data (in bytes) */ int *pixeloffsets, /* (ignored) */ int *pixelsizes) /* (ignored) */
This entry point is used by geometry drivers (those useable by bake3d()). A geometry driver will receive a list of points with associated data. The points are homogeneous, ie. each point * has the same amount of data. If the driver indicates support for the PkPointCloudQuery only, each point in the input (*data) consists of a position (3 floats), a normal (3 floats), a radius (1 float), and shader specified data (any number of floats), with the last being described in the DspyImageOpen routine. An example of such usage is:
typedef struct { FILE *file; int nvars; char **varnames; int *varsizes; } *PtDspyImage; /*ARGSUSED*/ PtDspyError DspyImageOpen(PtDspyImageHandle *pvImage, const char *drivername, const char *filename, int width, int height, int paramCount, const UserParameter *parameters, int formatCount, PtDspyDevFormat *format, PtFlagStuff *flagstuff) { PtDspyError ret = PkDspyErrorNone; PtDspyImage image; int i, v; int words; char mode[80], type[80], name[256]; image = (PtDspyImage) calloc(1, sizeof(*image)); if (!image) { ret = PkDspyErrorNoMemory; goto error; } image->nvars = formatCount; image->varsizes = (int *) malloc(formatCount * sizeof(int)); image->varnames = (char**) calloc(formatCount, sizeof(char*)); if (!image->varsizes || !image->varnames) { ret = PkDspyErrorNoMemory; goto error; } /* * We require data in this machine's native byte order, so modify * to inform the renderer of this fact. */ for (i = 0; i < formatCount; i++) { format[i].type = (format[i].type & ~PkDspyMaskOrder) | PkDspyByteOrderNative; } /* Look at filename, make sure it's not null. If it is, override */ if (filename == NULL || *filename == '\0') { filename = "ri.ptc"; /* default must be display specific */ } image->file = fopen(filename, "w+b"); if (!image->file) { ret = PkDspyErrorNoResource; goto error; } /* * Extract data types and names from the format information. This * will come in on the format[v].name parameter with the syntax: * * (uniform|varying) float|point|vector|normal|color|matrix name.XXX * * The trailing .XXX information exists to ensure a unique channel * identifier, and can be ignored. */ for (v = 0; v < formatCount; v++) { /* Skip file name */ char* typename = strchr(format[v].name, '|'); if (!typename) { ret = PkDspyErrorUndefined; goto error; } typename++; words = sscanf(typename, "%s %s %s", mode, type, name); if (words == 2) words = sscanf(typename, "%s %s", type, name); for (i = 0; i < strlen(name); i++) /* find first '.' in name */ if (name[i] == '.') break; image->varnames[v] = (char*) malloc(strlen(name)+1); if (!image->varnames[v]) { goto error; } if (strlen(type) == 0) { DspyError(drivername, "Bad type declaration - missing DisplayChannel?\n"); ret = PkDspyErrorBadParams; goto error; } /* Deduce data size */ if (strcmp(type, "float") == 0) { image->varsizes[v] = 1; } else if (strcmp(type, "point") == 0 || strcmp(type, "vector") == 0 || strcmp(type, "normal") == 0 || strcmp(type, "color") == 0) { image->varsizes[v] = 3; } else if (strcmp(type, "matrix") == 0) { image->varsizes[v] = 16; } else { printf("error: unknown variable type '%s' in pointcloud display driver\n", type); /* * Let's just hope that a varsize of 1 will work, better * than leaving this uninitialized. */ image->varsizes[v] = 1; } strncpy(image->varnames[v], name, i); /* clip off ".x" */ image->varnames[v][i] = '\0'; } *pvImage = image; return ret; error: if (image) { if (image->file) fclose(image->file); if (image->varsizes) free(image->varsizes); if (image->varnames) { for (i = 0; i < image->nvars; ++i) { if (image->varnames[i]) { free(image->varnames[i]); } } free(image->varnames); } free(image); } *pvImage = NULL; return ret; } PtDspyError DspyImageDeepData(PtDspyImageHandle pvImage, int xmin, int xmax, /* npoints, 0 */ int ymin, int ymax, /* ignored (0) */ char *data, /* filename and point data */ int totalsize, /* total size of all data (in bytes) */ int *pixeloffsets, /* (ignored) */ int *pixelsizes) /* (ignored) */ { PtDspyImage image = (PtDspyImage )pvImage; float *fdata = (float *)data; float *ptdata; int npoints = xmax - xmin + 1; int i, j, k; int fsize = totalsize / (npoints * sizeof(float)); fprintf(image->file, "Number of points: %d\n", npoints); /* * Pry point positions, normals, radii and data out of the * "unorganized" data list. */ for (i = 0; i < npoints; i++) { ptdata = fdata; /* Position */ fprintf(image->file, "point: %f %f %f\n", ptdata[0], ptdata[1], ptdata[2]); ptdata += 3; /* Normal */ fprintf(image->file, "normal: %f %f %f\n", ptdata[0], ptdata[1], ptdata[2]); ptdata += 3; /* Radius */ fprintf(image->file, "radius: %f\n", ptdata[0]); ptdata++; /* Rest of the data */ for (j = 0; j < image->nvars; ++j) { fprintf(image->file, "%s: ", image->varnames[j]); for (k = 0; k < image->varsizes[j]; ++k) { if (k == image->varsizes[j] - 1) { fprintf(image->file, "%f", *ptdata++); } else { fprintf(image->file, "%f ", *ptdata++); } } fprintf(image->file, "\n"); } fdata += fsize; } fprintf(image->file, "\n"); return PkDspyErrorNone; }
If the driver indicates support for the PkGridQuery as well, the data passed to the driver has the following format:
1 float - style: enum PtDspyGridStyle {PkDspyGridConnected, PkDspyGridLines, PkDspyGridPoints} 1 float - npoints: number of vertices 1 float - npolys: number of polys 1 float - fsize: size of per-vertex data npoints * fsize floats - per vertex data // this is the data as what we would have gotten in the // case where only PkPointCloudQuery was supported. npolys floats: per poly vertex count sum(per poly vertex count) floats - per poly vertex indices
In cases where the style is Lines or Points, the last two fields are not sent and npolys == 0. Otherwise, those fields contain topological information in the same style as style of RiPointsPolygons (i.e. polygon vertex counts followed by polygon vertex indices). Example usage (from the RIB driver) is:
/*ARGSUSED*/ PtDspyError DspyImageDeepData(PtDspyImageHandle pvImage, int xmin, int xmax, /* npoints, 0 */ int ymin, int ymax, /* ignored (0) */ char *data, /* filename and point data */ int totalsize, /* total size of all data (in bytes) */ int *pixeloffsets, /* (ignored) */ int *pixelsizes) /* (ignored) */ { PtDspyImage image = (PtDspyImage )pvImage; // Save renderer context RtContextHandle renderContext = RiGetContext(); // Switch to RIB context RiContext(image->context); RiAttributeBegin(); float *fdata = (float *)data; int style = (int) *fdata++; int npoints = (int) *fdata++; int npolys = (int) *fdata++; int fsize = (int) *fdata++; fsize /= sizeof(float); int i, j, k, offset; // We'll need a buffer to store data to present to the RI interface // in uninterleaved form RtToken *tokens = (RtToken*) malloc((4 + image->nvars) * sizeof(RtToken)); RtPointer *values = (RtPointer*) malloc((4 + image->nvars) * sizeof(RtPointer)); RtFloat *ridata = (RtFloat*) malloc(fsize * npoints * sizeof(RtFloat)); RtFloat *ridataptr = ridata; if (!tokens || !values || !ridata) { RiContext(renderContext); return PkDspyErrorNoMemory; } offset = 0; ridataptr = ridata; // Positions tokens[0] = RI_P; values[0] = ridataptr; float *ptdata = fdata; for (i = 0; i < npoints; i++) { *ridataptr++ = ptdata[0]; *ridataptr++ = ptdata[1]; *ridataptr++ = ptdata[2]; ptdata += fsize; } offset += 3; // Normals tokens[1] = RI_N; values[1] = ridataptr; ptdata = fdata + offset; for (i = 0; i < npoints; i++) { *ridataptr++ = ptdata[0]; *ridataptr++ = ptdata[1]; *ridataptr++ = ptdata[2]; ptdata += fsize; } offset += 3; // Radii - multiplied by two to become width tokens[2] = "varying float width"; values[2] = ridataptr; ptdata = fdata + offset; for (i = 0; i < npoints; i++) { *ridataptr++ = 2.0f * ptdata[0]; ptdata += fsize; } offset += 1; // Rest of the data for (j = 0; j < image->nvars; ++j) { tokens[j + 3] = (RtToken) malloc(strlen("varying") + strlen(image->typenames[j]) + strlen(image->varnames[j]) + 3); if (!tokens[j+3]) { RiContext(renderContext); return PkDspyErrorNoMemory; } sprintf(tokens[j + 3], "varying %s %s", image->typenames[j], image->varnames[j]); values[j + 3] = ridataptr; ptdata = fdata + offset; for (i = 0; i < npoints; i++) { for (k = 0; k < image->varsizes[j]; ++k) { *ridataptr++ = ptdata[k]; } ptdata += fsize; } offset += image->varsizes[j]; } if (style == PkDspyGridConnected) { RtFloat* trimdata = (RtFloat*) malloc(npoints * sizeof(RtFloat)); if (!trimdata) { for (j = 0; j < image->nvars; ++j) { free(tokens[j+3]); } free(tokens); free(values); free(ridata); return PkDspyErrorNoMemory; } RtInt *nvertices = (RtInt*) malloc(npolys * sizeof(RtInt)); if (!nvertices) { RiContext(renderContext); return PkDspyErrorNoMemory; } // Skip ahead to the face information fdata = ((float*) data) + 4 + npoints * fsize; int vsize = 0; for (i = 0; i < npolys; ++i) { nvertices[i] = (RtInt) (*fdata++); vsize += nvertices[i]; } RtInt *vertices = (RtInt*) malloc(vsize * sizeof(int)); if (!vertices) { RiContext(renderContext); return PkDspyErrorNoMemory; } RtInt *verticeptr = vertices; for (i = 0; i < npolys; ++i) { for (j = 0; j < nvertices[i]; ++j) { *verticeptr++ = (RtInt) (*fdata++); } } // Output trim information tokens[image->nvars + 3] = "uniform float __needstrim"; values[image->nvars + 3] = trimdata; for (i = 0; i < npolys; ++i) { trimdata[i] = (RtFloat) (*fdata++); } RiPointsPolygonsV(npolys, nvertices, vertices, image->nvars + 4, tokens, values); free(nvertices); free(vertices); free(trimdata); } else if (style == PkDspyGridPoints) { RiPointsV(npoints, image->nvars + 3, tokens, values); } else if (style == PkDspyGridLines) { RiCurvesV(RI_LINEAR, 1, &npoints, RI_NONPERIODIC, image->nvars + 3, tokens, values); } RiAttributeEnd(); // Switch back to renderer context RiContext(renderContext); for (j = 0; j < image->nvars; ++j) { free(tokens[j+3]); } free(tokens); free(values); free(ridata); return PkDspyErrorNone; }
PtDspyError DspyImageClose(PtDspyImageHandle);
Simply free any resources associated with the image handle.
PtDspyError DspyImageClose(PtDspyImageHandle pvImage) { PtDspyError ret; MyImageType image = (MyImageType )pvImage; fclose(image->file); free(image); ret = PkDspyErrorNone; }
PtDspyError DspyImageDelayClose(PtDspyImageHandle);
There are situations where a display manager might want to keep running after the renderer has finished sending data. For instance, a driver which displays images in a window may want to keep that window available for user manipulation. This function is the perfect place to put a display loop; simply don't return until the window is closed.
If this function exists in the display driver, then the display driver will run in a separate process from the main renderer and the renderer itself will exit as soon as the image is complete. This is important for two reasons:
In order to use the shared object, the renderer must be able to find it. When you pass a format name in via the type (second) parameter to RiDisplay:
Common usage is to put the driver into ${RMANTREE}/etc/ named d_*type*.so or d_*type*.dll.
An application linked to prman may register a display driver directly rather than through a shared library. This permits the driver to run in the same process as the application. The DspyRegisterDriverTable function takes a versioned struct filled with pointers to display driver functions.
PtDspyError DspyRegisterDriverTable(const char *name, const PtDspyDriverFunctionTable *pTable);
For example:
PtDspyDriverFunctionTable dt; dt.Version = k_PtDriverCurrentVersion; dt.pOpen = DirectDspyImageOpen; dt.pWrite = DirectDspyImageData; dt.pClose = DirectDspyImageClose; dt.pQuery = DirectDspyImageQuery; dt.pActiveRegion = DirectDspyImageActiveRegion; int err = DspyRegisterDriverTable("direct", &dt);
This will register a driver that can be loaded by using "direct" as format name in the type(second) parameter to RiDisplay. DspyRegisterDriverTable replaces the deprecated DspyRegisterDriver.
There are a set of basic functions that nearly every image implementation needs to do. Many of these have to do with accessing parameters, which are either predefined or passed through from the RIB Display line. These are automatically made available by the renderer, and do not require linking with any further files.
PtDspyError DspyFindStringInParamList(const char *string, char **result, int paramCount, const UserParameter *parameters)
If string is found in parameters, put a pointer to it in *result and return PkDspyErrorNone, else return PkDspyErrorNoResource.
char *result = NULL; DspyFindStringInParamList("secondchoice", &result, paramCount, parameters); DspyFindStringInParamList("firstchoice", &result, paramCount, parameters); if (result) { ...
void DspyMemReverseCopy(unsigned char *target, const unsigned char *source, int len);
Reverse len bytes from source to target.
PtDspyError DspyFindMatrixInParamList(const char *string, float *result, int paramCount, UserParameter *parameters)
Example:
float matrix[16]; if (DspyFindMatrixInParamList("NP", matrix, paramCount, parameters)) { ...
PtDspyError DspyFindFloatInParamList(const char *string, float *result, int paramCount, const UserParameter *parameters)
Get a floating point value from the parameter list. Here's an example of getting the near clipping plane from the parameter list:
float nearClip; DspyFindFloatInParamList("near", &nearClip, paramCount, parameters);
PtDspyError DspyFindFloatsInParamList(const char *string, int *resultCount, float *result, int paramCount, const UserParameter *parameters)
Get a floating point array from the parameter list. Similar to DspyFindFloatInParamList except that *resultCount is the maximum number of floats available at result, and is set to the number actually copied into result.
PtDspyError DspyFindIntInParamList(const char *string, int *result, int paramCount, const UserParameter *parameters)
Get an integer value from the parameter list. Used similarly to DspyFindFloatInParamList().
PtDspyError DspyFindIntsInParamList(const char *string, int *resultCount, int *result, int paramCount, const UserParameter *parameters)
Get an integer value from the parameter list. Used similarly to DspyFindFloatsInParamList().
PtDspyError DspyReorderFormatting(int formatCount, PtDspyDevFormat *format, int outFormatCount, const PtDspyDevFormat *outFormat)
Attempt to reorder format to look like outFormat. We can't save users from themselves, but if we expect to get "abgr", we can use this to try to order the incoming parameters to look more like "abgr" than "rgba" or whatever other random format our caller may request.
Obviously users can provide any set of parameters they want. The renderer will also provide a standard set of parameters:
Name | Type | Description |
---|---|---|
NP | matrix | world to screen space |
Nl | matrix | world to camera space |
near | float | near clipping plane (useful for scaled Z buffer formats) |
far | float | far clipping plane |
origin | int[2] | with crop windows provides the origin of this image within the larger image, in pixels. |
OriginalSize | int[2] | with crop windows provides the size of the larger image into which this image fits. |
PixelAspectRatio | float | pixel aspect ratio |
Software | string | renderer version |
HostComputer | string | host name |
Pixar Animation Studios
|