Softimage SDK Programmer's Guide > Customizing Softimage using the SDK > Custom Operators >

# Static versus Dynamic Operators

Most custom operators have a simple design with a fixed number of mandatory inputs and a single output. This operator is said to be static because its connections are not changed once the operator is connected. Two techniques are supported for creating and connecting static operators: automatic and manual. For static operators it is not necessary to consider port groups at all and only a minimal understanding of Ports is necessary.

The second type of operator is a dynamic operator. This is a more advanced model where an operator expects to have its connections change after it has been connected. For example it may support an optional connection with a object, or support an unlimited number of connections to objects. A typical example is a blending operator, which accepts any number of inputs and blends their data to a single output. The API supports this type of operator and there is a recommended technique for creating these operators.

## Using Port Groups

An operator may have many ports, and these ports are connected to specific data nested underneath an X3DObject or X3DObject. Unlike native Softimage operators, which can take X3DObjects and resolve the precise node to connect to, when building custom operators, you need to make connections to very specific nodes, such as primitives, global or local kinematic states, specific parameters, cluster properties, etc.

As a general guideline, if you are having trouble connecting your own custom operator you will probably do well to divide your ports into more groups—there is no size problem with having many groups. In fact, the only reason you are likely to have to want to group several ports together is based on the multi-instance feature, or dynamic connections.

## Specifying Dynamic Connections

Port groups are necessary when you want to build an operator that connects to an arbitrary number objects of the same type. For example you might want to have an object that can take any number of meshes and blend them together to form a new mesh. Rather than creating a huge number of ports based on some arbitrary maximum number of objects it is better to use multiple instances of a group.

Part of the definition of a group is how many instances it supports - it could be 0 to 1 for an optional group, 1 to 1 for a mandatory single instance group or 1 to 1000 for a mandatory group that supports up to 1000 connected objects. When an group has multiple instances active then there is a separate instance of each port within the group for each group instance, so the operator can read from all the objects.

Important

Note that you can add or remove ports and port groups dynamically until the operator is connected. Once it is connected, its definition is frozen.

## Using the Wizard to Create Dynamic Operators

Although the wizard generates only static code, you can still use it to generate basic operator code which can then be rendered dynamic with a few simple changes. For example, if you wanted to create a generator operator with a variable number of inputs, you could use the wizard to generate code for a static operator with two objects similar to this:

    // This is the implementation for the command to apply the operator, which is
    // is where you set up the ports and hook up the operator for static operators
    function ApplyMyOp_Execute(  )
    {
    	Application.LogMessage("ApplyMyOp_Execute called");
    	// TODO: This generated code works by hardcoding the exact names of the
    	// input and output objects.
    	// If you want to operator to be applied to objects with different names
    	// you way want to generalise this code to determine the objects
    	// based on the Selection or the arguments to this command
    	//
    	// Note: The AddCustomOp command is an alternative way to build the operator
    	var newOp = XSIFactory.CreateObject("MyOp");
    	newOp.AddOutputPort("grid2.polymsh");
    	newOp.AddInputPort("grid.polymsh");
    	newOp.AddInputPort("grid1.polymsh");
    	newOp.Connect();
    	return newOp;
    }

Once you have this basic code you can convert it to be dynamic with just a few modifications:

- Explicitly create a port group that can have a variable number of port objects connected. You do this with the CustomOperator.AddPortGroup or CustomOperator::AddPortGroup method, which you only need to explicitly call in these more complex cases.

- Rewrite the code from using the CustomOperator.AddInputPort or CustomOperator::AddInputPort and CustomOperator.AddOutputPort or CustomOperator::AddOutputPort methods to use the more flexible CustomOperator.AddInputPortByClassID or CustomOperator::AddInputPortByClassID and CustomOperator.AddOutputPortByClassID or CustomOperator::AddOutputPortByClassID methods. These allow you to define what kind of objects need to be connected to a port without requiring specific objects to actually exist.

- Replace the Operator.Connect or Operator::Connect method with the Operator.ConnectToGroup or Operator::ConnectToGroup method. The difference between the two is that the Operator.Connect or Operator::Connect method hooks all connections up at once, whereas the ConnectToGroup method allows you to connect objects individually at different times.

The highlighted snippet above (bold) might be rewritten as follows:

    // ...
    	var ingrp = newOp.AddPortGroup("MyDynPortGroup", 1, 1000);
    	newOp.AddInputPortByClassID(siPrimitiveID, "inprim", ingrp.Index);
    	// ...
    	// Once the objects exist in the scene they can be hooked up
    	newOp.ConnectToGroup(ingrp.Index, grid0.ActivePrimitive);
    	newOp.ConnectToGroup(ingrp.Index, grid1.ActivePrimitive);
    	// etc.

Tip

The SDK workgroup provides a number of constraint examples that demonstrate the differences between static and dynamic operators:

- CenterOp_Basic: Uses a fixed number of inputs and a single output.

- CenterOp_Simple: Similar to the Basic variation, but uses a variable number of inputs.

- CenterOp_Dynamic: Demonstrates how to hook up additional objects even after the operator has been created. by using a single port with multiple instances.