Houdini Engine 6.2
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Asset Inputs Samples

Marshalling Geometry Into Houdini

For documentation on marshalling geometry into Houdini, see Marshalling Geometry Into Houdini.

Below is a code snippet that marshals in the simplest of geometry - a triangle - then proceeds attach some string attrbutes onto each point of the triangle, and finally dump the resulting scene into a hip file so it can be opened and viewed in Houdini.

#include <HAPI/HAPI.h>
#include <iostream>
#include <string>
#define ENSURE_SUCCESS( result ) \
if ( (result) != HAPI_RESULT_SUCCESS ) \
{ \
std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl; \
std::cout << getLastError() << std::endl; \
exit( 1 ); \
}
#define ENSURE_COOK_SUCCESS( result ) \
if ( (result) != HAPI_RESULT_SUCCESS ) \
{ \
std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl; \
std::cout << getLastCookError() << std::endl; \
exit( 1 ); \
}
static std::string getLastError();
static std::string getLastCookError();
int
main( int argc, char **argv )
{
HAPI_Session session;
bool bUseInProcess = false;
if(bUseInProcess)
{
// Creates an in process session
}
else
{
// Start and connect to an out of process session
HAPI_ThriftServerOptions serverOptions{ 0 };
serverOptions.autoClose = true;
serverOptions.timeoutMs = 3000.0f;
// Start a HARS named-pipe server named "hapi"
ENSURE_SUCCESS( HAPI_StartThriftNamedPipeServer(&serverOptions, "hapi", nullptr) );
// and create a new HAPI session to use that server
ENSURE_SUCCESS( HAPI_CreateThriftNamedPipeSession(&session, "hapi") );
}
ENSURE_SUCCESS( HAPI_Initialize( &session,
&cookOptions,
true,
-1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr ) );
HAPI_NodeId newNode;
ENSURE_SUCCESS( HAPI_CreateInputNode( &session, &newNode, "Triangle" ) );
ENSURE_SUCCESS( HAPI_CookNode ( &session, newNode, &cookOptions ) );
int cookStatus;
HAPI_Result cookResult;
do
{
cookResult = HAPI_GetStatus( &session, HAPI_STATUS_COOK_STATE, &cookStatus );
}
while (cookStatus > HAPI_STATE_MAX_READY_STATE && cookResult == HAPI_RESULT_SUCCESS);
ENSURE_SUCCESS( cookResult );
ENSURE_COOK_SUCCESS( cookStatus );
newNodePart.type = HAPI_PARTTYPE_MESH;
newNodePart.faceCount = 1;
newNodePart.vertexCount = 3;
newNodePart.pointCount = 3;
ENSURE_SUCCESS( HAPI_SetPartInfo( &session, newNode, 0, &newNodePart ) );
newNodePointInfo.count = 3;
newNodePointInfo.tupleSize = 3;
newNodePointInfo.exists = true;
newNodePointInfo.storage = HAPI_STORAGETYPE_FLOAT;
newNodePointInfo.owner = HAPI_ATTROWNER_POINT;
ENSURE_SUCCESS( HAPI_AddAttribute( &session, newNode, 0, "P", &newNodePointInfo ) );
float positions[ 9 ] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f };
ENSURE_SUCCESS( HAPI_SetAttributeFloatData( &session, newNode, 0, "P", &newNodePointInfo, positions, 0, 3 ) );
int vertices[ 3 ] = { 0, 1, 2 };
ENSURE_SUCCESS( HAPI_SetVertexList( &session, newNode, 0, vertices, 0, 3 ) );
int face_counts [ 1 ] = { 3 };
ENSURE_SUCCESS( HAPI_SetFaceCounts( &session, newNode, 0, face_counts, 0, 1 ) );
char ** strs = new char * [ 3 ];
strs[ 0 ] = _strdup( "strPoint1 " );
strs[ 1 ] = _strdup( "strPoint2 " );
strs[ 2 ] = _strdup( "strPoint3 " );
newNodePointInfo.count = 3;
newNodePointInfo.tupleSize = 1;
newNodePointInfo.exists = true;
newNodePointInfo.storage = HAPI_STORAGETYPE_STRING;
newNodePointInfo.owner = HAPI_ATTROWNER_POINT;
ENSURE_SUCCESS( HAPI_AddAttribute( &session, newNode, 0, "strData", &newNodePointInfo ) );
ENSURE_SUCCESS( HAPI_SetAttributeStringData( &session, newNode, 0, "strData", &newNodePointInfo, (const char ** ) strs, 0, 3 ) );
ENSURE_SUCCESS( HAPI_CommitGeo( &session, newNode ) );
ENSURE_SUCCESS( HAPI_SaveHIPFile( &session, "examples/geometry_marshall.hip", false ) );
HAPI_Cleanup( &session );
return 0;
}
static std::string
getLastError()
{
int bufferLength;
&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
getLastCookError()
{
int bufferLength;
&bufferLength );
char * buffer = new char[ bufferLength ];
HAPI_GetStatusString( nullptr, HAPI_STATUS_COOK_RESULT, buffer, bufferLength );
std::string result( buffer );
delete[] buffer;
return result;
}

Marshalling Point Clouds

For documentation on marshalling point clouds into Houdini, see Marshalling Point Clouds.

The following sample showscases marshalling of a point cloud into Houdini Engine:

#include <HAPI/HAPI.h>
#include <iostream>
#include <string>
#define ENSURE_SUCCESS( result ) \
if ( (result) != HAPI_RESULT_SUCCESS ) \
{ \
std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl; \
std::cout << getLastError() << std::endl; \
exit( 1 ); \
}
#define ENSURE_COOK_SUCCESS( result ) \
if ( (result) != HAPI_RESULT_SUCCESS ) \
{ \
std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl; \
std::cout << getLastCookError() << std::endl; \
exit( 1 ); \
}
static std::string getLastError();
static std::string getLastCookError();
int
main( int argc, char **argv )
{
HAPI_Session session;
bool bUseInProcess = false;
if(bUseInProcess)
{
// Creates an in process session
}
else
{
// Start and connect to an out of process session
HAPI_ThriftServerOptions serverOptions{ 0 };
serverOptions.autoClose = true;
serverOptions.timeoutMs = 3000.0f;
// Start a HARS named-pipe server named "hapi"
ENSURE_SUCCESS( HAPI_StartThriftNamedPipeServer(&serverOptions, "hapi", nullptr) );
// and create a new HAPI session to use that server
ENSURE_SUCCESS( HAPI_CreateThriftNamedPipeSession(&session, "hapi") );
}
ENSURE_SUCCESS( HAPI_Initialize( &session,
&cookOptions,
true,
-1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr ) );
HAPI_NodeId newNode;
ENSURE_SUCCESS( HAPI_CreateInputNode( &session, &newNode, "Point Cloud" ) );
ENSURE_SUCCESS( HAPI_CookNode ( &session, newNode, &cookOptions ) );
int cookStatus;
HAPI_Result cookResult;
do
{
cookResult = HAPI_GetStatus( &session, HAPI_STATUS_COOK_STATE, &cookStatus );
}
while (cookStatus > HAPI_STATE_MAX_READY_STATE && cookResult == HAPI_RESULT_SUCCESS);
ENSURE_SUCCESS( cookResult );
ENSURE_COOK_SUCCESS( cookStatus );
HAPI_GeoInfo newNodeGeoInfo;
ENSURE_SUCCESS( HAPI_GetDisplayGeoInfo( &session, newNode, &newNodeGeoInfo ) );
HAPI_NodeId sopNodeId = newNodeGeoInfo.nodeId;
// Creating the triangle vertices
newNodePart.type = HAPI_PARTTYPE_MESH;
newNodePart.faceCount = 0;
newNodePart.vertexCount = 0;
newNodePart.pointCount = 8;
ENSURE_SUCCESS( HAPI_SetPartInfo( &session, sopNodeId, 0, &newNodePart ) );
newNodePointInfo.count = 8;
newNodePointInfo.tupleSize = 3;
newNodePointInfo.exists = true;
newNodePointInfo.storage = HAPI_STORAGETYPE_FLOAT;
newNodePointInfo.owner = HAPI_ATTROWNER_POINT;
ENSURE_SUCCESS( HAPI_AddAttribute( &session, sopNodeId, 0, "P", &newNodePointInfo ) );
float positions[ 24 ] = { 0.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f,
0.0f, 1.0f, 1.0f};
ENSURE_SUCCESS( HAPI_SetAttributeFloatData( &session, sopNodeId, 0, "P", &newNodePointInfo, positions, 0, 8 ) );
ENSURE_SUCCESS( HAPI_CommitGeo( &session, sopNodeId ) );
ENSURE_SUCCESS( HAPI_SaveHIPFile( &session, "examples/geometry_point_cloud.hip", false ) );
HAPI_Cleanup( &session );
return 0;
}
static std::string
getLastError()
{
int bufferLength;
&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
getLastCookError()
{
int bufferLength;
&bufferLength );
char * buffer = new char[ bufferLength ];
HAPI_GetStatusString( nullptr, HAPI_STATUS_COOK_RESULT, buffer, bufferLength );
std::string result( buffer );
delete[] buffer;
return result;
}

Connecting Assets

For documentation on connecting assets, see Connecting Assets.

The sample below marshals a cube into Houdini Engine, then proceeds to connect that cube to the subdivde node in Houdini. Note that the subdivide node is a standard Houdini node, we did not need to first load its definition from file with HAPI_LoadAssetLibraryFromFile(). The result is then dumped to a file so it can be viewed in Houdini:

#include <HAPI/HAPI.h>
#include <iostream>
#include <string>
#define ENSURE_SUCCESS( result ) \
if ( (result) != HAPI_RESULT_SUCCESS ) \
{ \
std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl; \
std::cout << getLastError() << std::endl; \
exit( 1 ); \
}
#define ENSURE_COOK_SUCCESS( result ) \
if ( (result) != HAPI_RESULT_SUCCESS ) \
{ \
std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl; \
std::cout << getLastCookError() << std::endl; \
exit( 1 ); \
}
static std::string getLastError();
static std::string getLastCookError();
int
main( int argc, char **argv )
{
HAPI_Session session;
bool bUseInProcess = false;
if(bUseInProcess)
{
// Creates an in process session
}
else
{
// Start and connect to an out of process session
HAPI_ThriftServerOptions serverOptions{ 0 };
serverOptions.autoClose = true;
serverOptions.timeoutMs = 3000.0f;
// Start a HARS named-pipe server named "hapi"
ENSURE_SUCCESS( HAPI_StartThriftNamedPipeServer(&serverOptions, "hapi", nullptr) );
// and create a new HAPI session to use that server
ENSURE_SUCCESS( HAPI_CreateThriftNamedPipeSession(&session, "hapi") );
}
ENSURE_SUCCESS( HAPI_Initialize( &session,
&cookOptions,
true,
-1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr ) );
HAPI_NodeId newNode;
ENSURE_SUCCESS( HAPI_CreateInputNode( &session, &newNode, "Cube" ) );
ENSURE_SUCCESS( HAPI_CookNode ( &session, newNode, &cookOptions ) );
int cookStatus;
HAPI_Result cookResult;
do
{
cookResult = HAPI_GetStatus( &session, HAPI_STATUS_COOK_STATE, &cookStatus );
}
while (cookStatus > HAPI_STATE_MAX_READY_STATE && cookResult == HAPI_RESULT_SUCCESS);
ENSURE_SUCCESS( cookResult );
ENSURE_COOK_SUCCESS( cookStatus );
// Creating the triangle vertices
newNodePart.type = HAPI_PARTTYPE_MESH;
newNodePart.faceCount = 6;
newNodePart.vertexCount = 24;
newNodePart.pointCount = 8;
ENSURE_SUCCESS( HAPI_SetPartInfo( &session, newNode, 0, &newNodePart ) );
newNodePointInfo.count = 8;
newNodePointInfo.tupleSize = 3;
newNodePointInfo.exists = true;
newNodePointInfo.storage = HAPI_STORAGETYPE_FLOAT;
newNodePointInfo.owner = HAPI_ATTROWNER_POINT;
ENSURE_SUCCESS( HAPI_AddAttribute( &session, newNode, 0, "P", &newNodePointInfo ) );
float positions[ 24 ] = { 0.0f, 0.0f, 0.0f, // 0
0.0f, 0.0f, 1.0f, // 1
0.0f, 1.0f, 0.0f, // 2
0.0f, 1.0f, 1.0f, // 3
1.0f, 0.0f, 0.0f, // 4
1.0f, 0.0f, 1.0f, // 5
1.0f, 1.0f, 0.0f, // 6
1.0f, 1.0f, 1.0f }; // 7
ENSURE_SUCCESS( HAPI_SetAttributeFloatData( &session, newNode, 0, "P", &newNodePointInfo, positions, 0, 8 ) );
int vertices[ 24 ] = { 0, 2, 6, 4,
2, 3, 7, 6,
2, 0, 1, 3,
1, 5, 7, 3,
5, 4, 6, 7,
0, 4, 5, 1 };
ENSURE_SUCCESS( HAPI_SetVertexList( &session, newNode, 0, vertices, 0, 24 ) );
int face_counts [ 6 ] = { 4, 4, 4, 4, 4, 4 };
ENSURE_SUCCESS( HAPI_SetFaceCounts( &session, newNode, 0, face_counts, 0, 6 ) );
ENSURE_SUCCESS( HAPI_CommitGeo( &session, newNode ) );
HAPI_NodeId subdivideNode;
ENSURE_SUCCESS( HAPI_CreateNode( &session, -1, "Sop/subdivide", "Cube Subdivider", true, &subdivideNode ) );
ENSURE_SUCCESS( HAPI_ConnectNodeInput( &session, subdivideNode, 0, newNode, 0 ) );
ENSURE_SUCCESS( HAPI_SaveHIPFile( &session, "examples/connecting_assets.hip", false ) );
HAPI_Cleanup( &session );
return 0;
}
static std::string
getLastError()
{
int bufferLength;
&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
getLastCookError()
{
int bufferLength;
&bufferLength );
char * buffer = new char[ bufferLength ];
HAPI_GetStatusString( nullptr, HAPI_STATUS_COOK_RESULT, buffer, bufferLength );
std::string result( buffer );
delete[] buffer;
return result;
}

The result of the hip file is shown below. We see the input asset we created, as well as the subdivide asset. The "GlobalNodes" is something that HAPI creates automatically when a new Houdini Engine session is started:

HAPI_AssetInputs_Result_of_Connecting1.png

Diving into the subdivide asset, we see that a subdivide SOP node was created, but with an object merge node automatically created by HAPI feeding into it, with the path of the object merge being set to the Input node:

HAPI_AssetInputs_Result_of_Connecting2.png

Finally, the result of the connected asset is seen (note that you may have to disable the display of the original input cube to see the subdivided cube):

HAPI_AssetInputs_Result_of_Connecting3.png