There are examples throughout the documentation and some provided as part of the XSISDK installation. This section provides an example of a runtime custom property that wants to be self-installing and some links to the examples installed under the XSISDK folder:
• Examples in the XSISDK Folder
• JScript Example: Implementing a Self-Installing-ish Custom Property
If you look under your %XSISDK%\examples\operators folder, these examples are provided as source code:
VBScript Example
• Splatter—creates the effect of squishing an object against the floor (as the user translates the sphere below Y=0, the points move away from the Y axis). For comparions purposes, there is also a C++ API version.
• ParticleOpExample—demonstrates how to write a compiled XSI operator that manipulates a particle cloud to simulate flocking behavior.
• SimpleOperator— writes the operator's ConstantValue parameter to the global posx of an object using the C++ API.
• Splatter—creates the effect of squishing an object against the floor (as the user translates the sphere below Y=0, the points move away from the Y axis). For comparions purposes, there is also a VBScript version.
• VertexColorMixer—illustrates how to create an operator that reads from many vertex color properties and writes a processed result to a vertex color property acting as the mixed result.
JScript Example: Implementing a Self-Installing-ish Custom Property
This example demonstrates how you can use Menu Callback Functions to create and connect a runtime custom property all defined within a single plug-in file.
What we’re trying to achieve with the operator is to change the value of the blue parameter for the AmbientLighting property as the trigger gets closer to (or farther from) thebackdrop:
1. Let’s get started by simply setting it up as if it’s a normal runtime custom operator that we will run from the Script Editor. First, let’s create the backdrop and encase the code in a convenience function:
function SetUpBackDrop()
{
// We’ll make the backdrop from a grid
var bd = ActiveSceneRoot.AddGeometry( "Grid", "MeshSurface", "BackDrop" );
// Since it's a backdrop, it should probably be vertical...
bd.Kinematics.Global.Parameters( "rotx" ).Value = 90;
// ...and really big
bd.Kinematics.Global.Parameters( "sclx" ).Value = 5;
bd.Kinematics.Global.Parameters( "sclz" ).Value = 5;
// Return the newly created backdrop
return bd;
}
2. Next, we’ll do the same for the trigger:
function SetUpDonut()
{
// The donut will be a smaller version of the preset torus
var dt = ActiveSceneRoot.AddGeometry( "Torus", "MeshSurface", "Donut" );
dt.radius.Value = 1.5;
dt.sectionradius.Value = 0.7;
dt.subdivu.Value = 6;
dt.subdivv.Value = 6;
dt.Kinematics.Global.Parameters( "posz" ).Value = 5;
// Make the Ambient Lighting local and then change the value
var al = dt.Properties( "Ambient Lighting" );
MakeLocal( al, siDefaultPropagation );
//al.Parameters( "ambience" ).Parameters( "red" ).Value = 0.683;
//al.Parameters( "ambience" ).Parameters( "blue" ).Value = 0.683;
//al.Parameters( "ambience" ).Parameters( "green" ).Value = 0.683;
// Return the newly created donut
return dt;
}
3. Now we can set up the Update function. We’ll take the path between the farthest point the trigger ever traveled away from the backdrop (in Z), compute the current location of the trigger from the backdrop and calculate the trigger’s current position along the path as a percentage to set as the new value for the output blue AmbientLighting parameter:
function CusOp_Update( ctx, out, inDposz, inBposz )
{
// Get the end of the path to calculate the percentage
var pathend = ctx.Operator.Parameters( "endofpath" ).Value;
if (pathend < Math.abs(inDposz.Value)) {
// Make sure the parameter value is always the most extreme value
pathend = Math.abs(inDposz.Value);
ctx.Operator.Parameters( "endofpath" ).Value = pathend;
}
// The closer the donut is to the backdrop, the more intense the ambient
// blue color will be
var percalongpath = Math.abs(inDposz.Value - inBposz.Value)/pathend;
// Update the output value
out.value = percalongpath;
}
4. For the Init function, we’ll get the starting position of the trigger and use it to set the initial value of our endofpath custom operator parameter. We can get both the parameter and the trigger’s (input port) value from the UpdateContext object:
function CusOp_Init( ctx )
{
// Get the parameter that's defined with the scripted operator from the
// input context (UpdateContext object) using the following thread:
//
// UpdateContext.Operator -> Operator.Parameters ->
// ParameterCollection.Item -> Parameter.Value
//
var cpset_param = ctx.Operator.Parameters( "endofpath" );
// Get the trigger's current (initial) position to use as the value of the
// 'endofpath' parameter from the input context using the following:
//
// UpdateContext.Operator -> Operator.InputPorts ->
// InputPortCollection.Item -> InputPort.Value
//
var trigger_posz = ctx.Operator.InputPorts( "inDposz" );
// Use the value of SOP's trigger input port as the value of the
// 'endofpath' parameter
cpset_param.Value = trigger_posz.Value;
// Succeeded
return true;
}
5. To officially set up the custom operator, we’ll need to bring all the elements together:
- Create the custom operator through the XSIFactory with both the Init function (CusOp_Init) and the Update function (CusOp_Update) we defined assigned to the Code parameter.
- Create the special parameter through the XSIFactory and add it to the custom operator.
- Add the output port as the blue parameter on the AmbientLighting property and call it out.
- Add the first input port as the posz parameter of the trigger and call it inDposz.
- Add the second input port as the posz parameter of the backdrop and call it inBposz.
- Call the Connect method on the custom operator (it knows how to connect all the ports).
function CusOp_Define()
{
// We’re going to need 2 callbacks: one is the usual Update and the other
// is the Init callback, which will initialize our parameter value based
// on the trigger's position at the time the operator is connected
var cusop = XSIFactory.CreateScriptedOp( "CusOp", CusOp_Init.toString()
+ CusOp_Update.toString(), "JScript" );
// Set up the custom operator with a special parameter
var pdef = XSIFactory.CreateParamDef2( "endofpath", siDouble,
20.0 /* default */, 0.0 /* min */, 200.0 /* max */);
cusop.AddParameter( pdef );
// Add the ports using the newly created objects
var target = SetUpBackDrop();
var trigger = SetUpDonut();
cusop.AddOutputPort( trigger.LocalProperties( "Ambient Lighting" ).Parameters( "ambience" ).Parameters( "blue" ), "out" );
cusop.AddInputPort( trigger.Kinematics.Global.Parameters( "posz" ), "inDposz" );
cusop.AddInputPort( target.Kinematics.Global.Parameters( "posz" ), "inBposz" );
// Connect the input and output ports to the operator
cusop.Connect();
// Return the new custom operator
return cusop;
}
6. We can use the CusOp_Define() function we just created as our menu callback function, so all we have to do is add the registration callback with a call to the RegisterMenu method and then define the menu as containing our callback function to make our whole script self-installing:
function XSILoadPlugin( in_reg )
{
// Set up the plug-in particulars
in_reg.Name = "Semi-Self-Installing Operator Plug-in";
in_reg.Author = "XSI SDK Education";
in_reg.URL = "http://www.softimage.com/education/xsi/default.asp";
in_reg.Email = "editors@softimage.com";
in_reg.Categories = "custom,plug-in,example,command,menu,operator";
in_reg.Major = 1;
in_reg.Minor = 0;
// Register the menu
in_reg.RegisterMenu( siMenuTbModelCreatePolygonMeshID,
"RunCusOpDemo", false );
// Registration succeeded
return true;
}
/*
Sets up the menu entry for the special callback function
*/
function RunCusOpDemo_Init( in_context )
{
// Get the menu from the context object
var mnu = in_context.Source;
mnu.AddCallbackItem( "Run Custom Operator Demo", "CusOp_Define" );
return true;
}
7. To run this demo, copy these code snippets into a single file and drop it into the Application/Plugins folder of your user location and then select Run Custom Operator Demo from the Model > Create > Poly. Mesh toolbar menu.