Applying operators is a three-part process that includes:
There are several ways to apply custom operators, depending on what kind of operator you are building (static vs. dynamic) and how much control you need over the connection process.
If you are building an operator with the wizard, it can generate the code to instantiate the operator and make the port connections (the Generate Command to Apply the Operator option is selected by default).
This is the simplest case, a static operator with a fixed number of port connections and includes cases where the objects to be connected are not yet created. You can use the AddCustomOp method or command, which takes care of instantiating the operator, specifying the ports to hook up, and making the connections.
Scripting Command: AddCustomOp
Object Model: ProjectItem.AddCustomOp, Parameter.AddCustomOp
If you want to break the application process into its component parts you can also use specific object model and C++ API functions to perform each part. This is the way that the wizard generates the code.
You can use the CreateObject function to instantiate the operator by specifying the PluginItem name used to register the operator with PluginRegistrar.RegisterOperator or PluginRegistrar::RegisterOperator. The CreateObject function returns a pointer to the instantiated operator which you need in order to define the port connections and connect the objects to ports.
// JScript var op = XSIFactory.CreateObject( "MyCustomOperator" ); // C++ Factory factory = Application().GetFactory(); CustomOperator op( factory.CreateObject(L"MyCustomOperator") );
For static operators you can define the port connections with a set of methods and functions that require the objects to be connected to exist. You can define an input port (AddInputPort), an output port (AddOutputPort), or both at the same time (AddIOPort, for example, for deformer operators).
// JScript op.AddInputPort( "sphere.polymsh.cls.Point" ); // C++ API CRef obj; obj.Set( "sphere.polymsh.cls.Point" ); op.AddIOPort( obj );
You can use the Connect method to finish the port connections. In the case of static operators, it is usually not necessary to pass in any arguments for the connection specification (such as an object or a connection set) because the explicit object needed was specified when the port connections were defined.
Applying dynamic operator is very similar to applying static operators manually, but you also have to add port groups before declaring port connections. In addition, you need to add ports by specifying the class ID instead of explicit objects since your connection objects may not exist yet. Finally, the ConnectToGroup method allows you to add instances of port objects to groups, thereby allowing you to connect in a truly dynamic way.
The AddPortGroup method allows you to specify how many objects can be connected to within this port group. By default, there is only one port connection allowed, but you can specify a valid range, as in the case of an operator that averages between at least two and no more than ten inputs:
myOp.AddPortGroup( "Inputs", 2, 10, "MyPolygonFilter" );
This method also allows you to specify a filter to validate any input object to connect to the group.
Declaring Potential Port Connections
The methods used to define static port connections manually need an explicit path to an object that already exists, whereas dynamic connections need to declared in a more hypothetical manner. For this reason, the CustomOperator or CustomOperator also provides methods that allow you to add ports by class ID:
myOp.AddInputPortByClassID( siPrimitiveID, "inputprim", 1 /* index of input port group */ );
Dynamically Connecting To Groups
The ConnectToGroup method allows you to add objects to a port group at any time after it is defined. However, this method can only be used on a port group containing a single port, and the object passed in the Object argument must be a specific node (that is, a KinematicState or KinematicState or Primitive or Primitive rather than an X3DObject or X3DObject):
newOp.ConnectToGroup( 0, mySphere.Kinematics.Local );
Examples of Applying Custom Operators
These examples demonstrate a number of approaches for applying custom operators:
JScript Example: Applying a Static Operator (Manual)
This example is extracted from the code generated by the SDK Operator Wizard. Note that because the ports are specified explicitly, no parameters need to be passed to the Operator.Connect or Operator::Connect method (see C++ Example: Applying a Static Operator (Automatic) for an automatic version of this example).
// Instantiate the operator var newOp = XSIFactory.CreateObject("MyOp"); // Specify the ports to hook up newOp.AddOutputPort("sphere.polymsh"); newOp.AddInputPort("sphere.polymsh"); // Connect the operator newOp.Connect();
C++ Example: Applying a Static Operator (Automatic)
This snippet is similar to the wizard-generated code in JScript Example: Applying a Static Operator (Manual) but uses the AddCustomOp function which combines the three steps (instantiate, declare ports, connect) into one. The JScript version and this one are examples of static operators, so the port connections must already exist.
// Output of the operator will be this mesh CRef obj; obj.Set(L"sphere.polymsh"); Primitive prim(obj); // AddCustomOp takes an array of inputs CRefArray inputs(3); // Get the objects to which the operator will be connected inputs[0].Set(L"grid.polymsh"); inputs[1].Set(L"null.kine.global"); inputs[2].Set(L"null2.kine.global"); // Apply the custom operator prim.AddCustomOp("MyOp", inputs);
Check out how the equivalent code in JScript using the AddCustomOp command takes only one line:
AddCustomOp("MyOp", "sphere.polymsh", "grid.polymsh,null.kine.global,null2.kine.global");
JScript Example: Applying an Operator Before Port Targets Exist
In this example the operator ports are declared before any matching objects actually exist in the scene, thanks to the AddIOPortByClassID method, which simply declares what kind of object to expect to connect to the port, rather than the exact object.
// Instantiate the operator var newOp = XSIFactory.CreateObject( "MyOp" ); // Specify the ports to hook up, this time using AddXXXPortByClassID which // allows you to add the port without specifying an existing object to connect newOp.AddIOPortByClassID( siKinematicStateID ); // ... // The operator connections are set up before the target object exists var sph = Application.ActiveSceneRoot.AddGeometry( "Sphere", "MeshSurface" ); newOp.ConnectToGroup( 0, sph.Kinematics.Local );
C++ Example: Applying a Dynamic Operator
This example demonstrates how to apply an operator with a variable number of objects connected by adding a port group that can accept between 2 and 10 connections and defining the ports using only a class ID.
// Instantiate the operator CustomOperator newOp = Application().GetFactory().CreateObject( L"MyOp" ); // Define an input port group that can accept at least 2 and up to 10 connections newOp.AddPortGroup( L"Output" ); newOp.AddPortGroup( L"Inputs", 2, 10 ); // Specify the ports to hook up, using AddXXXPortByClassID which allows you to // add the port without specifying an existing object to connect newOp.AddOutputPortByClassID( siClusterPropertyID, L"", 0 /* output port group */ ); newOp.AddInputPortByClassID( siClusterPropertyID, L"", 1 /* input port group */ ); // ... // When it's time to connect a new port instance to the operator CRef wp; CRef out_instance; wp.Set( L"sphere.polymsh.cls.Point.Weight_Map" ); newOp.ConnectToGroup( 1, wp, out_instance ); // etc.