Houdini Engine 7.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Volumes

Querying

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:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
HAPI_Result result =
hapiTestSession, "HAPI_Test_Volumes_VDBFog.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Volumes_VDBFog",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the display geo info.
result = HAPI_GetDisplayGeoInfo( hapiTestSession, node_id, &geo_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the part info. We know we only have on geo and one part so ids 0-0.
// The part info should claim it is a volume.
result = HAPI_GetPartInfo(
hapiTestSession, geo_info.nodeId, 0, &part_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( part_info.type == HAPI_PARTTYPE_VOLUME );
// Get the volume info.
HAPI_VolumeInfo volume_info;
hapiTestSession, geo_info.nodeId, 0, &volume_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Check the information in the volume info against expected values.
UT_String volume_name = HAPItestGetString( volume_info.nameSH );
HAPI_TEST_ASSERT( volume_name == "density" );
HAPI_TEST_ASSERT( volume_info.type == HAPI_VOLUMETYPE_VDB );
HAPI_TEST_ASSERT( volume_info.xLength == 30 );
HAPI_TEST_ASSERT( volume_info.yLength == 9 );
HAPI_TEST_ASSERT( volume_info.zLength == 29 );
HAPI_TEST_ASSERT( volume_info.minX == -15 );
HAPI_TEST_ASSERT( volume_info.minY == -4 );
HAPI_TEST_ASSERT( volume_info.minZ == -14 );
HAPI_TEST_ASSERT( volume_info.tupleSize == 1 );
HAPI_TEST_ASSERT( volume_info.storage == HAPI_STORAGETYPE_FLOAT );
HAPI_TEST_ASSERT( volume_info.tileSize == 8 );
HAPI_TEST_ASSERT( volume_info.hasTaper == false );
HAPI_TEST_ASSERT( approx( volume_info.xTaper, 1.0f ) );
HAPI_TEST_ASSERT( approx( volume_info.yTaper, 1.0f ) );

Positioning

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:

indexToObject( int i, int j, int k, HAPI_VolumeInfo vol ):
vec3 pos = 2.0 * ( ( i, j, k ) - ( 0.5, 0.5, 0.5 ) )
if ( vol.hasTaper ):
taper( pos, vol );
pos = vol.transform.scale * pos
pos = rotate( pos, vol.transform.rotationEuler, vol.transform.rotationOrder )
pos = pos + vol.transform.position
return pos

The taper function pseudo-code is given as follows:

taper( vec3 pos, HAPI_VolumeInfo vol ):
zscale = ( 1 - pos.z ) * 0.5;
taperx = 1 + ( vol.xTaper - 1 ) * zscale;
tapery = 1 + ( vol.yTaper - 1 ) * zscale;
pos.x = pos.x * taperx;
pos.y = pos.y * tapery;
return pos;

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.

Volume Output

By Tile

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:

  1. when the tile straddles either the boundary for the entire volume,
  2. or one of many boundaries within a VDB where there is sparse data.

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:

values[ k*tileSize*tileSize*tupleSize + j*tileSize*tupleSize + i*tupleSize + c ]

Here's an example that goes through all the tiles in a VDB color volume and adds up all the voxel values:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
HAPI_Result result =
hapiTestSession, "HAPI_Test_Volumes_VDBFogColor.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Volumes_VDBFogColor",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the display geo info.
result = HAPI_GetDisplayGeoInfo( hapiTestSession, node_id, &geo_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the part info for the second part which should be the color volume.
const HAPI_PartId part_id = 1;
// Get the volume info.
HAPI_VolumeInfo volume_info;
hapiTestSession, geo_info.nodeId, part_id, &volume_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( volume_info.tupleSize == 3 );
HAPI_TEST_ASSERT( volume_info.hasTaper == false );
// Get the first volume tile.
HAPI_VolumeTileInfo volume_tile_info;
hapiTestSession, geo_info.nodeId, part_id, &volume_tile_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( volume_tile_info.isValid );
// Allocate tile data buffer.
const int tile_value_count =
volume_info.tileSize *
volume_info.tileSize *
volume_info.tileSize *
volume_info.tupleSize;
float * tile_values = new float[ tile_value_count ];
// Get volume tiles until we've gotten all the volume tiles.
// Will re-use the first volume tile's info.
float full_volume_value_sum = 0.0f;
while ( volume_tile_info.isValid )
{
// Reset the data. These values should all be overwritten by
// the fill_value of -8.8 below.
for ( int i = 0; i < tile_value_count; ++i )
tile_values[ i ] = -5.8f;
// Get the color data.
hapiTestSession,
geo_info.nodeId, part_id, -8.8f, &volume_tile_info,
tile_values, tile_value_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Verify tile data.
const int color_tile_tuple_count =
tile_value_count / volume_info.tupleSize;
for ( int i = 0; i < color_tile_tuple_count; ++i )
{
// The volume voxel values are either the correct color constant
// or 1.0/1.0/1.0 if the voxel is outside the dense areas of the
// volume.
const int voxel_index = i * volume_info.tupleSize;
HAPI_TEST_ASSERT(
approx( tile_values[ voxel_index + 0 ], 0.8f ) ||
approx( tile_values[ voxel_index + 0 ], 1.0f ) );
HAPI_TEST_ASSERT(
approx( tile_values[ voxel_index + 1 ], 0.5f ) ||
approx( tile_values[ voxel_index + 1 ], 1.0f ) );
HAPI_TEST_ASSERT(
approx( tile_values[ voxel_index + 2 ], 0.2f ) ||
approx( tile_values[ voxel_index + 2 ], 1.0f ) );
}
// Add up all values for a sum check at the end.
for ( int i = 0; i < tile_value_count; ++i )
full_volume_value_sum += tile_values[ i ];
// Get the next color tile.
hapiTestSession,
geo_info.nodeId, part_id,
&volume_tile_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
}
// Check the value sum against expected.
HAPI_TEST_ASSERT( approx( full_volume_value_sum, 56388.0f ) );
// Cleanup.
delete [] tile_values;

By Voxel

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:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
HAPI_Result result =
hapiTestSession, "HAPI_Test_Volumes_VDBFogColor.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Volumes_VDBFogColor",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the display geo info.
result = HAPI_GetDisplayGeoInfo( hapiTestSession, node_id, &geo_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the part info for the second part which should be the color volume.
const HAPI_PartId part_id = 1;
// Get the volume info.
HAPI_VolumeInfo volume_info;
hapiTestSession, geo_info.nodeId, part_id, &volume_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( volume_info.tupleSize == 3 );
HAPI_TEST_ASSERT( volume_info.hasTaper == false );
// Get the values of all the voxels in the volume.
float full_volume_value_sum = 0.0f;
{
const HAPI_VolumeInfo & vi = volume_info;
for ( int x = vi.minX; x < vi.minX + vi.xLength; ++x )
for ( int y = vi.minY; y < vi.minY + vi.yLength; ++y )
for ( int z = vi.minZ; z < vi.minZ + vi.zLength; ++z )
{
float voxel_value[ 3 ]; // vi.tupleSize == 3
hapiTestSession,
geo_info.nodeId, part_id,
x, y, z, (float *) voxel_value, vi.tupleSize );
// The volume voxel values are either the correct color
// constant or 1.0/1.0/1.0 if the voxel is outside the
// dense areas of the volume.
HAPI_TEST_ASSERT(
approx( voxel_value[ 0 ], 0.8f ) ||
approx( voxel_value[ 0 ], 1.0f ) );
HAPI_TEST_ASSERT(
approx( voxel_value[ 1 ], 0.5f ) ||
approx( voxel_value[ 1 ], 1.0f ) );
HAPI_TEST_ASSERT(
approx( voxel_value[ 2 ], 0.2f ) ||
approx( voxel_value[ 2 ], 1.0f ) );
for ( int i = 0; i < vi.tupleSize; ++i )
full_volume_value_sum += voxel_value[ i ];
}
}
// Check the value sum against expected.
HAPI_TEST_ASSERT( approx( full_volume_value_sum, 37785.0f ) );

Volume Marshalling

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:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
HAPI_Result result =
hapiTestSession, "HAPI_Test_Volumes_VDBFogColor.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Volumes_VDBFogColor",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the display geo info.
result = HAPI_GetDisplayGeoInfo( hapiTestSession, node_id, &geo_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the part info for the second part which should be the color volume.
const HAPI_PartId part_id = 1;
// Get part info.
result = HAPI_GetPartInfo(
hapiTestSession, geo_info.nodeId, part_id, &part_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the volume info.
HAPI_VolumeInfo volume_info;
hapiTestSession, geo_info.nodeId, part_id, &volume_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( volume_info.tupleSize == 3 );
HAPI_TEST_ASSERT( volume_info.hasTaper == false );
// Create input asset to receive volume.
HAPI_NodeId input_node_id = -1;
hapiTestSession, -1, &input_node_id, "Input_Volume" );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Set part info.
result = HAPI_SetPartInfo(
hapiTestSession, input_node_id, 0, &part_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Set the volume info.
hapiTestSession, input_node_id, 0, &volume_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the first volume tile.
hapiTestSession, geo_info.nodeId, part_id, &volume_tile_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( volume_tile_info.isValid );
// Allocate tile data buffer.
const int tile_value_count =
volume_info.tileSize *
volume_info.tileSize *
volume_info.tileSize *
volume_info.tupleSize;
float * tile_values = new float[ tile_value_count ];
// Get volume tiles until we've gotten all the volume tiles.
// Will re-use the first volume tile's info.
// There should be the same number of tiles in both the density and
// color volumes.
while ( volume_tile_info.isValid )
{
// Reset the data. These values should all be overwritten by
// the fill_value of -8.8 below.
for ( int i = 0; i < tile_value_count; ++i )
tile_values[ i ] = -5.8f;
// Get the color data.
hapiTestSession,
geo_info.nodeId, part_id, -8.8f, &volume_tile_info,
tile_values, tile_value_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Set the color data on the input volume.
hapiTestSession,
input_node_id, 0, &volume_tile_info,
tile_values, tile_value_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the next color tile.
hapiTestSession,
geo_info.nodeId, part_id,
&volume_tile_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
}
// Commit the volume inputs.
result = HAPI_CommitGeo( hapiTestSession, input_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Cook the asset.
result = HAPI_CookNode( hapiTestSession, input_node_id, nullptr );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Cleanup.
delete [] tile_values;

Heightfields

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:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
HAPI_Result result =
hapiTestSession, "HAPI_Test_Terrain_Simple.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Terrain_Simple",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the display geo info.
result = HAPI_GetDisplayGeoInfo( hapiTestSession, node_id, &geo_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get and check the HAPI_PartInfo. In this case, we know we're
// specifically looking for the part 0.
result = HAPI_GetPartInfo(
hapiTestSession, geo_info.nodeId, 0, &part_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the volume info.
hapiTestSession, geo_info.nodeId, 0, &volume_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Check the volume infos correspond to those of a heightfield
HAPI_TEST_ASSERT( volume_info.zLength == 1 );
HAPI_TEST_ASSERT( volume_info.tupleSize == 1 );
const int totalsize = ( volume_info.xLength * volume_info.yLength );
HAPI_TEST_ASSERT( totalsize > 4 );
// Get the height field data.
std::vector< float > heightfieldData( totalsize );
hapiTestSession, geo_info.nodeId, 0,
heightfieldData.data(), 0, totalsize );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );

Here's an example that creates an input Heighfield, updates its height and mask data, and adds an extra "water" mask:

// Number of voxel in X/Y
int num_x_points = 500;
int num_y_points = 500;
int voxelsize = 1;
// Actual size in x/y
float xsize = 1000;
float ysize = 1000;
// Node IDs
HAPI_NodeId heightfield_id, height_id, mask_id, merge_id;
// Create a Heightfield input node
session, -1, "Heightfield_Test",
num_x_points, num_y_points, voxelsize,
&heightfield_id, &height_id, &mask_id, &merge_id);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Create the input volume transform. It'll be used for all volumes in the HF
HAPI_Transform_Init(&transform);
// Volume scale controls the final size, and is actually the half size of the voxel
transform.scale[0] = xsize / 2.0f;
transform.scale[1] = ysize / 2.0f;
transform.scale[2] = voxelsize / 2.0f;
// Height Volume
// We need to cook the height volume to get proper infos
result = HAPI_CookNode(session, height_id, nullptr);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Get the GEO/PART/VOLUME infos
HAPI_GeoInfo_Init(&geo_info);
HAPI_GetGeoInfo(session, height_id, &geo_info);
HAPI_PartInfo_Init(&part_info);
HAPI_GetPartInfo(session, geo_info.nodeId, 0, &part_info);
HAPI_VolumeInfo input_volume_info = HAPI_VolumeInfo_Create();
HAPI_VolumeInfo_Init(&input_volume_info);
HAPI_GetVolumeInfo(session, height_id, 0, &input_volume_info);
// The volume infos must be updated so they are properly set on the HAPI side
input_volume_info.tileSize = 1;
input_volume_info.type = HAPI_VOLUMETYPE_HOUDINI;
input_volume_info.transform = transform;
result = HAPI_SetVolumeInfo(session, height_id, 0, &input_volume_info);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Set the Height Data
const int totalsize = (input_volume_info.xLength * input_volume_info.yLength);
std::vector< float > heightfieldData(totalsize);
for (int n = 0; n < totalsize; ++n)
{
heightfieldData[n] = ((float)rand() / (float)RAND_MAX) * 5.0f;
}
result = HAPI_SetHeightFieldData(session, height_id, 0, "height", heightfieldData.data(), 0, totalsize);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Commit the geo
result = HAPI_CommitGeo(session, height_id);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Mask Volume
// Cook the volume to get proper infos
result = HAPI_CookNode(session, mask_id, nullptr);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Get the GEO/PART/VOLUME infos for the mask
HAPI_GeoInfo_Init(&geo_info);
HAPI_GetGeoInfo(session, mask_id, &geo_info);
HAPI_PartInfo_Init(&part_info);
HAPI_GetPartInfo(session, geo_info.nodeId, 0, &part_info);
HAPI_VolumeInfo_Init(&input_volume_info);
HAPI_GetVolumeInfo(session, geo_info.nodeId, 0, &input_volume_info);
// Update the volume info
input_volume_info.tileSize = 1;
input_volume_info.type = HAPI_VOLUMETYPE_HOUDINI;
input_volume_info.transform = transform;
result = HAPI_SetVolumeInfo(session, mask_id, 0, &input_volume_info);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Fill the mask data with noise
std::vector< float > maskData(totalsize);
for (int n = 0; n < totalsize; ++n)
{
maskData[n] = ((float)rand() / (float)RAND_MAX);
}
result = HAPI_SetHeightFieldData(session, mask_id, 0, "mask", maskData.data(), 0, totalsize);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Commit
result = HAPI_CommitGeo(session, mask_id);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// WATER
// Create an additional "water" mask by using HAPI_CreateHeightfieldInputVolumeNode()
HAPI_NodeId water_id;
result = HAPI_CreateHeightfieldInputVolumeNode(session, heightfield_id, &water_id, "water", num_x_points, num_y_points, voxelsize);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Cook the volume's node to get proper infos
result = HAPI_CookNode(session, water_id, nullptr);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Get the GEO/PART/VOLUME infos
HAPI_GeoInfo_Init(&geo_info);
HAPI_GetGeoInfo(session, water_id, &geo_info);
HAPI_PartInfo_Init(&part_info);
HAPI_GetPartInfo(session, geo_info.nodeId, 0, &part_info);
HAPI_VolumeInfo_Init(&input_volume_info);
HAPI_GetVolumeInfo(session, geo_info.nodeId, 0, &input_volume_info);
// Update the volume infos
input_volume_info.tileSize = 1;
input_volume_info.type = HAPI_VOLUMETYPE_HOUDINI;
input_volume_info.transform = transform;
result = HAPI_SetVolumeInfo(session, water_id, 0, &input_volume_info);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Fill the water mask
std::vector< float > waterData(totalsize);
for (int n = 0; n < totalsize; ++n)
{
waterData[n] = ((float)n / (float)totalsize);
}
result = HAPI_SetHeightFieldData(session, water_id, 0, "water", waterData.data(), 0, totalsize);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Commit the water mask
result = HAPI_CommitGeo(session, water_id);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Connect the water volume to the heightfield's merge
result = HAPI_ConnectNodeInput(session, merge_id, 2, water_id, 0);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
// Cook the HF node
result = HAPI_CookNode(session, heightfield_id, nullptr);
HAPI_TEST_ASSERT(result == HAPI_RESULT_SUCCESS);
HAPI_SaveHIPFile(session, "test_terrain.hip", false);