I couldn’t get any of the other exporters to do exactly what I wanted, so I have written a very crude python script to output straight to PRT files. You just drop it into a python node. You’ll have to change the attribute channels to suit what you are doing, but it might be a good start for someone.
It’s probably not a very good script. I wrote it on literally my second day of using Houdini. If anyone improves it, please update here. The first thing it is missing is any error checking.
If you want to try it, you’ll need to make sure you have the following attributes on your points/particles:
orient (quat)
pscale
meshID
MtlIndex
you can drop the following into an attribute wrangle to make some dummy values:
p@orient = {0,1,0,0};
v@pscale = {1,1,1};
i@meshID = rand(@ptnum);
i@MtlIndex = rand(@ptnum);
And the script:
import sys, struct, zlib
node = hou.pwd()
geo = node.geometry()
frameNum = str(hou.intFrame()).zfill(4)
OutFilename = 'C:YOURPATHHERE/Particles_%s.prt' %frameNum
particleCount = len(geo.points())
out = file(OutFilename, 'wb')
out.write(struct.pack("B", 0xc0)) # Magic numbers
out.write(struct.pack("B", 0x50))
out.write(struct.pack("B", 0x52))
out.write(struct.pack("B", 0x54))
out.write(struct.pack("B", 0x0d))
out.write(struct.pack("B", 0x0a))
out.write(struct.pack("B", 0x1a))
out.write(struct.pack("B", 0x0a))
out.write(struct.pack("<I", 56)) # Header Length
out.write(struct.pack("@32s", "Extensible Particle Format")) #Format Name
out.write(struct.pack("<I", 1)) # Version Number
out.write(struct.pack("<Q", particleCount)) # Particle Count
out.write(struct.pack("<I", 4)) # Reserved 4 bytes
out.write(struct.pack("<I", 6)) # Number of Channels
out.write(struct.pack("<I", 44)) # Channel definition entry length
# Write Channel Definitions
out.write(struct.pack("@32s", "Position")) #Name of Channel
out.write(struct.pack("<I", 4)) # Data type
out.write(struct.pack("<I", 3)) # number of values per particle for this channel
out.write(struct.pack("<I", 0)) # Data offset of this channel
out.write(struct.pack("@32s", "Orientation")) #Name of Channel
out.write(struct.pack("<I", 4)) # Data type
out.write(struct.pack("<I", 4)) # number of values per particle for this channel
out.write(struct.pack("<I", 12)) # Data offset of this channel
out.write(struct.pack("@32s", "Scale")) #Name of Channel
out.write(struct.pack("<I", 4)) # Data type
out.write(struct.pack("<I", 3)) # number of values per particle for this channel
out.write(struct.pack("<I", 28)) # Data offset of this channel
out.write(struct.pack("@32s", "ShapeIndex")) #Name of Channel
out.write(struct.pack("<I", 1)) # Data type
out.write(struct.pack("<I", 1)) # number of values per particle for this channel
out.write(struct.pack("<I", 40)) # Data offset of this channel
out.write(struct.pack("@32s", "MtlIndex")) #Name of Channel
out.write(struct.pack("<I", 1)) # Data type
out.write(struct.pack("<I", 1)) # number of values per particle for this channel
out.write(struct.pack("<I", 44)) # Data offset of this channel
out.write(struct.pack("@32s", "Color")) #Name of Channel
out.write(struct.pack("<I", 4)) # Data type
out.write(struct.pack("<I", 3)) # number of values per particle for this channel
out.write(struct.pack("<I", 48)) # Data offset of this channel
data = []
m = hou.Matrix4(( 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1 )) # Transform matrix to transform into 3DSMax space
for point in geo.points():
#Transform Position
pos = point.position()
pos = pos*m
data.append(pos)
#Transform Orientation
ov = point.attribValue('orient')
x = ov[0]
y = ov[1]
z = ov[2]
w = ov[3]
orient = hou.Quaternion(x, y, z, w)
orientM = hou.Matrix4(orient.extractRotationMatrix3())
newMatrix = orientM*m
orient = hou.Quaternion(newMatrix)
data.append(orient)
data.append(point.attribValue('pscale'))
data.append(point.attribValue('meshID'))
data.append(point.attribValue('MtlIndex'))
data.append(point.attribValue('Cd'))
z = zlib.compressobj()
for p in data:
if type(p) is int:
x = (struct.pack("<i", p))
compressedData = z.compress(x)
out.write(compressedData)
else:
for v in p:
x = (struct.pack("<f", v))
compressedData = z.compress(x)
out.write(compressedData)
compressedData = z.flush()
out.write(compressedData)
out.close()