AWS Thinkbox Discussion Forums

Houdini Exporter

I just noticed the Houdini exporter only supports the old pop network. Is this still the case? Is there any chance to update it to include the new pop network and any point system?

Found this, which does the trick. wish there was an official version from Thinkbox. flipswitchingmonkey.com/201 … m-houdini/

Thanks for letting us know about this. I’ll look into it.

A new exporter with support for Houdini 16 is now available on our Github page.
github.com/ThinkboxSoftware/Hou … r/releases

Hi Evan,

I’ve installed the new v16 Exporter and all seems fine but I can only get it to export a single frame. How do you tell your exporter to export the whole frame range?

Also, I have another Krakatoa exporter (the Flip Switching Mokey one) which works perfectly but is unfortunately single threaded. Is your exporter multi-threaded? One of the benefits of the FSM exporter is that it’s built as a ROP driver (it’s a proper Houdini node) and can be set up with Houdini Wedging (similar to Krakatoa partitions), so you can output multiply PRT’s overnight with a variation of seed values.

Houdini particles are fully threaded so it’s not ideal if caching is throttled by a single threaded operation, but unfortunately your Python exporter isn’t compatible with Wedging as it has to be run manually from the Python shell. If it’s threaded it will be easy enough to run separately seeded PRT exports manually as a 10 million particle sim exports in approx 1 second per frame (that’s to BGEO, the compressed variant of Houdini’s native cache format - BGEO.SC would take a little longer but not by much).

Hi jonmoore,

Our exporter always exports the current frame, but you can manipulate the current frame using Houdini’s Python API. You can use something like this to export the entire frame range (copy and paste into the Houdini Python Shell and hit enter):

import Krakatoa

# The Houdini node to export to PRT
node_to_export = hou.node('/obj/fireworks_particles/import_fireworks')

# Output file names will be '/out/fireworks0001.prt', '/out/fireworks0002.prt', etc.
# The destination folder must already exist
output_prefix = '/out/fireworks'

# Sets `start` variable to the first frame number and `end` to the last frame number
(start, end) = hou.playbar.playbackRange()

# You can set this to a larger number to skip over frames
step = 1

# Go through each frame, running the exporter for each one
for frame_num in range(int(start), int(end) + 1, step):
    hou.setFrame(frame_num)
    filename = output_prefix + str(frame_num).zfill(4) + '.prt'
    Krakatoa.exportParticles(node_to_export, filename)

Hopefully the code shouldn’t be too hard to understand.

Unfortunately, our exporter is also single-threaded. I will add your suggestions to our issue tracker.

Thanks for your detailed reply and suggestions Yin. Seeing as the Flip Switching Monkey PRT ROP functions well, I’ll carry on using that for the moment (it has some other nice features e.g. it allows you to filter and remap the attributes on export so keep the cache size as small as possible).

A multithreaded PRT exporter from Thinkbox would be such a powerful option for Houdin to Krakatoa workflows. With Houdini’s particles being multithreaded, a threaded exporter would maximise the utility of using Houdini in tandem with Krakatoa. You guys should take a look at the Flip Switching Monkey exporter. It may be single threaded but it’d great from a UX perspective is it works without Python so it’s a little quicker and with it being a standard ROP output driver, it can be wedged (output seed based partitions).

https://www.flipswitchingmonkey.com/houdini-prt-rop/

Hi…just wanted to chime in that there is definite interest out there for a more fleshed-out link between houdini and krakatoa.

Completely agree that a multi-threaded export would do wonders to alleviate a nasty bottle-neck.
Honestly, I’d like go even further and have the renderer directly in houdini, using vops on PRTs for artistic iterations the same way I would use magmaflow in krakatoaMX. That would really be the coolest thing ever.

cheers,
CZ

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()

The link was changed on the FlipSwitchingMonkey PRT ROP.

It’s now being maintained with the latest SDK versions here:

https://github.com/flipswitchingmonkey/houdini_PRTROP/releases

Hey guys! I’ve only seen this thread now somehow… (Too much Deadline maybe :slight_smile:)

I can’t promise anything, but I’ll let the right people know about the interest here.

@eamsler, that would be great.

The core missing aspect at the moment is a multithreaded PRT writer. Particles in Houdini are a delight to work with (highly optimised and fully threaded). But the existing PRT export methods from Houdini are single threaded and that makes the process a bit of a drag.

For everyone who need it here is the PRT Exporter plugin (PRT_RopDriver) for Houdini 19
Some issues are fixed as well

1 Like

Thank you for updating this plugin. I’m working with Houdini 19.5.534 and I can’t seem to get it working. I’ve tried copying the .dll file to

C:\Program Files\Side Effects Software\Houdini 19.5.534\houdini\dso
and to
C:\Users\USER\Documents\houdini19.5\dso

and had no luck. I’m not seeing the PRT ropdriver as available in the OUT context or in any ropnets I’ve created. Am I doing something wrong or does the dll need to be recompiled for 19.5.xxx?

Privacy | Site terms | Cookie preferences