The Point Cloud API

The Point Cloud API

March 2008

1   Introduction

Point cloud files can be read and written using the point cloud API. Manipulating point cloud files has many applications - for example: merging point clouds, decimating point clouds, subsurface scattering simulation, ambient occlusion computation, and so on.

Note: unorganized point clouds can be read even if they are gzipped, but organized point clouds cannot be read if they are gzipped.

1.1   API Definition

From an application program's point of view, a point cloud is just a void pointer:

typedef void *PtcPointCloud;

RMAN_POINTCLOUD_API_VERSION is #define'd in pointcloud.h and can be used to distinguish between different versions of the point cloud file API. The current version is 4.

The following procedures are used for writing point cloud files:

PtcPointCloud PtcCreatePointCloudFile (char *filename, int nvars, char **vartypes,
                                          char **varnames, float *world2eye, float *world2ndc,
                                          float *format);
PtcCreatePointCloudFile() returns a pointer (handle) to the created (unorganized) point cloud, or NULL if the creation failed. filename is the name of the file to be created. nvars describes how many variables are to be stored in the file. vartypes is an array of nvars strings, each string being one of "float", "point", "vector", "normal", "color", or "matrix". varnames is an array of nvars variable names of the stored data. world2eye is an array of 16 floats representing the 4x4 world-to-eye transformation matrix. world2ndc is an array of 16 floats representing the 4x4 world-to-NDC (normalized device coordinates) transformation matrix. format is an array of three floats: the image x resolution, the image y resolution, and the image pixel aspect ratio.
PtcPointCloud PtcCreateOrgPointCloudFile (char *filename, int npvars, char **pvartypes,
                                             char **pvarnames, int ntvars, char **tvartypes,
                                             char **tvarnames, float *world2eye, float *world2ndc,
                                             float *format);
PtcCreateOrgPointCloudFile() is used for writing organized point clouds. It returns a pointer (handle) to the created point cloud, or NULL if the creation failed. filename is the name of the file to be created. npvars describes how many variables are to be stored in the points in the file. pvartypes is an array of npvars strings, each string being one of "float", "point", "vector", "normal", "color", or "matrix". pvarnames is an array of npvars variable names of the data stored in the points. ntvars describes how many variables are to be stored in the octree nodes in the file. tvartypes is an array of ntvars strings, each string being one of "float", "point", "vector", "normal", "color", or "matrix". tvarnames is an array of ntvars variable names of the data stored in the octree nodes. world2eye is an array of 16 floats representing the 4x4 world-to-eye transformation matrix. world2ndc is an array of 16 floats representing the 4x4 world-to-NDC (normalized device coordinates) transformation matrix. format is an array of three floats: the image x resolution, the image y resolution, and the image pixel aspect ratio.
int PtcWriteDataPoint (PtcPointCloud pointcloud, float *point, float *normal,
                         float radius, float *data);
The first parameter, pointcloud, should be a point cloud pointer returned by a call to PtcCreatePointCloudFile() or PtcCreateOrgPointCloudFile(). point and normal specify the position and orientation of the data; each contain three floats. (normal can be (0,0,0) if the data points are in a volume rather than on a surface.) radius is the radius associated with the data point. data is an array of floats. The number of floats in data must correspond to the sum of sizes of the variables. The function returns 0 if the write failed (for example if the file hasn't been properly opened, if there is a system error, or if the radius is negative).
int PtcWriteTreeNode (PtcPointCloud pointcloud, int firstpoint, int npoints,
                        float *data, PtcFilePos childpos[8], PtcFilePos *filepos);
The first parameter, pointcloud, should be a point cloud pointer returned by a call to PtcCreateOrgPointCloudFile(). firstpoint specifies the index of the first point in the octree node, and npoints is the number of points in the octree node. data is an array of floats; its size must correspond to the sum of sizes of the tree variables. childpos is an array of the file positions of the eight children of this node. The positions should be 0 if the child does not exist. Since the positions of the children must be known before this call, child nodes must be written before their parent node. After the call, filepos is set to the file position this octree node was written at. The function returns 0 if the write failed (for example if the file hasn't been properly opened or if there is a system error).
void PtcFinishPointCloudFile (PtcPointCloud pointcloud);
This procedure finishes writing of the points and closes the (organized or unorganized) point cloud file.

The following procedures are available for reading an existing point cloud file:

PtcPointCloud PtcSafeOpenPointCloudFile (char *filename);
PtcSafeOpenPointCloudFile() returns a pointer to a point cloud, or NULL if the open failed. The open fails if the file doesn't exist or is not a point cloud file. The file name is specified with the variable filename.
int PtcGetPointCloudInfo (PtcPointCloud pointcloud, char *request, void *result);

This function is used to get various information from the point cloud. pointcloud is a pointer returned from a call to PtcSafeOpenPointCloudFile() (or PtcOpenPointCloudFile()). The possible requests and corresponding results are:

  • "npoints": the number of points in the point cloud file (an int).
  • "bbox": the bounding box of the point cloud (an array of six floats: Xmin, Ymin, Zmin, Xmax, Ymax, Zmax).
  • "nnodes": the number of octree nodes in the point cloud file (an int); 0 if the point cloud file is not organized.
  • "rootpos": the file position of the octree root node (a PtcFilePos).
  • "npointvars" or "nvars": the number of variables in points (an int).
  • "pointvartypes" or "vartypes": the types of the variables in points (an array of strings).
  • "pointvarnames" or "varnames": the names of the variables in points (an array of strings).
  • "pointdatasize" or "datasize": the number of float data in each data point (an int).
  • "ntreevars": the number of variables in octree nodes (an int).
  • "treevartypes": the types of the variables in octree nodes (an array of strings).
  • "treevarnames": the names of the variables in octree nodes (an array of strings).
  • "treedatasize": the number of float data in each octree node (an int).
  • "world2eye": the world-to-eye transformation matrix when the point cloud was created (16 floats).
  • "world2ndc": the world-to-NDC transformation matrix when the point cloud was created (16 floats).
  • "format": the image x resolution, y resolution, and pixel aspect ratio when the point cloud was created.
int PtcReadDataPoint (PtcPointCloud pointcloud, float *point, float *normal,
                        float *radius, float *data);
Reads the next data point (position, orientation, radius, and data values) from the point cloud file. Returns 0 if the read failed.
int PtcReadDataPoints (PtcPointCloud pointcloud, int firstpoint, int npoints,
                         PtcDataPoint *datapoints);
Reads npoints data points starting with firstpoint from the point cloud file. The positions, normals, radii, and data are written into the datapoints array. (The data associated with each point is written into the point's data array unless the data pointer is NULL.) Both datapoints and their data must have been allocated sufficiently large before the call - for example, each point's data array should have (at least) size pointdatasize * sizeof(float). The function returns 0 if the read failed.
int PtcReadTreeNode (PtcPointCloud pointcloud, PtcFilePos filepos, int *firstpoint,
                       int *npoints, float bbox[6], float *data,
                       PtcFilePos childpos[8]);
Reads the octree node at file position filepos. The values of firstpoint, npoints, and bbox are filled in. The octree node data are written into the data array (unless the array is NULL). (The data array must be allocated sufficiently large before the call.) The file positions of the octree node's (up to) eight children are written into the childpos array.
int PtcReadTreeNodes (PtcPointCloud pointcloud, PtcFilePos filepos[8],
                        PtcTreeNode nodes[8]);
Reads the octree nodes at the file positions in filepos. The (up to 8) octree nodes must be children of the same node; some of them can be 0. Fills in data in the nodes that correspond to non-zero filepos values. Calling this function once leads to more efficient cache and disk accesses than calling PtcReadTreeNode() repeatedly.
int PtcGetNearestPointsData (PtcPointCloud pointcloud, float *point, float *normal,
                                float maxdist, int numpoints, float *data);
Performs a lookup at the given point and normal. The result is interpolated from up to numpoints data points in the point cloud file at a distance of up to maxdist from the lookup point. Returns 1 on success, 0 on failure. All the points are read in and organized in memory. A warning will be printed if the point cloud file was already organized, but the in-memory organization and lookups will proceed. (This function can't utilize the organization in an organized point cloud file since that requires a point cache, which in turn requires a rendering context - which the point cloud API functions don't provide.)
void PtcClosePointCloudFile (PtcPointCloud pointcloud);
Closes the point cloud file. After this call the pointcloud pointer is no longer valid.

All these API function prototypes are defined in the header file pointcloud.h. Compile and link with the following library (in PRMan 14.0 and higher):

libprman.so/.dylib/.dll

Platform-specific linking instructions can be found in the RenderMan SDK documentation.


2   API Examples

2.1   ptmerge.c

To illustrate the point cloud API, we show a program that uses it. The program reads one or more point cloud files (with the same data types) and writes their content out as a single point cloud file.

/*
 * ptmerge.c
 * This program demonstrates how to use the pointcloud API to read
 * and write points from/to point cloud files.
 *
 * The program reads some point cloud files and writes their content
 * out as a single point cloud file.
 */

#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "math.h"
#include "assert.h"

#include "pointcloud.h"


int
main(int argc, char *argv[]) {
    PtcPointCloud *inptcs = NULL; /* an array of PtcPointClouds (pointers) */
    PtcPointCloud outptc = NULL;
    float w2e[16], w2n[16], format[3];
    float point[3], normal[3];
    float radius, ``*data``;
    int nInFiles = argc-2, f, v;
    int datasize, nvars, nv;
    int npoints, p;
    char **vartypes = NULL, **varnames = NULL, **vt = NULL, **vn = NULL; // arrays of strings
    char *inname, *outname;

    if (argc < 3) {
      fprintf(stderr, "ptmerge error: needs at least one input file and an output\n");
      exit(1);
    }

    inptcs = (PtcPointCloud *) malloc(nInFiles * sizeof(PtcPointCloud));

    /* Open the first input files to determine data types */
    inname = argv[1];
    inptcs[0] = PtcSafeOpenPointCloudFile(inname);
    if (!inptcs[0]) {
      fprintf(stderr, "ptmerge error: unable to open input file %s\n",
              inname);
      exit(1);
    }

    PtcGetPointCloudInfo(inptcs[0], "nvars", &nvars);
    PtcGetPointCloudInfo(inptcs[0], "vartypes", &vartypes);
    PtcGetPointCloudInfo(inptcs[0], "varnames", &varnames);

    /* Open following input files (if any) and make sure they have the
       same data types as the first */
    for (f = 1; f < nInFiles; f++) {
      inname = argv[f+1];
      inptcs[f] = PtcSafeOpenPointCloudFile(inname);
      if (!inptcs[f]) {
          fprintf(stderr, "ptmerge error: unable to open input file %s, skipping it.\n",
                  inname);
          continue;
      }

      PtcGetPointCloudInfo(inptcs[f], "nvars", &nv);
      PtcGetPointCloudInfo(inptcs[f], "vartypes", &vt);
      PtcGetPointCloudInfo(inptcs[f], "varnames", &vn);

      if (nv != nvars) {
          fprintf(stderr, "ptmerge error: input files differ number of vars\n");
          exit(1);
      }
      for (v = 0; v < nvars; v++) {
          if (strcmp(vartypes[v], vt[v])) {
              fprintf(stderr, "ptmerge error: input files differ in data types: %s vs %s\n",
                      vartypes[v], vt[v]);
              exit(1);
          }
          if (strcmp(varnames[v], vn[v])) {
              fprintf(stderr, "ptmerge error: input files differ in data names: %s vs %s\n",
                      varnames[v], vn[v]);
              exit(1);
          }
      }
    }

    /* Get transformation matrices and image format from first file */
    PtcGetPointCloudInfo(inptcs[0], "world2eye", w2e);
    PtcGetPointCloudInfo(inptcs[0], "world2ndc", w2n);
    PtcGetPointCloudInfo(inptcs[0], "format", format);

    /* Create output file with the same data types as input file(s) */
    outname = argv[argc - 1];
    outptc = PtcCreatePointCloudFile(outname, nvars, vartypes, varnames,
                                   w2e, w2n, format);
    if (!outptc) {
      fprintf(stderr, "Unable to open output file %s.\n", outname);
      exit(1);
    }

    PtcGetPointCloudInfo(inptcs[0], "datasize", &datasize);
    data = (float *) malloc(datasize * sizeof(float));

    /* Loop over the input files, reading all points and writing them to
       the output file. */
    for (f = 0; f < nInFiles; ++f) {
      PtcGetPointCloudInfo(inptcs[f], "npoints", &npoints);
      inname = argv[f+1];
      printf("input file '%s' has %i points\n", inname, npoints);
      for (p = 0; p < npoints; p++) {
          PtcReadDataPoint(inptcs[f], point, normal, &radius, data);
          PtcWriteDataPoint(outptc, point, normal, radius, data);
      }
      PtcClosePointCloudFile(inptcs[f]);
    }

    free(inptcs);
    free(data);

    /* Finish writing the point cloud data and close files */
    PtcFinishPointCloudFile(outptc);

    printf("merged file '%s' written\n", outname);

    return 0; /* success */
}

The ptmerge program can be invoked with:

ptmerge infile1.ptc infile2.ptc ... outfile.ptc

2.2   Subsurface Scattering

As another example of how to use the point cloud API, we are here providing source code for subsurface scattering simulation. (This is the same computation done by ptfilter -filter ssdiffusion and the subsurface() function.)

The main entry point is the function SSDiffusion(), which does the following:

  1. Reads in the points in the input point cloud file(s).
  2. Builds an octree.
  3. Computes the diffusion approximation for each point.
  4. Writes out a new point cloud.

The source code in C++ is listed here: ssdiffusion.cpp. This code is provided "as is"; use at your own risk. For an explanation of the algorithm, data structures, greek symbols, etc., please refer to the Translucency and Subsurface Scattering application note and the three papers listed in it.