# How can I write a custom smoke cache?

I'm working on a project where I'd really like to get voxel data loaded in cycles. This comment from a question I asked a while back indicates that it should be feasible by converting my voxel data to a smoke cache.

Is there a tool or api that can I can use to help create the a smoke cache from my voxel data? Or technical specification that describe the smoke cache file format such that I can write it from scratch?

Alternatively, I'm open to any other hacks or workarounds that can get my voxels into a Cycles texture!

• I recently developed an add-on to convert smoke data into a tiled EXR image where each 'slice' of the smoke was represented by a tile. This could then be used in an Image Texture node with a carefully crafted transform from the XYZ coords in the volume into XY coords in the image. If you can convert your voxel data in a similar format you could use the same technique. See baldingwizard.wixsite.com/blog/smoke2exr for the add-on (download also available via github.com/baldingwizard/Blender-Addons/wiki/Smoke2EXR). – Rich Sedman Aug 20 '19 at 8:53

## 2 Answers

Since 2.77 Blender can use the OpenVDB format for smoke caching, and OpenVDB has a nice API and documentation. (There is also a Python API)

It is also an industry standard, which will be better supported in Blender in the future.

• Any chance you'd be able to write a short example of how to write out a 3D numpy array to an OpenVDB volume? – ajwood Feb 23 '17 at 23:38
• There is a chapter named "Working with NumPy arrays" in the Python link. – lbalazscs Feb 24 '17 at 9:21
• I'm still having trouble getting it installed (on Ubuntu). Have you had difficulty with that? – ajwood Feb 24 '17 at 13:18
• No, I personally had no difficulty at all, because I didn't try :) But - considering that Blender itself uses it - it should be doable. It seems to be a pretty standard C++ program, but if you are not used to compiling this kind of stuff, it can be a challenge. Perhaps ask on the openvdb forums or on Ubuntu forums. – lbalazscs Feb 24 '17 at 15:45

As part of developing the Smoke2EXR add-on mentioned in my answer to Voxel data in Cycles I did analyse the code that writes out the pointcache format so I could re-interpret the pointcache for my own purposes. The source file is pointcache.c and can be found in https://github.com/martijnberger/blender/blob/master/source/blender/blenkernel/intern/pointcache.c (there's probably a link to the actual blender.git repository but I couldn't easily find it).

The details I managed to extract from 'pointcache.c' to be able to interpret the pointcache file were as commented in the source of that add-on :

# Supports PointCache smoke version 1.04
#
# Smoke pointcache format :
# Bytes     Content
# 8         'BPHYSICS'
# 4         int - 3 (indicates Smoke)
# 4         int? (!!thought this was size of data... but compressed size exceeds this so one of them is wrong)
# 4         int (2 (???))
# 4         '1.04' (version)
# 4         int (fluid_fields)
# 4         int (active_fields)
# 4         int (res_X)
# 4         int (res_Y)
# 4         int (res_Z)
# 4         float? (dx)
# block - shadow (x * y * z * float)
# block - density (x * y * z * float)
# Optional :
#   block - heat (x * y * z * float)
#   block - heatold (x * y * z * float)
# Optional :
#   block - flame (x * y * z * float)
#   block - fuel (x * y * z * float)
#   block - react (x * y * z * float)
# Optional :
#   block - red (x * y * z * float)
#   block - green (x * y * z * float)
#   block - blue (x * y * z * float)
# block - vx (x * y * z * float)
# block - vy (x * y * z * float)
# block - vz (x * y * z * float)
# block - obstacles (x * y * z * byte?) (!!Doesn't seem to be populated)
# (more fields)
#       ptcache_file_write(pf, &dt, 1, sizeof(float));
#       ptcache_file_write(pf, &dx, 1, sizeof(float));
#       ptcache_file_write(pf, &sds->p0, 3, sizeof(float));
#       ptcache_file_write(pf, &sds->p1, 3, sizeof(float));
#       ptcache_file_write(pf, &sds->dp0, 3, sizeof(float));
#       ptcache_file_write(pf, &sds->shift, 3, sizeof(int));
#       ptcache_file_write(pf, &sds->obj_shift_f, 3, sizeof(float));
#       ptcache_file_write(pf, &sds->obmat, 16, sizeof(float));
#       ptcache_file_write(pf, &sds->base_res, 3, sizeof(int));
#       ptcache_file_write(pf, &sds->res_min, 3, sizeof(int));
#       ptcache_file_write(pf, &sds->res_max, 3, sizeof(int));
#       ptcache_file_write(pf, &sds->active_color, 3, sizeof(float));
# Optional turbulance - hi-res [...size based on number of 'divisions'+1 cubed - so 1 division is 2x2x2 times bigger, 2 divisions is 3x3x3 times bigger, etc.]
#   Optional:
#       block - density (hi-res)
#   Optional :
#       block - flame (hi-res)
#       block - fuel (hi-res)
#       block - react (hi-res)
#   Optional :
#       block - red (hi-res)
#       block - green (hi-res)
#       block - blue (hi-res)
#   block - tcu (x * y * z * float)
#   block - tcv (x * y * z * float)
#   block - tcw (x * y * z * float)


You could possibly use this to take your raw data and generate a pointcache file. However, I have not tried this and could not be sure of all the output fields - since I only needed to interpret certain blocks to be able to extract the content, so didn't analyse it to the depth to be able to create a consistent pointcache file. Note that many of the blocks are 'optional' (dependent on whether, say, Flame and/or Hi-Resolution is enabled) and you need to get the flags consistent with the blocks you need to have present.

The alternative is to convert your data into a different format that can be used in Cycles - such as the tiles EXR produced by the Smoke2EXR add-on mentioned above.