Monday, 13 December 2010

Selection highlight using a scriptJob

I don't use scriptJobs that often simply because they can really kill the performance of a rig. Also, if not managed properly you can end up with lots and lots of duplicate jobs accumulating which over time brings Maya to a grinding halt.

However... this selection highlighter has proved extrememly useful for a very specific application; for Enslaved there were a couple of rigs I put together that were exploding objects with a large amount of pieces (one was a boat, another was a petrol tanker). The problem here was how to make it super easy for animators to pick a particular section to animate it. I couldn't use a nurbs controller for each as it would be messy and confusing with so many, and I couldn't have them pick the joint for each section as finding the correct one in a cloud of fifty or so was near impossible (remember, this is for games so everything is joint-based).

The solution was to make the mesh pieces themselves the controllers, and to highlight them with a coloured material as a piece was selected.
I've set this up in a test scene to demo it. The large plane is split into pieces and marked as 'highlightable', the four small planes are not.

Incidentally, I originally saw a version of this implimented in a very expensive three-part rigging book set which I won't mention the name of. Apart from just being badly written it was not even remotely useable; It broke the Undo queue and you couldn't select keyframes or keyframe tangets. Anyway...

There are three parts to this.
  1. Defining which meshes can be highlighted.
  2. The scripJob and selection script
  3. Creating a scriptNode that contains the material switch procedure and creates the scriptJob

1. Defining which meshes to highlight

The first step in setting this up is to define which meshes should be highlighted when selected and which should not. This is simply done by adding the attribute NT_dynSel to each one that should be highlighted.

2. The scriptJob and selection script

So underneath is the script in its entirety. Copy it out into an editor (if you haven't, get a mel syntax installed into your editor so it's easier to read) and I'll go through it underneath.

// Create a colour material and shading group for selection highlight if one doesn't already exist
if( ! `objExists NT_sel_highlight_MAT` )
    string $sel_Mat = `shadingNode -n "NT_sel_highlight_MAT" -asShader lambert`;
    setAttr (($sel_Mat)+".colorG") 1;
    setAttr (($sel_Mat)+".colorB") 0;
    setAttr (($sel_Mat)+".colorR") 1;
    sets -renderable true -noSurfaceShader true -empty -name "NT_sel_highlight_MATSG";
    connectAttr -f "NT_sel_highlight_MAT.outColor" "NT_sel_highlight_MATSG.surfaceShader";

// initialise script job
int $selectionScriptJobA = `scriptJob -killWithScene -event "SelectionChanged" "NT_callOnSelectionChanged"`;

//Declare the procedure that is executed when the selection changes
global proc NT_callOnSelectionChanged()
    // turn off the undo queue else you can't undo past a highlighted selection
    undoInfo -swf false;

    // List all geometry in the scene
    string $allGeometry[] = `listTransforms -geometry`;
    // For geometry with the selection highlight attribute...
    for ($geometry in $allGeometry)
        if (`attributeExists "NT_dynSel" $geometry`) {
            // assign default shader
            sets -e -forceElement "initialShadingGroup" $geometry;

    // Grab current selection
    string $sel[] = `ls -sl`;
    for ($s in $sel) {
        if (`attributeExists "NT_dynSel" $s`) {
//            // assign highlight shader
            sets -e -forceElement "NT_sel_highlight_MATSG" $s;
    // turn the undo queue back on
    undoInfo -swf true -infinity on;   

There are three distinct sections in the script. The first two are exectuted as soon as the scene is opened to create the material and shading group and the scriptJob itself. The reason they are executed immediately is because they are not contained within a procedure, and script not in a procedure will be executed when the script is called or sourced. The third is the procedure that assigns materials to meshes and is called by the scriptJob when its event is satisfied.

Creating the material
if( ! `objExists NT_sel_highlight_MAT` )
    string $sel_Mat = `shadingNode -n "NT_sel_highlight_MAT" -asShader lambert`;
    setAttr (($sel_Mat)+".colorG") 1;
    setAttr (($sel_Mat)+".colorB") 0;
    setAttr (($sel_Mat)+".colorR") 1;
    sets -renderable true -noSurfaceShader true -empty -name "NT_sel_highlight_MATSG";
    connectAttr -f "NT_sel_highlight_MAT.outColor" "NT_sel_highlight_MATSG.surfaceShader";
} where the coloured material is created. It consists of a lambert shader called NT_sel_highlighter_MAT that is yellow and a shading group called  NT_sel_highlighter_MATSG. The shading group is there so we can easily colour the meshes by assigning them to the group using the sets command.

Creating the scriptJob
 int $selectionScriptJobA = `scriptJob -killWithScene -event "SelectionChanged" "NT_callOnSelectionChanged"`; where the scriptJob is created. Note -killWithScene, this is really important and just means that when the scene is closed the job will be deleted. If you don't have this it will potentially be created over and over again as the user closes and re-opens various files. This duplication of jobs will eventually slow Maya to a crawl.
The argument -event is what must occur for the job to call the target procedure NT_callOnSelectionChanged, in this case SelectionChanged which is fairly self explanatory. There are numberous events and also conditions that you can use, check out Maya documentation for a full list.
The last part is the procedure, NT_callOnSelectionChanged, to call when the event is satisfied.

Procedure for assigning the materials (called by the scriptJob)
NT_callOnSelectionChanged is pretty straightforward.
  • First it temorarily disables the undo queue so none of the material assigning is added to it. This means that undo behaves exactly as it would if anything else was selected. 
  • All geometry is listed and stored in the string array $allGeometry. 
  • The first loop reassigns all highlightable geometry to the default shading group (mid-grey colour)
  •  Current selection is stored, and the a loop assigns all selected highlightable geometry to NT_sel_highlight_MATSG shading group. 
  • Finally, the undo queue is turn back on.

Creating the scriptJob using the expression editor.

Finally setting all this up is really easy by creating a scriptNode - simply a script that is stored in the scene which you can tell Maya to execute in various ways. Open up the expression editor and give your scriptNode a name.

Copy the entire script (containing material creating, scriptJob creation and material assign procedure) into the script window and click Create at the bottom left.
Now you need to set when this script is executed, we want it to be on Open/Close. This means that the script will be called on scene open/close. The material and scriptJob will be created as soon as the scene is opened because the mel script that creates it is outside of a procedure so will be executed the moment the script is called.

You can click Test Script to execute it now rather than having to save and reopen the scene to see it working. Check it out in action in the video at the top.

1 comment: