Houdini Engine 7.0
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
Nodes

Nodes Basics

All functions and data in Houdini is represented as nodes. In Houdini Engine, nodes are identified via the HAPI_NodeId. Most things that you can do in Houdini with nodes, you can also do with HAPI. For example, setting parameters on a node. See Parameters.

Given a HAPI_NodeId you can fill a HAPI_NodeInfo struct with a call to HAPI_GetNodeInfo(). You can also get specialized node infos for specific types:

  1. Get the HAPI_AssetInfo with HAPI_GetAssetInfo(). See Assets.
  2. Get the HAPI_ObjectInfo with HAPI_GetObjectInfo(). See Objects.
  3. Get the HAPI_GeoInfo with HAPI_GetGeoInfo(). See Geos.
  4. Get the HAPI_MaterialInfo with HAPI_GetMaterialInfo(). See Materials.

Editable Node Networks

You can promote a node network (any node in Houdini that can have nodes inside it) as an editable node network by adding its relative path to the Editable Nodes field of your asset's Operator Type Properties, under the Node tab.

You can then query the list of editable node networks using HAPI_ComposeChildNodeList(), using HAPI_NODEFLAGS_EDITABLE and HAPI_NODEFLAGS_NETWORK.

Create, Delete, Connect Nodes

Once you have the HAPI_NodeId of a node you know is a node network, like one returned from HAPI_ComposeChildNodeList(), you can start creating, deleting, and connecting nodes inside it.

First, you will probably want to see what is already inside a node network with HAPI_ComposeChildNodeList() with the recursive argument set to false. The number of children in a node network is returned in the child_count argument. You can then get the HAPI_NodeId array with HAPI_GetComposedChildNodeList().

Create Node

You can create nodes with HAPI_CreateNode(). The operator_name parameter should include only the namespace, name, and version. For example, if you have an Object type asset, in the "hapi" namespace, of version 2.0, named "foo", the operator_name here will be: "hapi::foo::2.0". Normally, if you don't have a namespace or version, just pass "foo".

Here's an example of creating a Box SOP node in an exposed OBJ node network:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
hapiTestSession, "HAPI_Test_Nodes_EditableNodeNetwork.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Nodes_EditableNodeNetwork",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the asset node info.
result = HAPI_GetNodeInfo( hapiTestSession, node_id, &node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the editable node network node id.
int editable_network_count = 0;
hapiTestSession, node_id,
true, &editable_network_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( editable_network_count == 1 );
HAPI_NodeId network_node_id = 0;
hapiTestSession, node_id, &network_node_id, 1 );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the editable node network info.
HAPI_NodeInfo network_node_info;
result = HAPI_GetNodeInfo(
hapiTestSession, network_node_id, &network_node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( network_node_info.childNodeCount == 2 );
// Create a node.
HAPI_NodeId box_node_id;
result = HAPI_CreateNode(
hapiTestSession, network_node_id, "box", nullptr, true, &box_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Check new child count.
result = HAPI_GetNodeInfo(
hapiTestSession, network_node_id, &network_node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( network_node_info.childNodeCount == 3 );
// Get and verify the box node info.
HAPI_NodeInfo box_node_info;
result = HAPI_GetNodeInfo( hapiTestSession, box_node_id, &box_node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( box_node_info.parentId == network_node_id );
HAPI_TEST_ASSERT( box_node_info.createdPostAssetLoad == true );

Delete Node

You can delete nodes with HAPI_DeleteNode(). Note that you can only delete nodes that you created with HAPI_CreateNode() or that were created via an internal script in the asset (like Python). More specifically, you can only delete nodes that have their HAPI_NodeInfo::createdPostAssetLoad set to true.

Here's an example of deleting nodes:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
hapiTestSession, "HAPI_Test_Nodes_EditableNodeNetwork.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Nodes_EditableNodeNetwork",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the asset node info.
result = HAPI_GetNodeInfo( hapiTestSession, node_id, &node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the editable node network node id.
int editable_network_count = 0;
hapiTestSession, node_id,
true, &editable_network_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( editable_network_count == 1 );
HAPI_NodeId network_node_id = 0;
hapiTestSession, node_id, &network_node_id, 1 );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the editable node network info.
HAPI_NodeInfo network_node_info;
result = HAPI_GetNodeInfo(
hapiTestSession, network_node_id, &network_node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( network_node_info.childNodeCount == 2 );
// Create a node.
HAPI_NodeId box_node_id;
result = HAPI_CreateNode(
hapiTestSession, network_node_id, "box", nullptr, true, &box_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Delete the node.
result = HAPI_DeleteNode( hapiTestSession, box_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Check new child count.
result = HAPI_GetNodeInfo(
hapiTestSession, network_node_id, &network_node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( network_node_info.childNodeCount == 2 );
// Make sure asset is still valid!
result = HAPI_GetNodeInfo( hapiTestSession, node_id, &node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Try to delete a node that was part of the original asset.
result = HAPI_DeleteNode( hapiTestSession, network_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_INVALID_ARGUMENT );

Connecting Nodes

You can also connect nodes with HAPI_ConnectNodeInput(), disconnect them with HAPI_DisconnectNodeInput(), and query existing connections with HAPI_QueryNodeInput(). HAPI_QueryNodeInput() will return -1 as the node id of the connected node if nothing is connected.

If any of these node changes have an affect on an HAPI-exposed part of the asset, like the Material or Geo, you need to call HAPI_CookNode() for the changes to be incorporated.

Here's an example of connecting two nodes and then disconnecting them - querying the connection at each step:

// Load the library from file.
HAPI_AssetLibraryId library_id = -1;
hapiTestSession, "HAPI_Test_Nodes_EditableNodeNetwork.otl",
false, &library_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( library_id >= 0 );
// Instantiate the asset.
HAPI_NodeId node_id = -1;
result = HAPI_CreateNode(
hapiTestSession, -1, "Object/HAPI_Test_Nodes_EditableNodeNetwork",
nullptr, true, &node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the asset node info.
result = HAPI_GetNodeInfo( hapiTestSession, node_id, &node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the editable node network node id.
int editable_network_count = 0;
hapiTestSession, node_id,
true, &editable_network_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( editable_network_count == 1 );
HAPI_NodeId network_node_id = 0;
hapiTestSession, node_id, &network_node_id, 1 );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_NodeInfo network_node_info;
result = HAPI_GetNodeInfo(
hapiTestSession, network_node_id, &network_node_info );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get the child node ids.
int child_count = 0;
hapiTestSession, network_node_id,
false, &child_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( child_count > 0 );
std::vector< HAPI_NodeId > child_node_ids( child_count );
hapiTestSession,
network_node_id,
child_node_ids.data(),
child_count );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Create a node.
HAPI_NodeId box_node_id;
result = HAPI_CreateNode(
hapiTestSession, network_node_id, "box", nullptr, true, &box_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Get node id of our merge SOP.
const HAPI_NodeId merge_node_id = child_node_ids[ 0 ];
// First query should return nothing at index 1.
HAPI_NodeId input_node_id;
hapiTestSession, merge_node_id, 1, &input_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( input_node_id == -1 );
// Connect the box to our merge SOP.
hapiTestSession, merge_node_id, 1, box_node_id, 0 );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Second query should return the box.
hapiTestSession, merge_node_id, 1, &input_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( input_node_id == box_node_id );
// Disconnect the box.
hapiTestSession, merge_node_id, 1 );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
// Thrid query, after the disconnect, should again return nothing.
hapiTestSession, merge_node_id, 1, &input_node_id );
HAPI_TEST_ASSERT( result == HAPI_RESULT_SUCCESS );
HAPI_TEST_ASSERT( input_node_id == -1 );