Houdini Engine 7.0
|
Volumetric data can be thought of as 3D grids in space. In Houdini, volumes can represent two main types of objects: either fields in a fluid simulation, or the surface of an object (SDF). For example, in a single fluid simulation, there are many fields, eg. density, velocity, heat, temperature, fuel. These fields all come together to become a fluid. Volumes may contain two types of data, ints or floats, and it may be scalars (tupleSize == 1) or vectors (tupleSize > 1).
In Houdini, volumes may be represented in two different ways, as Houdini native volumes, or as VDBs. Houdini native volumes are dense whereas VDBs are sparse. The volume data representation you will be extracting / setting will be sparse, as the dense data is a special case of the sparse data. Therefore, instead of retrieving the volume data as one massive array, it is given out in smaller subarrays called tiles. Each tile refers to a cube subset of the volume. Each volume might have a different resolution for the size of the tile. At the time of writing, this should either be 8 or 16 (ie. 8x8x8 and 16x16x16).
To extract volumetric data through HAPI, we begin at the point of the process where we are extracting parts from geos. A part is a volume part if HAPI_PartInfo::type is equal to HAPI_PARTTYPE_VOLUME and that means more information can be obtained about the volume though the HAPI_GetVolumeInfo() call.
A HAPI_VolumeInfo struct is returned. Note that the HAPI_VolumeInfo::minX, HAPI_VolumeInfo::minY, HAPI_VolumeInfo::minZ, HAPI_VolumeInfo::xLength, HAPI_VolumeInfo::yLength, and HAPI_VolumeInfo::zLength fields do NOT refer to the spatial extents of the volume - notice they are integers and not floats. Instead, they refer to the range of valid "indices" into the volume. The quotes are around the term indices because to index into volume data, a 3-tuple of indices in each of x,y, and z are required. It is important to notice that the 3-tuple of integers may be negative, as each of HAPI_VolumeInfo::minX, HAPI_VolumeInfo::minY, and HAPI_VolumeInfo::minZ could be negative.
Here's an example of getting the volume info:
The HAPI_VolumeInfo::minX, HAPI_VolumeInfo::minY, HAPI_VolumeInfo::minZ, HAPI_VolumeInfo::xLength, HAPI_VolumeInfo::yLength, and HAPI_VolumeInfo::zLength fields tell us how to index into the overall volume, and the transform field tells us how to go from volume object space into world space, but we need to know how to map a voxel at a particular (i,j,k) index into object space. This mapping from (i,j,k) index into object space is given by the following pseudo-code:
The taper function pseudo-code is given as follows:
Note that the situation of tapering is relatively rare in practice.
If needed, you can access a volume's bounds by calling the HAPI_GetVolumeBounds() function. This will give you the minimum, maximum and center XYZ values of the volume.
Knowing the size of each tile from the HAPI_VolumeInfo, we can retrieve each tile as a HAPI_VolumeTileInfo with the following functions:
The HAPI_VolumeTileInfo::minX, HAPI_VolumeTileInfo::minY, and HAPI_VolumeTileInfo::minZ fields denote the tile's position within the overall volume. Note that a tile may not have values at all of its voxels. This can happen in two cases:
In the first case, the invalid voxels will not be written to (ie. the input buffer at those locations will be left as is). Whether a voxel index is within the volume or not can be determined by checking if the index is in the range given by HAPI_VolumeInfo::minX, HAPI_VolumeInfo::minY, HAPI_VolumeInfo::minZ, HAPI_VolumeInfo::xLength, HAPI_VolumeInfo::yLength, and HAPI_VolumeInfo::zLength.
Finally, the actual values in each tile can be retrieved with:
Given an index into the tile (i,j,k), and the component index c (0 for x, 1 for y, 2 for z), the value of the component c in the voxel is given by:
Here's an example that goes through all the tiles in a VDB color volume and adds up all the voxel values:
You can also get the values of individual voxels using either HAPI_GetVolumeVoxelFloatData() or HAPI_GetVolumeVoxelIntData(). Here's an example of adding up all voxels in a VDB color volume:
You can marshal volume data into a node created with HAPI_CreateInputNode(), similar to geometry. See Marshalling Geometry Into Houdini. Volumes are just another primitive type that can be stored in a part of an Input Node.
Once you have an Input Node, you have to set the parameters of the volume part with a HAPI_SetPartInfo() call. For volumes, HAPI_PartInfo::type has to be set to HAPI_PARTTYPE_VOLUME. You can also set the attribute counts if you wish to add attributes to your volume but everything else in the HAPI_PartInfo can be left to the default value returned by HAPI_PartInfo_Init().
Next, set the volume info with HAPI_SetVolumeInfo(). It is up to you to make sure the prorties you set in your HAPI_VolumeInfo are valid. Some validation will be done on HAPI too so make sure you check the HAPI_Result when setting the volume info.
You are now ready to input tile data. You do this with HAPI_SetVolumeTileFloatData() and HAPI_SetVolumeTileIntData(). You can also set individual voxel values by calling the HAPI_SetVolumeVoxelFloatData() or HAPI_SetVolumeVoxelIntData() functions.
Finally, when you're done inputting data, you have to commit the volume by calling HAPI_CommitGeo().
Here's an example that takes a VDB volume and reproduces it inside an input node:
Heightfields are actually stored as two dimensional volumes ( with a Z size of 1 ) in Houdini.
Accessing or marshalling heightfield data is no different than volume data, but you can use the HAPI_GetHeightFieldData() and HAPI_SetHeightFieldData() helper functions for faster access. The type of heightfield data stored in the volume can be identified with the volume name (height, mask, debris ... ) available on the HAPI_VolumeInfo.
Before getting or setting the data with HAPI_GetHeightFieldData() or HAPI_SetHeightFieldData(), it is mandatory that you call either HAPI_GetVolumeInfo() or HAPI_SetVolumeInfo().
When creating heightfields in Houdini, it is recommended that you set the tile size and tuple size to 1, and that you set the volume type to HAPI_VOLUMETYPE_HOUDINI in the HAPI_VolumeInfo. Please note that in order to be fully compatible with the heightfield nodes, it is required that you create at least two volumes named "height" and "mask" with HAPI, and that you merge them together.
The easiest way to create Heightfield input is to use the HAPI_CreateHeightfieldInput() function. It will create an unlocked heightfield node, and give you access to the height, mask volume node so you can update their data. It also give you access to the Heightfield's internal merge node, that you can use to add more masks/layers by using the HAPI_CreateHeightfieldInputVolumeNode helper function.
Here's an example that reads data from a Heightfield:
Here's an example that creates an input Heighfield, updates its height and mask data, and adds an extra "water" mask: