Search - User list
Full Version: HAPI_SetHeightFieldData
Root » Houdini Engine API » HAPI_SetHeightFieldData
robert.white
So I've been struggling with getting just a simple example of HAPI_SetHeightFieldData to work.

I've hacked together a simple example from the documentation, and the provided examples, and while I get a hip file, and it clearly has data in it (just around 1MB), when I open that file the heightfield node is locked and won't render. I also can't use it as an input into another height field nodes with out it triggering an error.

Am I missing a step? Is there no way to actually visualize the results when mucking about with height fields?
Are there any examples of generating a heightfield input from data?

#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();

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 ) );
	
	int start = 0;
	int part_id = 0;
	HAPI_NodeId out_node_id = -1;
	ENSURE_SUCCESS( HAPI_CreateNode( &session, -1, "sop/heightfield", nullptr, true, &out_node_id ) );

	HAPI_GeoInfo out_geo_info = HAPI_GeoInfo_Create();
	ENSURE_SUCCESS( HAPI_GetDisplayGeoInfo( &session, out_node_id, &out_geo_info ) );
	
	HAPI_PartInfo out_part_info = HAPI_PartInfo_Create();
	ENSURE_SUCCESS( HAPI_GetPartInfo( &session, out_geo_info.nodeId, part_id, &out_part_info ) );
	
	HAPI_VolumeInfo height_volume_info = HAPI_VolumeInfo_Create();
	ENSURE_SUCCESS( HAPI_GetVolumeInfo( &session, out_geo_info.nodeId, part_id, &height_volume_info ) );

	const int totalsize = ( height_volume_info.xLength * height_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);
	}
	
	height_volume_info.type = HAPI_VOLUMETYPE_HOUDINI;
	height_volume_info.storage = HAPI_STORAGETYPE_FLOAT;
	height_volume_info.tupleSize = 1;
	height_volume_info.tileSize = 1;
	height_volume_info.zLength = 1;
	height_volume_info.hasTaper = false;
	height_volume_info.xTaper = 0.0;
	height_volume_info.yTaper = 0.0;

	ENSURE_SUCCESS( HAPI_SetVolumeInfo( &session, out_geo_info.nodeId, 0, &height_volume_info ) );

	ENSURE_SUCCESS( HAPI_SetHeightFieldData( &session, out_geo_info.nodeId, 0, heightfieldData.data(), start, totalsize, "height" ) );
	
	ENSURE_SUCCESS( HAPI_CommitGeo( &session, out_geo_info.nodeId ) );
	ENSURE_SUCCESS( HAPI_CookNode( &session, out_geo_info.nodeId, 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;
}
robert.white
So I was finally able to get something that worked.

Instead of pushing the data into a sop/heightfield, I created a an input node and gave it a HAPI_PARTTYPE_VOLUME.
The other missing parts seemed to be that I needed a volume visualization node, and that my volume info transforms all had scales of 0, which kind of made it hard to visualize anything.

In case anyone else runs into a similar problem, this is what finally worked.

If there is a smarter / better way to do this please let me know. I'm kind of flailing in the dark with the current examples.

#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 ) );
	
	HAPI_NodeId volume_visualization_id = -1;
	HAPI_NodeId height_input_id = -1;
	HAPI_NodeId display_node_id = -1;
	HAPI_NodeInfo volume_visualization_info = HAPI_NodeInfo_Create();
	
	HAPI_ParmId vis_id = -1;
	HAPI_ParmId density_id = -1;
	HAPI_ParmInfo parm_info = HAPI_ParmInfo_Create();

	HAPI_NodeId merge_node_id = -1;
	
	ENSURE_SUCCESS( HAPI_CreateNode( &session, -1, "sop/volumevisualization", "MergeNode", false, &volume_visualization_id ) );

	ENSURE_SUCCESS( HAPI_SetParmIntValue( &session, volume_visualization_id, "vismode", 0, 2 ) );
	ENSURE_SUCCESS( HAPI_GetParmIdFromName( &session, volume_visualization_id, "densityfield", &density_id ) );
	ENSURE_SUCCESS( HAPI_GetParmInfo( &session, volume_visualization_id, density_id, &parm_info ) );
	ENSURE_SUCCESS( HAPI_SetParmStringValue( &session, volume_visualization_id, "height", density_id, 0 ) );
	
	ENSURE_SUCCESS( HAPI_CookNode( &session, volume_visualization_id, nullptr ) );

	display_node_id = volume_visualization_id;
	
	
	char * name = "height";
	int start = 0;
	HAPI_PartId part_id = 0;	
	HAPI_NodeId volume_node_id = -1;
	
	ENSURE_SUCCESS( HAPI_CreateInputNode( &session, &volume_node_id, name ) );
	ENSURE_SUCCESS( HAPI_CookNode( &session, volume_node_id, nullptr) );

	HAPI_GeoInfo volume_geo_info = HAPI_GeoInfo_Create();
	ENSURE_SUCCESS( HAPI_GetDisplayGeoInfo( &session, volume_node_id, &volume_geo_info ) );

	HAPI_Transform transform = HAPI_Transform_Create();

	transform.scale[0] = 1000.0f;
	transform.scale[1] = 1000.0f;
	transform.scale[2] = 1.0f;
	
	HAPI_VolumeInfo heightfield_volume_info = HAPI_VolumeInfo_Create();

	heightfield_volume_info.xLength = 500;
	heightfield_volume_info.yLength = 500;
	heightfield_volume_info.zLength = 1;
	
	heightfield_volume_info.minX = 0;
	heightfield_volume_info.minY = 0;
	heightfield_volume_info.minZ = 0;
	
	heightfield_volume_info.transform = transform;

	heightfield_volume_info.type = HAPI_VOLUMETYPE_HOUDINI;
	heightfield_volume_info.storage = HAPI_STORAGETYPE_FLOAT;
	heightfield_volume_info.tupleSize = 1;
	heightfield_volume_info.tileSize = 1;

	heightfield_volume_info.hasTaper = false;
	heightfield_volume_info.xTaper = 0.0;
	heightfield_volume_info.yTaper = 0.0;
	
	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);
	}

	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;

	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, heightfieldData.data(), start, totalsize, name ) );

	ENSURE_SUCCESS( HAPI_CommitGeo( &session, volume_geo_info.nodeId ) );
	ENSURE_SUCCESS( HAPI_CookNode( &session, volume_geo_info.nodeId, nullptr ) );
	
	// ENSURE_SUCCESS( HAPI_ConnectNodeInput( &session, merge_node_id, 0, volume_geo_info.nodeId ) );

	ENSURE_SUCCESS( HAPI_ConnectNodeInput( &session, volume_visualization_id, 0, volume_geo_info.nodeId ) );
	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;
}
dpernuit
Hi Robert,

The SetHeightfieldData() function is a helper function to modify data on an exisiting heightfield.

There is no better way to create a new Heightfield than the way you described above, as Heightfields are just volumes in houdini (two volumes, “height” and “mask”, merged together with an added volume visualization node).

The way you do it on your second example is correct, though you might want to add a merge node between the height volume and the volume visualisation, and add a second volume called “mask”, as most heightfield SOPs will not create a mask volume if its not present, so not having it originally might cause you some trouble down the line.

To create a new heightfield node, you need to:

- Create a volume visualisation node (“sop/volumevisualisation”)
- set its “vismode” parameter to 2 (heightfield)
- set its “densityfield” parameter to “height”
- cook it

- Create a merge node, and connect it to the volume visualisation input

- For the “height” volume:
- Create an Input node
- Create a HAPI_VolumeInfo for it
zLength needs to be set to 1, the minX/Y/Z values to 0 (and the transform will need a non zero scale)
type HAPI_VOLUMETYPE_HOUDINI, storage HAPI_STORAGETYPE_FLOAT with a tuple and tile size of 1, no taper.
- Create a Part Info, with 1 primitive attribute, 1 face, type HAPI_PARTTYPE_VOLUME.
- You can the call HAPI_SetHeightFieldData, using “height” for the volume name.
- Commit the geo, cook it, then connect it to the merge node's first input.

- For the “mask” volume
- Repeat the process used for the height data
( just make sure to name the mask “mask” and connect it to the merge node's second input. Default value for mask is zero )

- Finally, you can cook the Volume visualisation node.


I'll update HAPI docs so the volume page has a proper example of how to create a new heightfield node.

Here's an updated version of your code with the added merge node and mask volume.


#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));

    HAPI_NodeId volume_visualization_id = -1;
    HAPI_NodeId merge_node_id = -1;
    HAPI_NodeId height_input_id = -1;
    HAPI_NodeId mask_input_id = -1;
    HAPI_NodeId display_node_id = -1;
    HAPI_NodeInfo volume_visualization_info = HAPI_NodeInfo_Create();

    HAPI_ParmId vis_id = -1;
    HAPI_ParmId density_id = -1;
    HAPI_ParmInfo parm_info = HAPI_ParmInfo_Create();

    // Create the Heightfield visualisation node, this will be our display node
    // We need to set the visualisation mode to heightfield, and the density field to height.
    ENSURE_SUCCESS(HAPI_CreateNode(&session, -1, "sop/volumevisualization", "Volvis", false, &volume_visualization_id));

    ENSURE_SUCCESS(HAPI_SetParmIntValue(&session, volume_visualization_id, "vismode", 0, 2));
    ENSURE_SUCCESS(HAPI_GetParmIdFromName(&session, volume_visualization_id, "densityfield", &density_id));
    ENSURE_SUCCESS(HAPI_GetParmInfo(&session, volume_visualization_id, density_id, &parm_info));
    ENSURE_SUCCESS(HAPI_SetParmStringValue(&session, volume_visualization_id, "height", density_id, 0));

    ENSURE_SUCCESS(HAPI_CookNode(&session, volume_visualization_id, nullptr));

    display_node_id = volume_visualization_id;

    // Create a merge node
    // This will be connected to the volvis node, and be used to merge the heightfields height and mask(s) volumes
    ENSURE_SUCCESS(HAPI_CreateNode(&session, -1, "merge", "MergeNode", false, &merge_node_id));
    ENSURE_SUCCESS(HAPI_ConnectNodeInput(&session, volume_visualization_id, 0, merge_node_id));

    // Create the height volume
    char * name = "height";
    int start = 0;
    HAPI_PartId part_id = 0;
    HAPI_NodeId volume_node_id = -1;

    ENSURE_SUCCESS(HAPI_CreateInputNode(&session, &volume_node_id, name));
    ENSURE_SUCCESS(HAPI_CookNode(&session, volume_node_id, nullptr));

    HAPI_GeoInfo volume_geo_info = HAPI_GeoInfo_Create();
    ENSURE_SUCCESS(HAPI_GetDisplayGeoInfo(&session, volume_node_id, &volume_geo_info));

    HAPI_Transform transform = HAPI_Transform_Create();

    transform.scale[0] = 1000.0f;
    transform.scale[1] = 1000.0f;
    transform.scale[2] = 1.0f;

    HAPI_VolumeInfo heightfield_volume_info = HAPI_VolumeInfo_Create();

    heightfield_volume_info.xLength = 500;
    heightfield_volume_info.yLength = 500;
    heightfield_volume_info.zLength = 1;

    heightfield_volume_info.minX = 0;
    heightfield_volume_info.minY = 0;
    heightfield_volume_info.minZ = 0;

    heightfield_volume_info.transform = transform;

    heightfield_volume_info.type = HAPI_VOLUMETYPE_HOUDINI;
    heightfield_volume_info.storage = HAPI_STORAGETYPE_FLOAT;
    heightfield_volume_info.tupleSize = 1;
    heightfield_volume_info.tileSize = 1;

    heightfield_volume_info.hasTaper = false;
    heightfield_volume_info.xTaper = 0.0;
    heightfield_volume_info.yTaper = 0.0;

    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);
    }

    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;

    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, heightfieldData.data(), start, totalsize, name));

    ENSURE_SUCCESS(HAPI_CommitGeo(&session, volume_geo_info.nodeId));
    ENSURE_SUCCESS(HAPI_CookNode(&session, volume_geo_info.nodeId, nullptr));

    // Create the mask volume
    // We can reuse the part and volume info  created for the height volume
    char * maskname = "mask";
    HAPI_NodeId mask_volume_node_id = -1;

    ENSURE_SUCCESS(HAPI_CreateInputNode(&session, &mask_volume_node_id, name));
    ENSURE_SUCCESS(HAPI_CookNode(&session, mask_volume_node_id, nullptr));

    HAPI_GeoInfo mask_volume_geo_info = HAPI_GeoInfo_Create();
    ENSURE_SUCCESS(HAPI_GetDisplayGeoInfo(&session, mask_volume_node_id, &mask_volume_geo_info));

    HAPI_VolumeInfo mask_volume_info = heightfield_volume_info;
    heightfield_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, &heightfield_volume_info));

    ENSURE_SUCCESS(HAPI_SetHeightFieldData(&session, mask_volume_geo_info.nodeId, part.id, maskData.data(), 0, totalsize, maskname));

    ENSURE_SUCCESS(HAPI_CommitGeo(&session, mask_volume_geo_info.nodeId));
    ENSURE_SUCCESS(HAPI_CookNode(&session, mask_volume_geo_info.nodeId, nullptr));

    // Connect the height to the merge node
    int next_input = 0;
    ENSURE_SUCCESS(HAPI_ConnectNodeInput(&session, merge_node_id, next_input++, volume_geo_info.nodeId));

    // And the mask
    ENSURE_SUCCESS(HAPI_ConnectNodeInput(&session, merge_node_id, next_input++, mask_volume_geo_info.nodeId));

    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;
}
robert.white
Thanks for that, sorry about responding so late, my trial license ran out and I was waiting to get our actual license.

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;
}
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB