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

For documentation on the asset inputs APIs, see Curves.

The Curve Node

Below is a sample that creates a NURBS curve with 4 control points.

#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();
static std::string getString( HAPI_StringHandle stringHandle );
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 curveNode;
ENSURE_SUCCESS( HAPI_CreateNode( &session, -1, "sop/curve", "NURBS", false, &curveNode ) );
ENSURE_SUCCESS( HAPI_CookNode ( &session, curveNode, &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_NodeInfo curveNodeInfo;
ENSURE_SUCCESS( HAPI_GetNodeInfo( &session, curveNode, &curveNodeInfo ) );
HAPI_ParmInfo * parmInfos = new HAPI_ParmInfo[ curveNodeInfo.parmCount ];
ENSURE_SUCCESS( HAPI_GetParameters( &session, curveNode, parmInfos, 0, curveNodeInfo.parmCount ) );
int coordsParmIndex = -1;
int typeParmIndex = -1;
for (int i = 0; i < curveNodeInfo.parmCount; i++)
{
std::string parmName = getString( parmInfos[i].nameSH );
if ( parmName == "coords" )
{
coordsParmIndex = i;
}
if ( parmName == "type" )
{
typeParmIndex = i;
}
}
if ( coordsParmIndex == -1 || typeParmIndex == -1 )
{
std::cout << "Failure at " << __FILE__ << ": " << __LINE__ << std::endl;
std::cout << "Could not find coords/type parameter on curve node" << std::endl;
}
ENSURE_SUCCESS( HAPI_GetParameters( &session, curveNode, &parm, typeParmIndex, 1 ) );
int typeValue = 1;
ENSURE_SUCCESS( HAPI_SetParmIntValues( &session, curveNode, &typeValue, parm.intValuesIndex, 1 ) );
ENSURE_SUCCESS( HAPI_GetParameters ( &session, curveNode, &parm, coordsParmIndex, 1 ) );
ENSURE_SUCCESS( HAPI_SetParmStringValue( &session, curveNode, "-4,0,4 -4,0,-4 4,0,-4 4,0,4", parm.id, 0 ) );
HAPI_SaveHIPFile( &session, "examples/nurbs_curve.hip", true );
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;
}
static std::string
getString( HAPI_StringHandle stringHandle )
{
if ( stringHandle == 0 )
{
return "";
}
int bufferLength;
stringHandle,
&bufferLength );
char * buffer = new char[ bufferLength ];
HAPI_GetString ( nullptr, stringHandle, buffer, bufferLength );
std::string result( buffer );
delete [] buffer;
return result;
}

The output file, when opened in Houdini, shows the following:

HAPI_Curves_CurveInput.png

Curve Marshalling

For documentation on curve marshaling, see Curve Marshalling.

The following code uses curve marshaling to create the same curve as in The Curve Node example:

#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, "Curve" ) );
ENSURE_SUCCESS( HAPI_CookNode ( &session, newNode, &cookOptions ) );
int cookStatus;
HAPI_Result cookResult;
do
{
cookResult = HAPI_GetStatus( &session, HAPI_STATUS_COOK_STATE, &cookStatus );
std::cout << "Waiting on cook." << std::endl;
}
while (cookStatus > HAPI_STATE_MAX_READY_STATE && cookResult == HAPI_RESULT_SUCCESS);
ENSURE_SUCCESS( cookResult );
ENSURE_COOK_SUCCESS( cookStatus );
newNodePart.type = HAPI_PARTTYPE_CURVE;
newNodePart.faceCount = 1;
newNodePart.vertexCount = 4;
newNodePart.pointCount = 4;
ENSURE_SUCCESS( HAPI_SetPartInfo( &session, newNode, 0, &newNodePart ) );
HAPI_CurveInfo curveInfo;
curveInfo.curveCount = 1;
curveInfo.vertexCount = 4;
curveInfo.knotCount = 8;
curveInfo.isPeriodic = false;
curveInfo.order = 4;
curveInfo.hasKnots = true;
ENSURE_SUCCESS( HAPI_SetCurveInfo( &session, newNode, 0, &curveInfo ) );
int curveCount = 4;
ENSURE_SUCCESS( HAPI_SetCurveCounts( &session, newNode, newNodePart.id, &curveCount, 0, 1 ) );
float curveKnots[ 8 ] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f };
ENSURE_SUCCESS( HAPI_SetCurveKnots( &session, newNode, newNodePart.id, curveKnots, 0, 8 ) );
attrInfo.count = 4;
attrInfo.tupleSize = 3;
attrInfo.exists = true;
ENSURE_SUCCESS( HAPI_AddAttribute( &session, newNode, 0, "P", &attrInfo ) );
float positions [ 12 ] = { -4.0f, 0.0f, 4.0f,
-4.0f, 0.0f, -4.0f,
4.0f, 0.0f, -4.0f,
4.0f, 0.0f, 4.0f };
ENSURE_SUCCESS( HAPI_SetAttributeFloatData( &session, newNode, 0, "P", &attrInfo, positions, 0, 4 ) );
ENSURE_SUCCESS( HAPI_CommitGeo( &session, newNode ) );
ENSURE_SUCCESS( HAPI_SaveHIPFile( &session, "examples/curve_marshall.hip", true ) );
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;
}

Curve Output

For documentation on curve outputs, see Curve Output.

The code sample below shows loading the above asset via code and displaying information about the curve.

#include <HAPI/HAPI.h>
#include <iostream>
#include <string>
#include <vector>
#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();
static std::string getString( HAPI_StringHandle stringHandle );
static void printCurveInfo ( HAPI_ObjectInfo &objInfo, HAPI_GeoInfo &geoInfo, HAPI_PartInfo &partInfo);
int
main( int argc, char **argv)
{
const char * hdaFile = argc == 2 ? argv[ 1 ] : "examples/nurbs_curve.hda";
HAPI_Session session;
ENSURE_SUCCESS( HAPI_Initialize( &session,
&cookOptions,
true,
-1,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr ) );
HAPI_AssetLibraryId assetLibId;
ENSURE_SUCCESS( HAPI_LoadAssetLibraryFromFile( &session, hdaFile, true, &assetLibId ) );
int assetCount;
ENSURE_SUCCESS( HAPI_GetAvailableAssetCount( &session, assetLibId, &assetCount ) );
if (assetCount > 1)
{
std::cout << "Should only be loading 1 asset here" << std::endl;
exit ( 1 );
}
ENSURE_SUCCESS( HAPI_GetAvailableAssets( &session, assetLibId, &assetSh, assetCount ) );
std::string assetName = getString( assetSh );
HAPI_NodeId nodeId;
ENSURE_SUCCESS( HAPI_CreateNode( &session, -1, assetName.c_str(), "Loaded Asset", false, &nodeId ) );
ENSURE_SUCCESS( HAPI_CookNode( &session, nodeId, &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_NodeInfo nodeInfo;
ENSURE_SUCCESS( HAPI_GetNodeInfo( &session, nodeId, &nodeInfo ) );
HAPI_ObjectInfo objInfo;
ENSURE_SUCCESS( HAPI_GetObjectInfo( &session, nodeId, &objInfo ) );
int childCount;
ENSURE_SUCCESS( HAPI_ComposeChildNodeList( &session, nodeId, HAPI_NODETYPE_SOP, HAPI_NODEFLAGS_SOP_CURVE, true, &childCount ) );
HAPI_NodeId *nodeChildren = new HAPI_NodeId[ childCount ];
ENSURE_SUCCESS( HAPI_GetComposedChildNodeList( &session, nodeId, nodeChildren, childCount ) );
for ( int i = 0; i < childCount; ++i )
{
HAPI_NodeId &child = nodeChildren[ i ];
ENSURE_SUCCESS( HAPI_GetNodeInfo( &session, child, &info ) );
if ( info.type != HAPI_NODETYPE_SOP )
{
continue;
}
HAPI_GeoInfo geoInfo;
ENSURE_SUCCESS( HAPI_GetGeoInfo( &session, child, &geoInfo ) );
for ( int partIndex = 0; partIndex < geoInfo.partCount; ++partIndex )
{
HAPI_PartInfo partInfo;
ENSURE_SUCCESS( HAPI_GetPartInfo( &session, child, partIndex, &partInfo ) );
if ( partInfo.type == HAPI_PARTTYPE_CURVE )
{
printCurveInfo( objInfo, geoInfo, partInfo );
}
}
}
char in;
std::cout << "Enter some input to exit" << std::endl;
std::cin >> in;
HAPI_Cleanup( &session );
HAPI_Shutdown( &session );
return 0;
}
static void printCurveInfo ( HAPI_ObjectInfo &objInfo, HAPI_GeoInfo &geoInfo, HAPI_PartInfo &partInfo)
{
std::cout << "Object Node: " << objInfo.nodeId << ", Geometry Node: " << geoInfo.nodeId <<
", Part ID: " << partInfo.id << std::endl;
HAPI_CurveInfo curveInfo;
ENSURE_SUCCESS( HAPI_GetCurveInfo( nullptr, geoInfo.nodeId, partInfo.id, &curveInfo ) );
if ( curveInfo.curveType == HAPI_CURVETYPE_LINEAR )
std::cout << "curve mesh type = Linear" << std::endl;
else if ( curveInfo.curveType == HAPI_CURVETYPE_BEZIER )
std::cout << "curve mesh type = Bezier" << std::endl;
else if ( curveInfo.curveType == HAPI_CURVETYPE_NURBS )
std::cout << "curve mesh type = Nurbs" << std::endl;
else
std::cout << "curve mesh type = Unknown" << std::endl;
std::cout << "curve count: " << curveInfo.curveCount << std::endl;
int vertexOffset = 0, knotOffset = 0;
for ( int curveIndex = 0; curveIndex < curveInfo.curveCount; ++curveIndex )
{
std::cout << "Curve " << curveIndex + 1 << " of " << curveInfo.curveCount << std::endl;
// Number of control vertices
int numVertices;
ENSURE_SUCCESS( HAPI_GetCurveCounts( nullptr, geoInfo.nodeId, partInfo.id, &numVertices, curveIndex, 1 ) );
std::cout << "Number of vertices : " << numVertices << std::endl;
// Order of this particular curve
int order;
if ( curveInfo.order != HAPI_CURVE_ORDER_VARYING
&& curveInfo.order != HAPI_CURVE_ORDER_INVALID )
{
order = curveInfo.order;
}
else
{
ENSURE_SUCCESS( HAPI_GetCurveOrders( nullptr, geoInfo.nodeId, partInfo.id, &order, curveIndex, 1 ) );
}
std::cout << "Curve Order: " << order << std::endl;
// If there's not enough vertices, then don't try to create the curve.
if ( numVertices < order )
{
std::cout << "Not enough vertcies on curve " << curveIndex + 1 << " of "
<< curveInfo.curveCount << ": skipping to next curve" << std::endl;
// The curve at curveIndex will have numVertices vertices, and may have
// some knots. The knot count will be numVertices + order for
// nurbs curves.
vertexOffset += numVertices * 4;
knotOffset += numVertices + order;
continue;
}
HAPI_AttributeInfo attrInfoP;
ENSURE_SUCCESS( HAPI_GetAttributeInfo( nullptr, geoInfo.nodeId, partInfo.id, "P", HAPI_ATTROWNER_POINT, &attrInfoP ) );
std::vector< float > pArray( attrInfoP.count * attrInfoP.tupleSize );
ENSURE_SUCCESS( HAPI_GetAttributeFloatData( nullptr, geoInfo.nodeId, partInfo.id, "P", &attrInfoP,
-1, &pArray.front(), 0, attrInfoP.count ) );
for ( int j = 0; j < numVertices; j++ )
{
std::cout << "CV " << j + 1 << ": " << pArray[ j * 3 + 0 ] << ","
<< pArray[ j * 3 + 1 ] << ","
<< pArray[ j * 3 + 2 ] << std::endl;
}
if ( curveInfo.hasKnots)
{
std::vector< float > knots;
knots.resize( numVertices + order );
ENSURE_SUCCESS( HAPI_GetCurveKnots( nullptr, geoInfo.nodeId, partInfo.id, &knots.front(),
knotOffset, numVertices + order ) );
for( int j = 0; j < numVertices + order; j++ )
{
std::cout
<< "knot " << j + 1
<< ": " << knots[ j ] << std::endl;
}
}
// NOTE: Periodicity is always constant, so periodic and
// non-periodic curve meshes will have different parts.
// The curve at i will have numVertices vertices, and may have
// some knots. The knot count will be numVertices + order for
// nurbs curves.
vertexOffset += numVertices * 4;
knotOffset += numVertices + order;
}
}
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;
}
static std::string
getString( HAPI_StringHandle stringHandle )
{
if ( stringHandle == 0 )
{
return "";
}
int bufferLength;
stringHandle,
&bufferLength );
char * buffer = new char[ bufferLength ];
HAPI_GetString ( nullptr, stringHandle, buffer, bufferLength );
std::string result( buffer );
delete [] buffer;
return result;
}