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:
- Get the HAPI_AssetInfo with HAPI_GetAssetInfo(). See Assets.
- Get the HAPI_ObjectInfo with HAPI_GetObjectInfo(). See Objects.
- Get the HAPI_GeoInfo with HAPI_GetGeoInfo(). See Geos.
- 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:
hapiTestSession, "HAPI_Test_Nodes_EditableNodeNetwork.otl",
false, &library_id );
HAPI_TEST_ASSERT( library_id >= 0 );
hapiTestSession, -1, "Object/HAPI_Test_Nodes_EditableNodeNetwork",
nullptr, true, &node_id );
int editable_network_count = 0;
hapiTestSession, node_id,
true, &editable_network_count );
HAPI_TEST_ASSERT( editable_network_count == 1 );
hapiTestSession, node_id, &network_node_id, 1 );
hapiTestSession, network_node_id, &network_node_info );
hapiTestSession, network_node_id, "box", nullptr, true, &box_node_id );
hapiTestSession, network_node_id, &network_node_info );
HAPI_TEST_ASSERT( box_node_info.
parentId == network_node_id );
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:
hapiTestSession, "HAPI_Test_Nodes_EditableNodeNetwork.otl",
false, &library_id );
HAPI_TEST_ASSERT( library_id >= 0 );
hapiTestSession, -1, "Object/HAPI_Test_Nodes_EditableNodeNetwork",
nullptr, true, &node_id );
int editable_network_count = 0;
hapiTestSession, node_id,
true, &editable_network_count );
HAPI_TEST_ASSERT( editable_network_count == 1 );
hapiTestSession, node_id, &network_node_id, 1 );
hapiTestSession, network_node_id, &network_node_info );
hapiTestSession, network_node_id, "box", nullptr, true, &box_node_id );
hapiTestSession, network_node_id, &network_node_info );
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:
hapiTestSession, "HAPI_Test_Nodes_EditableNodeNetwork.otl",
false, &library_id );
HAPI_TEST_ASSERT( library_id >= 0 );
hapiTestSession, -1, "Object/HAPI_Test_Nodes_EditableNodeNetwork",
nullptr, true, &node_id );
int editable_network_count = 0;
hapiTestSession, node_id,
true, &editable_network_count );
HAPI_TEST_ASSERT( editable_network_count == 1 );
hapiTestSession, node_id, &network_node_id, 1 );
hapiTestSession, network_node_id, &network_node_info );
int child_count = 0;
hapiTestSession, network_node_id,
false, &child_count );
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 );
hapiTestSession, network_node_id, "box", nullptr, true, &box_node_id );
hapiTestSession, merge_node_id, 1, &input_node_id );
HAPI_TEST_ASSERT( input_node_id == -1 );
hapiTestSession, merge_node_id, 1, box_node_id, 0 );
hapiTestSession, merge_node_id, 1, &input_node_id );
HAPI_TEST_ASSERT( input_node_id == box_node_id );
hapiTestSession, merge_node_id, 1 );
hapiTestSession, merge_node_id, 1, &input_node_id );
HAPI_TEST_ASSERT( input_node_id == -1 );