HAPI_SetHeightFieldData

   335   2   0
User Avatar
Member
2 posts
Joined: April 2015
Offline
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;
}

Attachments:
set_height_field.cpp (2.8 KB)

User Avatar
Member
2 posts
Joined: April 2015
Offline
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;
}
User Avatar
Staff
119 posts
Joined: Sept. 2016
Offline
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;
}
Edited by dpernuit - Sept. 29, 2017 10:46:27
  • Quick Links