Custom ICENode Categories

There are four main categories of ICENodes:

• Regular Custom ICENode

• Single-Thread Custom ICENode

• Element Generator Custom ICENodes

• Single-Thread/Element Generator Custom ICENode

Regular Custom ICENode

Most custom ICENodes are regular ICENodes processed in multi-threading mode. Regular nodes port connections are based on the following type sets:

Type of data | Structure of the data | Context of the data
siICENodeDataBool | siICENodeStructureSingle | siICENodeContextSingleton
siICENodeDataLong | siICENodeStructureArray | siICENodeContextComponent0D
siICENodeDataFloat | siICENodeStructureAny | siICENodeContextComponent1D
siICENodeDataVector2 |  | siICENodeContextComponent2D
siICENodeDataVector3 |  | siICENodeContextComponent0D2D
siICENodeDataVector4 |  | siICENodeContextElementGenerator
siICENodeDataQuaternion |  | siICENodeContextAny
siICENodeDataMatrix33 |  |
siICENodeDataMatrix44 |  |
siICENodeDataColor4 |  |
siICENodeDataRotation |  |
siICENodeDataShape |  |
siICENodeDataAny |  |

These types define the connectivity of a custom ICENode port. Data and structure types define the port data type that gets propagated through other ICENodes. Context types specify the connection compatibility of custom node ports with other ICENode ports. The context of input and output ports can be specified with any of the context types listed above. Mixing context types together is supported for both input and output ports but must follow the rule below:

siICENodeContextSingleton can be mixed with siICENodeContextComponent0D and/or siICENodeContextComponent1D and/or siICENodeContextComponent2D and/or siICENodeContextComponent0D2D. All ports defined with a mixed context type must be constrained with a context map ID.

| Softimage will log an error at registration time if the above rule is not met, in which case the ICENode registration will be aborted.

| See the CustomVector3ToScalar Example for an example of how to create a regular ICENode.

Example: Custom ICENode Using Mixed Context Types:

    enum IDs
    {
       ID_IN_SINGLETON = 0,
       ID_IN_COMPONENT0D = 1,
       ID_IN_SINGLETON_OR_COMPONENT0D = 2,
       ID_G_100 = 100,
       ID_OUT_FLOAT_ARRAY = 200,
       ID_OUT_VECTOR3F_ARRAY = 201,
       ID_G_OUT_FLOAT_ARRAY = 300,
       ID_G_OUT_VECTOR3F_ARRAY = 301,
       ID_TYPE_CNS = 400,
       ID_STRUCT_CNS,
       ID_CTXT_CNS,
       ID_UNDEF = ULONG_MAX
    };

    XSI::CStatus MixedContextSample_Register( XSI::PluginRegistrar& in_reg );

    XSIPLUGINCALLBACK CStatus XSILoadPlugin( PluginRegistrar& in_reg )
    {
       in_reg.PutAuthor(L"Softimage");
       in_reg.PutName(L"MixedContextSample Plugin");
       in_reg.PutVersion(1,0);

       MixedContextSample_Register( in_reg );

       return CStatus::OK;
    }

    CStatus MixedContextSample_Register( PluginRegistrar& in_reg )
    {
       ICENodeDef nodeDef;
       nodeDef = Application().GetFactory().CreateICENodeDef(L"MixedContextSample");
       CStatus st;

       st = nodeDef.AddPortGroup( ID_G_100 );
       st.AssertSucceeded();

       // This port is a single integer.
       st = nodeDef.AddInputPort( ID_IN_SINGLETON,
           ID_G_100,
           siICENodeDataLong,
           siICENodeStructureSingle,
           siICENodeContextSingleton,
           L"int_singleton", L"int_singleton",
           1L,
           ID_UNDEF, ID_UNDEF, ID_UNDEF );
       st.AssertSucceeded( ) ;

       // Array of positions port.
       st = nodeDef.AddInputPort( ID_IN_COMPONENT0D,
           ID_G_100,
           siICENodeDataVector3,
           siICENodeStructureSingle,
           siICENodeContextSingleton | siICENodeContextComponent0D,
           L"C0D", L"C0D",
           MATH::CVector3f(1.0,1.0,1.0),
           ID_UNDEF, ID_UNDEF, ID_CTXT_CNS );
       st.AssertSucceeded( ) ;

       // A single float or an array of floats matching the positions array count above.
       st = nodeDef.AddInputPort( ID_IN_SINGLETON_OR_COMPONENT0D,
           ID_G_100,
           siICENodeDataFloat,
           siICENodeStructureSingle,
           siICENodeContextSingleton | siICENodeContextComponent0D,
           L"Singleton_or_C0D", L"Singleton_or_C0D",
           1.0f,
           ID_UNDEF, ID_UNDEF, ID_CTXT_CNS );
       st.AssertSucceeded( ) ;

       st = nodeDef.AddPortGroup( ID_G_OUT_FLOAT_ARRAY );
       st.AssertSucceeded();

       // Port to output an array of float values
       st = nodeDef.AddOutputPort( ID_OUT_FLOAT_ARRAY,
           ID_G_OUT_FLOAT_ARRAY,
           siICENodeDataFloat,
           siICENodeStructureArray,
           siICENodeContextSingleton | siICENodeContextComponent0D,
           L"OutFloatArray", L"OutFloatArray",
           ID_UNDEF, ID_UNDEF, ID_CTXT_CNS );
       st.AssertSucceeded( ) ;

       st = nodeDef.AddPortGroup( ID_G_OUT_VECTOR3F_ARRAY );
       st.AssertSucceeded();

       // Port to output an array of CVector3f values
       st = nodeDef.AddOutputPort( ID_OUT_VECTOR3F_ARRAY,
           ID_G_OUT_VECTOR3F_ARRAY,
           siICENodeDataVector3,
           siICENodeStructureSingle,
           siICENodeContextSingleton | siICENodeContextComponent0D,
           L"OutVector3fArray", L"OutVector3fArray",
           ID_UNDEF, ID_UNDEF, ID_CTXT_CNS );
       st.AssertSucceeded( ) ;

       PluginItem nodeItem = in_reg.RegisterICENode( nodeDef );
       nodeItem.PutCategories( L"Custom ICENode" );

       return CStatus::OK;
    }

    XSIPLUGINCALLBACK CStatus MixedContextSample_Evaluate( ICENodeContext& in_ctxt )
    {
       switch( (ULONG)in_ctxt.GetEvaluatedOutputPortID() )
       {
           case ID_OUT_FLOAT_ARRAY :
           {
              CDataArrayLong a_count( in_ctxt, ID_IN_SINGLETON );

              LONG count = a_count[0];
              CDataArray2DFloat aa_out( in_ctxt );
              CDataArray2DFloat::Accessor a_out = aa_out.Resize( 0, count );

              for( ULONG i = 0; i < a_out.GetCount(); ++i )
              {
                  a_out[i] = (float)i;
              }
           }
           break;

           case ID_OUT_VECTOR3F_ARRAY :
           {
              CIndexSet indexSet( in_ctxt );
              CDataArrayVector3f v3fArray( in_ctxt, ID_IN_COMPONENT0D );

              CDataArrayFloat fArray( in_ctxt, ID_IN_SINGLETON_OR_COMPONENT0D );
              float fFact = fArray[0];
              MATH::CVector3f v3f( fFact, fFact*2.0, -fFact );

              CDataArrayVector3f outVector3f( in_ctxt );
              for(CIndexSet::Iterator it = indexSet.Begin(); it.HasNext(); it.Next())
              {
                  fFact *= 1.1;
                  v3f.ScaleInPlace( fFact );
                  outVector3f[it] = v3f;
              }
           }
           break;
       };

       return CStatus::OK;
    }

Single-Thread Custom ICENode

Custom ICENodes are processed in multi-thread by default. Sometimes it may be required to have a way of processing the node in one single thread instead. Single-thread processing gives you the ability to access the entire input data set at once, as opposed to batch processing as it's the case in multi-threading. Single-threading mode is useful for implementing simple mathematical functions such as average, min, or max. You could also use it to implement more sophisticated nodes such as bounding box generators or particle collison detectors. The bounding box generator however can also be done in multi-threading but will force you to perform extra operations in order to find the min and max values among the values collected once the multi-thread processing is done.

The first step to enable the single-thread processing is to set the threading model to siICENodeSingleThreadingModel. This must be done during the node registration with ICENodeDef::PutThreadingModel. The input and output ports need to be defined with respect to the following rules:

1. All input ports must be set with a singleton context (siICENodeEvaluationContextSingleton), if not set with a singleton context then all input ports' context must be constrained by their context type.

2. Output ports can be of any context type.

If the first rule is not met, Softimage will log an error at registration time and then abort the ICENode registration.

The following example demonstrates how the single-threading model can be used to generate a bounding box out of a geometry vertices:

    XSIPLUGINCALLBACK CStatus XSILoadPlugin( PluginRegistrar& in_reg )
    {
       in_reg.PutAuthor(L"Softimage");
       in_reg.PutName(L"BBoxGenerator Plugin");
       in_reg.PutVersion(1,0);

       ICENodeDef nodeOpDef;
       nodeOpDef = Application().GetFactory().CreateICENodeDef(L"BBoxGenerator");

       // Set the threading model to single-thread
       CStatus st;
       st = nodeOpDef.PutThreadingModel(XSI::siICENodeSingleThreading);
       st.AssertSucceeded( ) ;

       // Add input ports and groups.
       st = nodeOpDef.AddPortGroup(ID_G_100);
       st.AssertSucceeded( ) ;

       st = nodeOpDef.AddInputPort(
                                           ID_IN_vector3, // port index
                                           ID_G_100, // group index
                                           siICENodePortDataVector3, // data type
                                           siICENodePortStructureSingle, // structure type
                                           siICENodeEvaluationContextAny, // context type
                                           L"vector3", // port name
                                           L"vector3", // port scripting name
                                           MATH::CVector3f(1.0,1.0,1.0) // default value
       );
       st.AssertSucceeded( ) ;

       // Add output ports and groups.
       st = nodeOpDef.AddPortGroup(ID_G_300);
       st.AssertSucceeded( ) ;

       st = nodeOpDef.AddPortGroup(ID_G_301);
       st.AssertSucceeded( ) ;

       st = nodeOpDef.AddOutputPort(
                                           ID_OUT_minPos, // port index
                                           ID_G_300, // group index
                                           siICENodePortDataVector3, // data type
                                           siICENodePortStructureSingle, // structure type
                                           siICENodeEvaluationContextSingleton, // context type
                                           L"minPos", // port name
                                           L"minPos "// port scripting name
       );
       st.AssertSucceeded( ) ;

       st = nodeOpDef.AddOutputPort(
                                           ID_OUT_maxPos, // port index
                                           ID_G_301, // group index
                                           siICENodePortDataVector3, // data type
                                           siICENodePortStructureSingle, // structure type
                                           siICENodeEvaluationContextSingleton, // context type
                                           L"maxPos", // port name
                                           L"maxPos "// port scripting name
       );
       st.AssertSucceeded( ) ;

       PluginItem nodeItem = in_reg.RegisterICENode(nodeOpDef);
       nodeItem.PutCategories(L"Custom ICENode");

       return CStatus::OK;
    }

    XSIPLUGINCALLBACK CStatus BBoxGenerator_Evaluate( ICENodeContext& in_ctxt )
    {
       ULONG nPortID = in_ctxt.GetEvaluatedOutputPortID();
       switch ( nPortID )
       {
           case ID_OUT_minPos:
           case ID_OUT_maxPos:
           {
              Application xsi;

              CDataArrayVector3f posArray( in_ctxt, ID_IN_vector3 );
              CDataArrayVector3f outData( in_ctxt );

              CVector3f v3fMin(FLT_MAX,FLT_MAX,FLT_MAX);
              CVector3f v3fMax(-FLT_MAX,-FLT_MAX,-FLT_MAX);

              CIndexSet indexSet( in_ctxt );
              for ( CIndexSet::Iterator it = indexSet.Begin(); it.HasNext(); it.Next() )
              {
                  CVector3f& v3f = posArray[ it ];

                  if ( v3fMin.GetX() > v3f.GetX() && v3fMin.GetY() > v3f.GetY() && v3fMin.GetZ() > v3f.GetZ() )
                  {
                     v3fMin = v3f;
                  }
                  else if ( v3fMax.GetX() < v3f.GetX() && v3fMax.GetY() < v3f.GetY() && v3fMax.GetZ() < v3f.GetZ() )
                  {
                     v3fMax = v3f;
                  }
              }

              if ( ID_OUT_minPos == nPortID )
              {
                  xsi.LogMessage( L"BBox Min: " + CString( v3fMin ) );
                  outData[ 0 ] = v3fMin;
              }
              else
              {
                  xsi.LogMessage( L"BBox Max: " + CString( v3fMax ) );
                  outData[ 0 ] = v3fMax;
              }
           }
           break;
       };
       return CStatus::OK;
    }

Element Generator Custom ICENodes

Element generator nodes are like regular custom ICENodes with the additional capability to define the number of elements to process during an evaluation. For now, this feature can only be used to generate particles. The element generator capability is enabled by setting one or more output ports context to siICENodeEvaluationContextElementGenerator during the ICENode registration phase. The siICENodeEvaluationContextElementGenerator context is not compatible with other contexts and must be the only one specified.

Some simple registration rules must be followed when defining the element generator ports:

1. All input ports must be defined with a singleton context (siICENodeEvaluationContextSingleton).

2. All output ports must be defined with a siICENodeEvaluationContextSingleton or siICENodeEvaluationContextElementGenerator.

If these rules are not met, Softimage will log an error at registration time and then abort the ICENode registration.

The number of elements to process must be specified in the BeginEvaluate callback with MDNodeContext::PutNumberOfElementsToProcess. Exceptionally, accessing input port data from the BeginEvaluate callback is supported for generator nodes. Softimage issues an error if the BeginEvaluate callback is missing.

The following ICENode example demonstrates the element generator feature by generating a 2D particle grid:

    #include <xsi_application.h>
    #include <xsi_context.h>
    #include <xsi_pluginregistrar.h>
    #include <xsi_status.h>
    #include <xsi_mdnodecontext.h>
    #include <xsi_mdnodedef.h>
    #include <xsi_factory.h>
    #include <xsi_math.h>
    #include <xsi_vector3f.h>
    #include <xsi_indexset.h>
    #include <xsi_dataarray.h>

    // Defines port, group and map identifiers used for registering the ICENode
    enum IDs
    {
       ID_Size = 1,
       ID_G_100 = 100,
       ID_Vector3D = 200,
       ID_Elements = 201,
       ID_G_300 = 300,
       ID_TMAP = 400,
       ID_SMAP,
       ID_CMAP,
       ID_UNDEF = ULONG_MAX
    };

    using namespace XSI;

    XSIPLUGINCALLBACK CStatus XSILoadPlugin( PluginRegistrar& in_reg )
    {
       in_reg.PutAuthor(L"Softimage");
       in_reg.PutName(L"ICENodeGenerator Plugin");
       in_reg.PutVersion(1,0);

       ICENodeDef nodeOpDef;
       nodeOpDef = Application().GetFactory().CreateICENodeDef( L"ICENodeGenerator" );

       CStatus st;

       // Add a port to specify the size of the grid to generate
       st = nodeOpDef.AddPortGroup(ID_G_100);
       st.AssertSucceeded( ) ;

       st = nodeOpDef.AddInputPort(
                                           ID_Size, // port index
                                           ID_G_100, // group index
                                           siICENodePortDataLong, // data type
                                           siICENodePortStructureSingle, // structure type
                                           siICENodeEvaluationContextSingleton, // context type
                                           L"Size", // port name
                                           L"Size", // port scripting name
                                           10  // default value
       );
       st.AssertSucceeded( ) ;

       // Add output ports and groups.
       st = nodeOpDef.AddPortGroup(ID_G_300);
       st.AssertSucceeded( ) ;

       st = nodeOpDef.AddOutputPort(
                                           ID_Vector3D, // port index
                                           ID_G_300, // group index
                                           siICENodePortDataVector3, // data type
                                           siICENodePortStructureSingle, // structure type
                                           siICENodeEvaluationContextElementGenerator, // context type
                                           L"OutVector3D", // port name
                                           L"OutVector3D "// port scripting name
       );
       st.AssertSucceeded( ) ;

       st = nodeOpDef.AddOutputPort(
                                           ID_Elements, // port index
                                           ID_G_300, // group index
                                           siICENodePortDataLong, // data type
                                           siICENodePortStructureSingle, // structure type
                                           siICENodeEvaluationContextSingleton, // context type
                                           L"Elements", // port name
                                           L"Elements "// port scripting name
       );
       st.AssertSucceeded( ) ;

       PluginItem nodeItem = in_reg.RegisterICENode(nodeOpDef);
       nodeItem.PutCategories(L"Custom ICENode");

       return CStatus::OK;
    }

    XSIPLUGINCALLBACK CStatus ICENodeGenerator_BeginEvaluate( ICENodeContext& in_ctxt )
    {
       CDataArrayLong inSize( in_ctxt, ID_Size );

       // Total number of elements to generate
       ULONG nSize = inSize[ 0 ];
       ULONG nElements = nSize * nSize;
       in_ctxt.PutNumberOfElementsToProcess( nElements );

       return CStatus::OK;
    }

    XSIPLUGINCALLBACK CStatus ICENodeGenerator_Evaluate( ICENodeContext& in_ctxt )
    {
       // The current output port being evaluated...
       ULONG out_portID = in_ctxt.GetEvaluatedOutputPortID( );

       switch( out_portID )
       {
           case ID_Vector3D:
           {
              // Set the output port array with the new elements
              CDataArrayVector3f outData( in_ctxt );
              CDataArrayLong inSize( in_ctxt, ID_Size );

              ULONG nSize = inSize[ 0 ];
              CIndexSet indexSet( in_ctxt );

              for(CIndexSet::Iterator it = indexSet.Begin(); it.HasNext(); it.Next())
              {
                  ULONG nAbsIndex = it.GetAbsoluteIndex( );

                  ULONG x = nAbsIndex % nSize;
                  ULONG y = nAbsIndex / nSize;

                  MATH::CVector3f v;
                  v.PutX( x * 1.0f - nSize/2 );
                  v.PutY( y * 1.0f - nSize/2 );
                  v.PutZ( 0.0f );
                  outData[ it ] = v;
              }
           }
           break;

           case ID_Elements:
           {
              // Returns the number of elements to generate
              CDataArrayLong outData( in_ctxt );
              outData[ 0 ] = in_ctxt.GetNumberOfElementsToGenerate( );
           }
           break;
       };

       return CStatus::OK;
    }

Single-Thread/Element Generator Custom ICENode

This type of ICENode is simply the mix of single-thread and Element Generator category. It allows you to generate the number of elements you need and to access them in one pass.

The following rules must be respected in order to properly register this kind of ICENode:

1. All input ports must be set with a singleton context (siICENodeEvaluationContextSingleton), if not set with a singleton context then all input ports must be constrained by their context type.

2. All output ports must be defined with a siICENodeEvaluationContextSingleton or siICENodeEvaluationContextElementGenerator context.

Autodesk Softimage v7.5