So I was trying you're updated version with 3.1 and running into some errors.
Finally worked through them all, and thought I'd post the fixed version.
The big gotcha seemed to be that volume visualization node, is created from “volumevisualization” and not “sop/volumevisualization”.
Also for completeness I added a transform node to act as parent for the other nodes, plus it orients the field properly when you open the hip file.
#include <HAPI/HAPI.h> #include <iostream> #include <string> #include <vector> #include <random> #define ENSURE_SUCCESS( result ) \ if ( (result) != HAPI_RESULT_SUCCESS ) \ { \ std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl; \ std::cout << getLastError() << std::endl; \ exit( 1 ); \ } static std::string getLastError(); static std::string getString(HAPI_StringHandle stringHandle); int main(int argc, char ** argv) { HAPI_CookOptions cookOptions = HAPI_CookOptions_Create(); HAPI_Session session; HAPI_CreateInProcessSession(&session); ENSURE_SUCCESS(HAPI_Initialize(&session, &cookOptions, true, -1, nullptr, nullptr, nullptr, nullptr, nullptr)); // Create a transform node, so that the field is oriented properly. HAPI_NodeId transform_node_id = -1; HAPI_NodeInfo transform_info; ENSURE_SUCCESS(HAPI_CreateNode(&session, -1, "SOP/xform", "ZX", true, &transform_node_id)); ENSURE_SUCCESS(HAPI_SetParmFloatValue(&session, transform_node_id, "rx", 0, -90.0f)); ENSURE_SUCCESS(HAPI_SetParmFloatValue(&session, transform_node_id, "ry", 0, -90.0f)); ENSURE_SUCCESS(HAPI_CookNode(&session, transform_node_id, nullptr)); ENSURE_SUCCESS(HAPI_GetNodeInfo(&session, transform_node_id, &transform_info)); // Create the height field visualization, and connect to the xform node. HAPI_NodeId volume_visualization_id = -1; ENSURE_SUCCESS(HAPI_CreateNode(&session, transform_info.parentId, "volumevisualization", "Volvis", false, &volume_visualization_id)); ENSURE_SUCCESS(HAPI_ConnectNodeInput(&session, transform_node_id, 0, volume_visualization_id)); ENSURE_SUCCESS(HAPI_SetParmIntValue(&session, volume_visualization_id, "vismode", 0, 2)); // Hook up the height portion HAPI_ParmId density_id = -1; HAPI_ParmInfo density_info = HAPI_ParmInfo_Create(); ENSURE_SUCCESS(HAPI_GetParmIdFromName(&session, volume_visualization_id, "densityfield", &density_id)); ENSURE_SUCCESS(HAPI_GetParmInfo(&session, volume_visualization_id, density_id, &density_info)); ENSURE_SUCCESS(HAPI_SetParmStringValue(&session, volume_visualization_id, "height", density_id, 0)); // Hooking up the mask portion HAPI_ParmId cdfield_id = -1; HAPI_ParmInfo cdfield_info = HAPI_ParmInfo_Create(); ENSURE_SUCCESS(HAPI_GetParmIdFromName(&session, volume_visualization_id, "cdfield", &cdfield_id)); ENSURE_SUCCESS(HAPI_GetParmInfo(&session, volume_visualization_id, cdfield_id, &cdfield_info)); ENSURE_SUCCESS(HAPI_SetParmStringValue(&session, volume_visualization_id, "height", cdfield_id, 0)); ENSURE_SUCCESS(HAPI_CookNode(&session, volume_visualization_id, nullptr)); // Create a merge node, and connect it to the visualization node. HAPI_NodeId merge_node_id = -1; ENSURE_SUCCESS(HAPI_CreateNode(&session, transform_info.parentId, "merge", "MergeNode", false, &merge_node_id)); ENSURE_SUCCESS(HAPI_ConnectNodeInput(&session, volume_visualization_id, 0, merge_node_id)); // Input data creation. // Create the part info for the height and mask input volumes. HAPI_PartId part_id = 0; HAPI_PartInfo part = HAPI_PartInfo_Create(); part.nameSH = 0; part.id = part_id; part.attributeCounts[HAPI_ATTROWNER_POINT] = 0; part.attributeCounts[HAPI_ATTROWNER_PRIM] = 1; part.attributeCounts[HAPI_ATTROWNER_VERTEX] = 0; part.attributeCounts[HAPI_ATTROWNER_DETAIL] = 0; part.pointCount = 0; part.vertexCount = 0; part.faceCount = 1; part.type = HAPI_PARTTYPE_VOLUME; // Create the input volume transform. HAPI_Transform transform = HAPI_Transform_Create(); transform.scale[0] = 1000.0f; transform.scale[1] = 1000.0f; transform.scale[2] = 1.0f; // Create the input volume info HAPI_VolumeInfo input_volume_info = HAPI_VolumeInfo_Create(); input_volume_info.xLength = 500; input_volume_info.yLength = 500; input_volume_info.zLength = 1; input_volume_info.minX = 0; input_volume_info.minY = 0; input_volume_info.minZ = 0; input_volume_info.type = HAPI_VOLUMETYPE_HOUDINI; input_volume_info.storage = HAPI_STORAGETYPE_FLOAT; input_volume_info.tupleSize = 1; input_volume_info.tileSize = 1; input_volume_info.hasTaper = false; input_volume_info.xTaper = 0.0; input_volume_info.yTaper = 0.0; // Create the height volume char * name = "height"; int start = 0; HAPI_NodeId volume_node_id = -1; HAPI_GeoInfo volume_geo_info = HAPI_GeoInfo_Create(); ENSURE_SUCCESS(HAPI_CreateInputNode(&session, &volume_node_id, name)); ENSURE_SUCCESS(HAPI_CookNode(&session, volume_node_id, nullptr)); ENSURE_SUCCESS(HAPI_GetDisplayGeoInfo(&session, volume_node_id, &volume_geo_info)); ENSURE_SUCCESS(HAPI_ConnectNodeInput(&session, merge_node_id, 0, volume_geo_info.nodeId)); HAPI_VolumeInfo heightfield_volume_info = input_volume_info; heightfield_volume_info.transform = transform; const int totalsize = (heightfield_volume_info.xLength * heightfield_volume_info.yLength); std::vector< float > heightfieldData(totalsize); std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<> dis(1, 2); for (int n = 0; n < totalsize; ++n) { heightfieldData[n] = (float)dis(gen); } ENSURE_SUCCESS(HAPI_SetPartInfo(&session, volume_geo_info.nodeId, part.id, &part)); ENSURE_SUCCESS(HAPI_SetVolumeInfo(&session, volume_geo_info.nodeId, part.id, &heightfield_volume_info)); ENSURE_SUCCESS(HAPI_SetHeightFieldData(&session, volume_geo_info.nodeId, part.id, name, heightfieldData.data(), start, totalsize)); ENSURE_SUCCESS(HAPI_CommitGeo(&session, volume_geo_info.nodeId)); ENSURE_SUCCESS(HAPI_CookNode(&session, volume_geo_info.nodeId, nullptr)); // Creating the Mask input char * maskname = "mask"; HAPI_NodeId mask_volume_node_id = -1; HAPI_GeoInfo mask_volume_geo_info = HAPI_GeoInfo_Create(); // Create the input node, and connect it to the merge node ENSURE_SUCCESS(HAPI_CreateInputNode(&session, &mask_volume_node_id, maskname)); ENSURE_SUCCESS(HAPI_CookNode(&session, mask_volume_node_id, nullptr)); ENSURE_SUCCESS(HAPI_GetDisplayGeoInfo(&session, mask_volume_node_id, &mask_volume_geo_info)); ENSURE_SUCCESS(HAPI_ConnectNodeInput(&session, merge_node_id, 1, mask_volume_geo_info.nodeId)); HAPI_VolumeInfo mask_volume_info = input_volume_info; mask_volume_info.transform = transform; // fill the mask with zeros std::vector< float > maskData(totalsize); for (int n = 0; n < totalsize; ++n) { maskData[n] = 0.0f; } // we can reuse the height part info here ENSURE_SUCCESS(HAPI_SetPartInfo(&session, mask_volume_geo_info.nodeId, part.id, &part)); ENSURE_SUCCESS(HAPI_SetVolumeInfo(&session, mask_volume_geo_info.nodeId, part.id, &mask_volume_info)); ENSURE_SUCCESS(HAPI_SetHeightFieldData(&session, mask_volume_geo_info.nodeId, part.id, maskname, maskData.data(), 0, totalsize)); ENSURE_SUCCESS(HAPI_CommitGeo(&session, mask_volume_geo_info.nodeId)); ENSURE_SUCCESS(HAPI_CookNode(&session, mask_volume_geo_info.nodeId, nullptr)); // Cook and save the node out to a .hip file to prove this works. ENSURE_SUCCESS(HAPI_CookNode(&session, volume_visualization_id, nullptr)); ENSURE_SUCCESS(HAPI_SaveHIPFile(&session, "height_field.hip", false)); HAPI_Cleanup(&session); return 0; } static std::string getLastError() { int bufferLength; HAPI_GetStatusStringBufLength(nullptr, HAPI_STATUS_CALL_RESULT, HAPI_STATUSVERBOSITY_ERRORS, &bufferLength); char * buffer = new char[bufferLength]; HAPI_GetStatusString(nullptr, HAPI_STATUS_CALL_RESULT, buffer, bufferLength); std::string result(buffer); delete[] buffer; return result; } static std::string getString(HAPI_StringHandle stringHandle) { if (stringHandle == 0) { return ""; } int bufferLength; HAPI_GetStringBufLength(nullptr, stringHandle, &bufferLength); char * buffer = new char[bufferLength]; HAPI_GetString(nullptr, stringHandle, buffer, bufferLength); std::string result(buffer); delete[] buffer; return result; }