FumeFX to RenderMan - Part 1

The following is a transcription of one of my independent projects while I was a student at SCAD.  The original page is still available here.  Unfortunately, I haven't had much time or inclination to further develop this as I haven't really done anything with an fx simulation in a couple of years.  Perhaps some of the information here is useful to someone!

FumeFX is a fluid simulation plugin to 3D Studio Max, useful for smoke and fire effects. In the past, I did a fair bit of FX simulation with FumeFX in 3D Studio Max. A major limitation with FumeFX was the shading. The volumes could only be rendered with the software renderer, and later mental ray support was buggy to say the least (admittedly V-Ray shading was supported from the first release, though I never had access to V-Ray). A lot of time was wasted animating lights to approximate the contribution of the fire to the lighting of the environment, as well as just dialing in the shading parameters as the defaults were terrible.

In the RenderMan II class, I have experimented with volume primitives and plan to build scripts to convert FumeFX data to a RIB Archive sequence for use in RenderMan. I will also build a shader based on the physical properties of fire but retains art-directibility. If time allows, I will incorporate this project with my RenderMan II shader and implement indirect illumination of the smoke and the environment.


Volumes in RenderMan can now be handled with RiVolumes, or a voxelized primitive that can have arbitrary data at each voxel point. Additionally, these volumes are shaded with surface shaders as opposed to the more complicated and slow raymarching approaches of VPVolumes. The data values from the voxel grid can be treated as primvars inside the surface shader, and this feature is the primary benefit of volume primitives for shading FumeFX data. FumeFX data is likewise a voxelized grid, and can be accessed and exported via MaxScript commands inside of 3DS Max.

Much of the process described below is also explained in the RPS application noteVolume Rendering [1].


FumeFX is not an open format, and does not appear to even have a C++ API, only a very limited MaxScript API. MaxScript is notoriously slow, and the only available way to export the data from the file is by looping through the voxel grid and writing the data out using the built in commands. My initial approach was to use these commands and build an intermediate text-based format for later conversion to RenderMan RIB files with Python.

-- example MaxScript for FumeFX data extraction
fn PostLoad = (
-- PostLoad is called when each frame's data is
-- loaded for viewing/playback/rendering.
-- nx, ny, and nz are defined by FumeFX as the 
-- maximum voxel sizes for the current frame
    for i in 0 to (nx-1) do
    for j in 0 to (ny-1) do
    for k in 0 to (nz-1) do (
        smokeVal = GetSmoke i j k

Download the final MaxScript function

After running the simulation, the MaxScript function PostLoad is activated through a checkbox and the sequence is played back and written to disk as ascii text files. The runtime for the full MaxScript code was up to 20 minutes for my 19 MegaVoxel grid - an obvious concern. Next, I used Python to convert these text files into RenderMan RIBs.

Initially the Python segment was very slow as I was generating the giant strings to write to the RIB file. After switching to arrays and writing directly from the arrays, I was able to drop the execute time of the Python script from around 25 minutes to 30 seconds. However, these RIB files did not account for 3D Studio's Z-Up system. I reworked the Python to utilize 3D Arrays, so that I might query them differently to account for swapping Y and Z. This operation caused execute times to approach 20 minutes again.

Total conversion times of at least 40 minutes per frame was not acceptable, especially considering I would be working with even larger grids for production use. I ultimately decided to write one more optimized MaxScript function that would read the data from FumeFX's voxel grid, do the Y and Z axis swap, and write the RenderMan RIB files. With this scenario, the worst case frame conversions were about 12 minutes, a much more manageable (though still slow). Additionally, I converted the RIBs to GZipped Binary RIBs with catrib, a PRMan utility. File sizes were on average about 4 times smaller than the ascii counterpart.

A small RenderMan for Maya issue was encountered, where the 'resize bounding box' RIB Archive control did not seem to work if the RIB was GZipped (or binary), so I made the choice to just set a large bounding box for my renders at the small expense of time in order to keep file sizes manageable.

I also utilized FumeFX 2's post-processing functionality to 'shrink' the voxel grids to the minimum size needed to contain the smoke and fire. By default, the grids can become quite large and encompass a lot of additional space that only contains velocity data. This can be useful for some applications but was not needed in this case. The grid 'shrinking' vastly reduced the number of frames that were close to the full 19 MegaVoxel grid size.


[1] RenderMan Pro Server App Note - Volume Rendering