Scripted Plug-in Clauses

The various optional clauses inside the plug-in body are similar to those for scripted utilities and tools. There is an additional scripted plug-in specific clause <parameters> for defining scene-savable and Track View visible parameters for the plug-in.

The <plugin_body> of a scripted plug-in definition is made up of a sequence of plug-in clauses as follows:

<plugin_body>     ::= { <plugin_clause> }+

Plug-in clauses define the components of a scripted plug-in and can be one of five basic things:

Formally, the syntax of a <plugin_clause> is defined as follows:

<plugin_clause>   ::= <local_variable_decl> |

                      <local_function_decl> |

                      <local_struct_decl>   |

                      <parameters>          |

                      <tools>               |

                      <rollouts>            |

                      <event_handler>

Locals

A <local_variable_decl>, <local_function_decl>, and <local_struct_decl> are exactly the same as local variable, function, and structure definitions in MAXScript:

<local_variable_decl> ::= local <decl> { , <decl> }

<decl>                ::= <name> [ = <expr> ] -- optional initial value

<local_function_decl> ::= [ mapped ](function | fn) <name> { <argument> } = <expr>

<local_struct_decl>   ::= struct <name> ( <member> { , <member> } )

<member>              ::= ( <name> [ = <expr> ] | <local_function_decl> )

Global variables cannot be declared as a plug-in clause, however they can be declared within event handler scripts. If you need to ensure a variable name references a global variable, declare the variable name as global immediately before defining the plug-in in your script.

When writing scripts, it is good programming practice to explicitly declare your local and global variables. Implicit declaration is provided as a short-hand, typically used when working in the Listener interactively or developing short scripts. When developing extended scripts, explicitly declaring variables can reduce errors and improve readability of the code. It is also recommend that you declare as local all variables unless you really want them to be global variables. The reasons for this are described in Scope of Variables.

Plug-in locals are attached to plug-in objects, and each new instance of the scripted plug-in has its own local storage area. When you reference a plug-in local in a plug-in function or handler, it accesses the local storage of the currently active object, for example, the object currently open in the command panel. Plug-in local storage is not permanent across scene saves and loads (unlike <parameters>). You can provide initial values for locals, as elsewhere in MAXScript, and the locals are re-initialized when a saved scripted plug-in object is loaded again as part of a scene file open. You can also do this by hand in a create event handler if you supply one in the plug-in.

Plug-in locals are accessible to script code outside a plug-in as a normal property on the object, but note that they may be hidden by parameters or other common object properties if they have the same name. When accessing a property on a scripted plug-in object, MAXScript first looks in the common properties (such as .pos, .scale, .parent, etc. on a node), then in the parameters, and then in the locals for a property name match.

There are three predefined local variables accessible in all scripted plug-ins:

version

Yields the current version of the object as an Integer value.

this

Yields the instance of the scripted plug-in.

delegate

Yields the instance of the plug-in you are extending in an extends: plug-in.

The version local variable contains the version number specified in the scripted plug-in definition. See Updating Scripted Plug-ins for more information on this variable.

The this local variable always contains the MAXScript value corresponding to the current instance of the scripted plug-in. This variable provides a guaranteed way of accessing parameters on the new object within plug-in code (in case there are locals or globals with the same name), and it is a way of referencing the plug-in in case you want to store it to a variable.

The delegate local variable contains the MAXScript value corresponding to the instance of the plug-in being extended. To access common properties or subAnim properties in the instance of the plug-in being extended, you need to do so via the delegate local variable. For example,

delegate.width = thisWidth

inside a plug-in function or handler would set the .width property in the instance of the plug-in being extended. The delegate local variable in a scripted plug-in that creates a scene object does not contain the node itself, but a the BaseObject of that node. As such, you can not access the node level properties of the object being created. An exception to this is the node's transform, which is exposed to the scripted plug-in through a local variable called nodeTM. This variable is described in the applicable scripted plug-in type topics.

If the extends: parameter is not supplied for a plug-in, delegate returns undefined.

As well as local variables, you can define local functions and local structures, just as you can with scripted utilities and rollouts.

Parameters

You can define one or more parameter blocks in a scripted plug-in. A parameter block has the form:

parameters <name> [type:#class] [rollout:<name>]

(

{ <param_defs> }+

{ <event_handler> }

)

A parameter block defines a set of parameters for the plug-in, which are like plug-in local variables, but are directly animatable and are saved to and restored from scene files. You can also associate each parameter with appropriate user-interface elements in one of the plug-ins rollouts. When associated in this way, the parameters are "wired" to their spinners and checkboxes, etc., and both update automatically as the other changes, so you don't have to write any user-interface event handlers for them. These parameters correspond to the visible parameters you see in other 3ds max plug-ins. Parameters in the parameter block do not participate in 3ds max's undo system, therefore changes to the parameter values are not undoable.

The <name> you give to a parameter block becomes permanently associated with the parameter block and is used by MAXScript to connect to the right parameter block in a plug-in object that is being loaded from a scene file. This becomes important as you modify a plug-in script and yet there are still objects corresponding to the old definition in other files. When the old-version object is loaded, MAXScript attempts to convert it to the new definition, using the current parameter block definition as a guide. In order to properly do this, each parameter in a parameter block is also given a permanent name. So, if you have existing objects of your plug-in in other files, don't go randomly changing parameter block names or they won't be loadable.

The optional type:#class specifies that the parameters in the parameter block are "class" parameters. This means that there is one copy of each parameter for the *all* the objects of this plug-in class; they all share the parameter. Examples of existing class parameters are the creation type and type-in parameters in a typical geometry primitive.

The optional rollout:<name> specifies a rollout definition elsewhere in the plug-in body to use as the user-interface rollout for the parameter block's parameters. A limitation of the internal parameter block mechanism requires that each parameter block be associated with a separate rollout and each rollout be associated with only one parameter block. This means that each rollout you want to have in the plug-ins user interface must have its own separate parameter block.

The <param_defs> are definitions for each parameter in the parameter block. They have the form:

<name> type:<#name>  [tabSize:<integer>] [tabSizeVariable:<boolean>] \

                     [default:<operand>] [animatable:<boolean>]      \

                     [subAnim:<boolean>] [ui:<ui_def>]

The <name> is permanently associated with the parameter in the same sense as parameter block names. They are used to connect to the right parameter when loading objects of the scripted plug-in and allow you to make certain changes to a script and yet still be able to load old version objects. So, as with parameter block names, don't change or re-use parameter names if you have scene files with old version objects you want to get at.

The type:<#name> parameter is required and defines the parameter type. It can be one of the following:

#float          animatable

#integer        animatable

#color          animatable

#point3         animatable

#boolean        animatable

#angle          animatable

#percent        animatable

#worldUnits     animatable

#string

#filename

#colorChannel   animatable

#time           animatable

#radiobtnIndex

#material

#texturemap

#bitmap

#node

#maxObject

or one of the following types which are arrays of the above base types:

#floatTab

#intTab

#colorTab

#point3Tab

#boolTab

#angleTab

#percentTab

#worldUnitsTab

#stringTab

#filenameTab

#colorChannelTab

#timeTab

#radiobtnIndexTab

#materialTab

#texturemapTab

#bitmapTab

#nodeTab

#maxObjectTab

#worldUnits and #worldUnitsTab specify that a parameter holds a world distance value, kept internally as a system units float. For example:

parameters main rollout:params

(

height type:#worldUnits ui:heightSpin

)

Note that this doesn't cause any spinner display to be automatically shown in current display units, you need to also specify type:#worldUnits on the associated rollout spinner definition.

#node and #nodeTab are used to store references to nodes. The nodes stored in these parameters cannot create a circular dependency. If, for example, you were creating a scripted helper, and set the helper as a target or parent of a camera, you can not store the camera node in a #node or #nodeTab parameter. Likewise, if you created a scripted modifier or material, and store a node in a #node or #nodeTab parameter, you cannot apply the modifier or material to that node.

The various xxTab parameter types allow you to store an array of values having the corresponding type. The initial size of the array is specified using the tabSize: specifier. If tabSizeVariable:true is specified, the size of the array will be automatically expanded when you assign to an higher array index. For example:

parameters main rollout:params

(

mapAmounts type:#floatTab tabSize:4 tabSizeVariable:true \ ui:(map1Amount, map2Amount, map3Amount)

)

defines an array parameter containing 4 float elements (defined by the tabSize: parameter). You can associate a separate rollout element with each array parameter element. These parameters appear as arrays when accessed in plug-in handler code, for example:

a = mapAmounts[i]

Since tabSizeVariable:true was specified, the following will automatically increase the array size to 10:

mapAmounts[10] = a

You can also increase or decrease the array size by assigning a value to the .count property

Each element in an array parameter whose base type is a number of some kind (floatTab, intTab, percentTab, etc.) is independently and automatically animatable, unless you specify animatable:false. You can get at and assign individual controllers via the .controller property. For example:

c = mapAmounts[i].controller

The optional parameter default:<operand> specifies a initial default value for the parameter. The <operand> expression has to be convertible by MAXScript to the base type of the parameter.

The optional parameter animatable:<boolean> specifies whether the parameter is animatable and hence visible in the track view. Only those base types marked as animatable in the above type list can be specified as animatable. Defaults to true.

The optional parameter subAnim:<boolean> can be specified for the 3ds max object types: #node, #material, #textureMap, and #maxObject. If specified as true, the 3ds max object is made visible as a sub-object track in track view. Defaults to false.

The optional parameter ui:<ui_def> is used to specify which user-interface element in the parameter block's associated rollout is to be linked to this parameter. When so linked, the handling of user actions for these elements are automatic and they also update automatically to follow any changes to the parameter caused by animation, scripts, etc. For example, in the following, the parameter block named main is associated with the rollout named params. The parameters height and spread are then linked with the spinners named height and spread in that rollout:

parameters main rollout:params

(  

type:#node subAnim:true

fill type:#node subAnim:true

back type:#node subAnim:true

height type:#float animatable:true default:10 ui:height

spread type:#float animatable:true default:10 ui:spread

on height set val do

(

key.pos.z = val

back.pos.z = val * 1.5

fill.pos.z = val * 0.5

)

)

rollout params "Light Parameters"

(  

spinner height "Height"

spinner spread "Spread"

)

Once this is done, the parameter and spinner are linked, such that changes in one are reflected immediately and automatically in the other. Further, if the parameter is animated, the spinner will show red keyframe highlights when the time slider is positioned at a keyframe for that parameter, as with other 3ds max plug-ins. The kinds of user-interface items that can be linked to particular parameter types is limited to the following sensible combinations:

Parameter type    Rollout user-interface item

#integer          spinner, slider, radioButtons, checkbox, checkbutton

#float            spinner, slider

#time             spinner, slider

#color            colorpicker

#angle            spinner, slider

#percent          spinner, slider

#colorChannel     spinner, slider

#boolean          checkbox, checkbutton

#node             pickButton

#textureMap       mapButton

#material         materialButton

#worldUnits       spinner, slider

Parameter Event Handlers

Two event handlers are associated with parameters. These handlers are called when a parameter is assigned a value, or when the parameter value is retrieved.

on <name> set <arg> do <expr>

A set handler can be supplied for any of the parameters in the block and it will be called whenever the parameter is updated, either through an associated rollout user-interface item or directly, especially via MAXScript property assignment. In general, the way you would code change handlers is to supply set handlers for parameters, rather than on <item> change handlers for spinners and checkboxes, etc. in the rollout definition. A set handler in used in the previous example to update the light positions when the height parameter value changes.

The set handlers are also called when an instance is created or loaded so that common dependent actions can be taken, such as setting up other dependent system object or delegate properties.

If a parameter is animated and has a set handler supplied, the handler will be called whenever the 3ds max time changes, say by dragging the time slider around or within an animation rendering.

on <name> get <arg> do <expr>

A get handler can be supplied for any of the parameters in the block and it will be called whenever the parameter value is retrieved. This will occur when the user interface updates itself, a script accesses the parameter, a viewport is redraw, a render occurs. In all these cases, the animated value is retrieved one or more times. The get handler is called with a single argument which is the current value stored in the paramblock (or underlying controller if the parameter is animated). The currentTime variable is set to the time at which the value is being retrieved and not necessarily the current slider time, since you can ask a parameter for its value for any specified time. If you implement the get handler, you must return a value, and that value is returned as the value of the parameter to the value requester. If you don't want to modify the value, but just monitor accesses, return the argument. You can also compute and return any value of the appropriate type.

Tools

Plug-ins can define local mouse tools. Although you can have any number of local tools and start them as needed, you would typically supply a single tool with the reserved name create. This is taken to be the create tool for objects created in the Create panel and is invoked automatically when you create scripted plug-in objects. Tool definitions are described in Scripted Mouse Tools.

Creatable scene scripted plug-ins (scripted plug-ins that are not invisible, temporary, or extend another plug-in) require a create tool. A create tool, if present on a non-temporary extending plug-in, will override the delegate's create tool. The animate state is implicitly turned off while the create tool is executed.

A local variable nodeTM is accessible within the create tool. This variable contains a Matrix3 values. This value is in the active grid coordinate system. This variable allows the create tool to set the transform of the node currently being created. Also, within the create tool you should use the gridX values rather than worldX values in all cases, since object-creation is managed in the active grid coordinate system.

Rollouts

Plug-ins can define local rollouts. These have the same syntax as local rollouts in scripted utilities. Unlike utility local rollouts, these rollouts are automatically opened as appropriate for the plug-in type (in the command panel or Material Editor, etc.).

Note that rollout local variables only live as long as the rollout is open in the user interface. It gets opened and closed each time it is displayed and so the rollout open and close events are signalled at this time. If you want to load any spinners or other user-interface elements from the currently editing plug-in object, supply an on <rollout> open event handler to pick up these values. Rollout definitions are described in Utility Clauses.

Event Handlers

As with mouse tools, scripted plug-ins respond to certain actions in 3ds max by implementing handler functions. All types of scripted plug-ins support the following event handlers. You can use these event handlers to perform any extra initialization needed, such as loading up plug-in local variables or initializing properties of the plug-in's delegate.

on create do <expr>

The create event handler that is called whenever an instance of a scripted plug-in is created in a scene file. The animate state is implicitly turned off while the create event handler expression is executed.

on load do <expr>

The load event handler that is called whenever an instance of a scripted plug-in is loaded from a scene file. The animate state is implicitly turned off while the load event handler expression is executed.

on update do <expr>

The update event handler that is called whenever an instance of a scripted plug-in is present in the scene file, and the definition of the scripted plug-in is changed (i.e., if you define a scripted plug-in, create an instance of the plug-in object in the scene, and then change and execute the script defining the scripted plug-in, the update event handler defined in the new definition of the scripted plug-in script is called for each instance of the plug-in object in the scene). See the Updating Scripted Plug-ins topic for more information.

Specific classes of scripted plug-ins implement additional event handlers, and are described with description of the scripted plug-in type.

Runtime errors in certain event handlers cause the plug-in to be disabled until it is re-defined. When disabled, none of the plug-in event handlers are called. For example, errors in the map event handler expression in SimpleMods and the buildMesh event handler expression in SimpleObject disable the plug-in, since they are called so frequently. You will need to correct any problems and re-evaluate the plug-in definition again to re-enable the plug-in event handlers.

See also