This module allows reading and writing RenderMan point cloud files. The module relies on an external shared library that implements the actual low-level access to the point cloud file. This library is not part of cgkit but must be provided by the renderer package that you are using (for example, PRMan or 3Delight). Without such a library you won’t be able to read or write any point cloud file using this module.
The module provides one single function open() which opens a point cloud file for reading or writing.
Open a point cloud file for reading or writing.
fileName is the name of the point cloud file. mode is either "r" for reading a file or "w" for writing a new point cloud file. libName is the library name that implements the point cloud API. When mode is "w", the following additional keyword arguments must be present:
Depending on the mode, the function either returns a PtcReader or PtcWriter object.
A PtcReader object as returned by the open() function handles reading point cloud files. The object has the following attributes and methods:
Read the next data point. Returns a tuple (pos, normal, radius, dataDict) where pos and normal are 3-tuples of floats, radius is a single float and dataDict a dictionary with the extra variables that are attached to the point. If no more point is available an EOFError exception is thrown. An IOErrror exception is thrown when an error occurs during reading or when the file has already been closed.
Note that reading a large file using this method will be slow because every single point has to be read by a dedicated Python call. If you can process the points in batches, you should rather use the readDataPoints() method which will be a lot faster because a single Python call will read an entire sequence of points at once.
Read a sequence of data points. numPoints is the number of points to read. buffer is either a single buffer that will receive all values or a tuple (pointbuf, normalbuf, radiusbuf, databuf) that contains the individual buffers for the respective values. A buffer must always be large enough to hold numPoints values. The function accepts ctypes arrays as buffers or any sequence object that supports the array interface (such as numpy arrays).
The return value is the number of points that have actually been read (additional items in the buffers remain at their previous value). When 0 is returned, the end of the file has been reached
A PtcWriter object as returned by the open() function handles writing point cloude files. The object has the following attributes and methods:
You create a new point cloud file like this:
>>> from cgkit.cgtypes import *
>>> from cgkit import pointcloud
>>> ptc = pointcloud.open("cloud.ptc", "w", "3delight", vars=[("float", "foo")],
world2eye=mat4(1), world2ndc=mat4(1), format=(640,480,1))
>>> ptc.name
'cloud.ptc'
>>> ptc.datasize
1
The data size of 1 means there is one additional float attached to each point which is the extra variable foo. Once the file is open you can write individual points like this:
>>> ptc.writeDataPoint((0.5,0.3,0.9), (0,0,1), 1.0, {"foo":0.75})
Writing each point individually will be slow though. If possible you should try to gather a larger number of points and write them with a single call like this (for the sake of the example, we just create three points manually):
>>> pnts = (9*ctypes.c_float)(1.5,0.5,0.8, 0.8,0.9,1.0, 0.1,0.2,0.3)
>>> normals = (9*ctypes.c_float)(0,0,1, 1,0,0, 0,1,0)
>>> rads = (3*ctypes.c_float)(0.6, 0.7, 0.8)
>>> datas = (3*ptc.datasize*ctypes.c_float)(0.4, 0.3, 0.2)
>>> ptc.writeDataPoints(3, (pnts,normals,rads,datas))
>>> ptc.close()
The file can then be read again as follows:
>>> ptc = pointcloud.open("cloud.ptc", "r", "3delight")
>>> ptc.name
'cloud.ptc'
>>> ptc.npoints
4
>>> ptc.datasize
1
>>> ptc.variables
[('float', 'foo')]
>>> ptc.world2eye
[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]
>>> ptc.bbox
[-1.3333333730697632, -1.3333333730697632, -1.3333333730697632, 2.6666667461395264, 2.6666667461395264, 2.6666667461395264]
>>> ptc.format
(640.0, 480.0, 1.0)
Once the file is open, the data can be read in various ways. If you want to process each point individually in Python, you could either use the readDataPoint() method or just iterate over all the points:
>>> for p in ptc.iterPoints(): print p
...
((0.5, 0.30000001192092896, 0.89999997615814209), (0.0, 0.0, 1.0), 1.0, {'foo': 0.75})
((1.5, 0.5, 0.80000001192092896), (0.0, 0.0, 1.0), 0.60000002384185791, {'foo': 0.40000000596046448})
((0.80000001192092896, 0.89999997615814209, 1.0), (1.0, 0.0, 0.0), 0.69999998807907104, {'foo': 0.30000001192092896})
((0.10000000149011612, 0.20000000298023224, 0.30000001192092896), (0.0, 1.0, 0.0), 0.80000001192092896, {'foo': 0.20000000298023224})
Again, this will be slow for large point cloud files. If your application allows batch processing the points or you interface with C code, it will be faster to read several points at once (this time we use numpy instead of ctypes for creating a buffer):
>>> import numpy
>>> buf = numpy.zeros(shape=(5, 7+ptc.datasize), dtype=numpy.float32)
>>> ptc.readDataPoints(5, buf)
4
>>> print buf
[[ 0.5 0.30000001 0.89999998 0. 0. 1. 1.
0.75 ]
[ 1.5 0.5 0.80000001 0. 0. 1.
0.60000002 0.40000001]
[ 0.80000001 0.89999998 1. 1. 0. 0.
0.69999999 0.30000001]
[ 0.1 0.2 0.30000001 0. 1. 0.
0.80000001 0.2 ]
[ 0. 0. 0. 0. 0. 0. 0.
0. ]]
>>> ptc.readDataPoints(5, buf)
0
Instead of a single buffer, you could again pass four individual buffers just like above when we were writing the file.
You could also leave the buffer creation up to the point cloud handle and just iterate over buffers (output slightly reformatted for better readability):
>>> for buffers in ptc.iterBatches(numpyArray=True): print buffers
...
(array([[ 0.5 , 0.30000001, 0.89999998],
[ 1.5 , 0.5 , 0.80000001],
[ 0.80000001, 0.89999998, 1. ],
[ 0.1 , 0.2 , 0.30000001]],dtype=float32),
array([[ 0., 0., 1.],
[ 0., 0., 1.],
[ 1., 0., 0.],
[ 0., 1., 0.]], dtype=float32),
array([ 1. , 0.60000002, 0.69999999, 0.80000001], dtype=float32),
array([[ 0.75 ],
[ 0.40000001],
[ 0.30000001],
[ 0.2 ]], dtype=float32))