Archive for General Tips

Script: Measure Rig Performance

Script: Measure Rig Performance

As a rigger you have to get along with the animators, and a good startingpoint for that is to provide them with fast rigs. If the animators have to playblast for each time they want to see the actual animation, they are going to waste a lot of time!

I asked this question over at Rigging Dojo about a year ago:

I often find myself implementing a lot of stuff in a rig, stuff I think the animators may need. As I’m improving my rigs, I get more nodes and more calculations going on to achieve what I want – which in the end may result in performance issues with the rig when animating. I’m trying to find out if there’s some way to measure the performance of a rig-component? I find it really hard to find out which components that slows down my rig, as I have to add and animate all of the rig-components together, and then try to compare the playback-speed, remove components, re-check, etc. How do you guys approach this? Also, is there any documentation of speed differences for expressions vs nodes, extra joints vs corrective blendshapes, skinClusters vs wrapDeformers, constraints vs normal parenting, etc?

I got a lot of interesting answers in the thread, but one in particular from Josh Carey helped a lot. He tipped me off on dgtimer, which does exactly what I asked for. So I’ve just wrapped this into a clean little script.

The Script
The script measures the speed by playing off the scene and evaluating each DG node’s performance, which means that for the object(s) to actually be evaluated you have to animate each component (that you want to be included in the evaluation) over a given timespan, the more frames you calculate from the more accurate the results will be.

jh_measurePerformance

So if you have a rig that runs too slow, you can use this script to see which node(s) that does the hardest impact on the performance. You often find that it’s just a couple of nodes that really takes onto the percentage, stuff like wrap deformers are something you want to avoid (at least in the proxy-rig) :)

Video Demonstration:
I’ve uploaded a video to demonstrate how it can be used, in the video I’m using it to compare normal parenting, direct connections, constraints and skinclusters.

jh_measurePerformance (Measure Scene Performance)

//*************************************************************************************************************
// Title: jh_measurePerformance.mel
// Author: Jorn-Harald Paulsen
// Created: December 7, 2011
// Last Update: June 06, 2013
// Description: Utility to measure the speed of a scene, especially useful for rig-performance tweaks.
//*************************************************************************************************************
// MAIN WINDOW
//*************************************************************************************************************
global proc jh_measurePerformance ()
{
//Delete window if it already exists
if (`window -q -ex jh_measurePerformance`) deleteUI jh_measurePerformance;

//Main Window
window -te 30 -t “Measure Performance” -mxb 0 -s 1 -rtf 0 -mb 0 -mbv 0 -w 350 -h 544 jh_measurePerformance;

//Window content
columnLayout -adjustableColumn true;
text -label “\nUtility to measure performance/evaluation of the scene” -fn boldLabelFont;
separator -w 300 -h 10;
text -label “It measures by playing off the scene, so you should”;
text -label “have animation on the obects you want to evaluate!”;
text -label “Note: Longer playback = More accurate results”;
separator -w 300 -h 15;
text -label “Startframe:”;
intField startFrame;
text -label “Endframe:”;
intField endFrame;
separator -w 300 -h 15;
text -label “Number of nodes to return:”;
intField -v 20 numNodes;
separator -w 300 -h 10;
button -label “Measure performance” -c jh_evaluate;
textField -text “Time taken: ” -ed 0 timeField;
textField -text “FPS: ” -ed 0 fpsField;
textScrollList -ams 0 -h 250 -sc jh_selNode evaluatedNodes;
separator -w 300 -h 15;
text -label “Filter by nodetype” -fn boldLabelFont;
textField -text “” nodeField;
button -label “Filter” -c jh_filterResult;
separator -w 300 -h 15;
button -label “Choose where to save the file” -c jh_getExportDir;
textField -en 0 evalExportField;
button -label “Export the results” -c jh_exportToFile;
separator -w 300 -h 15;
//Set the startFrame/endFrame min/max to the timeline’s min/max
intField -e -v `playbackOptions -q -min` startFrame;
intField -e -v `playbackOptions -q -max` endFrame;
//Create the window
window -e -w 350 -h 544 jh_measurePerformance;
showWindow jh_measurePerformance;
}

global proc jh_evaluate()
{
//Remove all items in the textScrollList
textScrollList -e -ra evaluatedNodes;
//Get the frames to playback
int $min = `intField -q -v startFrame`;
int $max = `intField -q -v endFrame`;
//Set the timeSlider to the min/max values
playbackOptions -min $min -max $max;
//Get the number of frames to playback
int $frames = $max – $min;
//Get the number of nodes to return
int $nodesCount = `intField -q -v numNodes`;

//Set the playBack to Free/Play Every Frame
playbackOptions -e -playbackSpeed 0 -maxPlaybackSpeed 0;
//Set the timeSlider to the startFrame
currentTime `playbackOptions -q -min`;
//Reset the dgtimer
dgtimer -on -reset;
//Play the scene (don’t loop)
play -wait;
//Turn off the dgtimer
dgtimer -off;
//Stor the result of the dgTimer
string $evalResult[] = `dgtimer -outputFile “MEL” -maxDisplay $nodesCount -query`;
//Extract the elapsed time and the FPS
string $tempToken[];
tokenize $evalResult[12] “: ” $tempToken;
float $time = $tempToken[3];
float $fps = ($frames / $time);
//Update the textField for time and FPS
textField -e -text (“Time taken: ” + $tempToken[3] + ” seconds”) timeField;
textField -e -text (“FPS: ” + $fps) fpsField;
//For each returned node
for($a = 40; $a < (40 + $nodesCount); $a++)
{
//Remove all of the spaces between each element
string $el[];
tokenize $evalResult[$a] ” ” $el;
//If the result has a % at index 3
if (`gmatch $el[3] “*%”` == 1)
{
//Create a separator string
string $sep = ” | “;
//Generate the string to put into the textScrollList
string $string = ($el[0] + $sep + $el[3] + $sep + $el[7] + $sep + $el[8]);
//Update the textScrollList
textScrollList -e -append $string evaluatedNodes;
}
}
//Get the actual number of elements returned
int $returnedElements = size(`textScrollList -q -ai evaluatedNodes`);
//Update the node count to the actual number
intField -e -v $returnedElements numNodes;
//Print the result
print $evalResult;
print “\n\n\nSee the script editor for details!\n”;
}

global proc jh_selNode()
{
//Get the selected item in the textScrollList
string $selItem[] = `textScrollList -q -si evaluatedNodes`;
//Extract the elapsed time and the FPS
string $tempToken[];
tokenize $selItem[0] “|” $tempToken;
//If the object exists
if(objExists($tempToken[3]) == 1)
{
//Select the item
select -r $tempToken[3];
//Print information
print (“\nSelected: ” + $tempToken[3] + “\n”);
}
}

global proc jh_filterResult()
{
//Get the text to filter from
string $filter = `textField -q -text nodeField`;
//Get all of the items in the textScrollList
string $allItems[] = `textScrollList -q -ai evaluatedNodes`;
//Remove all items in the textScrollList
textScrollList -e -ra evaluatedNodes;
//For each item in the textScrollList
for($item in $allItems)
{
//Separate the string
string $tempToken[];
tokenize $item ” | ” $tempToken;
//If the current element matches the filter-tekst, add it to the textScrollList
if (`gmatch $tempToken[2] $filter` == 1) textScrollList -e -append $item evaluatedNodes;
}
//Get the actual number of elements returned
int $returnedElements = size(`textScrollList -q -ai evaluatedNodes`);
//If no elements was returned
if($returnedElements == 0)
{
//Add the original elements back into the textScrollList
for($element in $allItems) textScrollList -e -append $element evaluatedNodes;
//Print warning message
warning “\nCould not find any matching items!\n”;
}
//Else, print information
else print (“A total of ” + $returnedElements + ” nodes was found matching \”” + $filter + “\”\n”);
}

global proc jh_getExportDir()
{
//Open the file-dialog, and get the results from it
string $getExportDir = `fileDialog -m 1 -dm “*.mel”`;
//Put the directory in the textField
textField -e -text $getExportDir evalExportField;
}

global proc jh_exportToFile()
{
//Get all of the items in the textScrollList
string $allItems[] = `textScrollList -q -ai evaluatedNodes`;
//Get the directory for where to store the animation
string $exportDir = `textField -q -text evalExportField`;
//If a directory wasn’t defined, print error
if($exportDir == “”) error “\nYou need to define a directory to store the animation in!\n”;

//Create and open the storefile for writing
int $fileId = `fopen $exportDir “w”`;
//For each item in the textScrollList, print the item to the file
for($item in $allItems) fprint $fileId ($item + “\n”);
//Close the file for writing
fclose $fileId;
//Print information
print “\nDone!”;
}

jh_measurePerformance;

Download Script

Script: Delete Flat Keys

Script: Delete Flat Keys

When you’re working with animation you eventually end up with a lot of extra keys, by that I mean flat keys, keys we don’t need.

See the image below, I’ve marked the flat keys with red dots:

 

With the script you just select all of the objects that you want to clean the keyframes on, then you define the angle threshold, and the script will go through and delete all of the keys with a lower angle threshold than defined.

When run on the example above, we end up with this:

 

jh_delFlatKeys (Delete Flat Keys)

//*************************************************************************************************************
// Title: jh_delFlatKeys.mel
// Author: Jørn-Harald Paulsen
// Created: September 08, 2010
// Last Update: May 28, 2011
// Description: Utility to remove key’s with a lower value-difference than specified.
//*************************************************************************************************************
// MAIN WINDOW
//*************************************************************************************************************
global proc jh_delFlatKeys()
{
//Close window if it already exists
if (`window -q -ex jh_delFlatKeys`) deleteUI jh_delFlatKeys;

//Main Window
window -topEdge 30 -title “Keyframe cleanup”
-mxb false -s true -rtf false -mb false -mbv false -w 412 -h 268 jh_delFlatKeys;

//Window content
columnLayout -adjustableColumn true;
text -label “\nUtility to remove key’s with a lower value-difference than specified.\n” -fn boldLabelFont;
separator -w 240 -h 40;
text -label “- Select the object(s) that you want to clean keyframes on”;
text -label “- Define the angle/value tolerance (keys with values lower than these will be deleted)\n”;
floatSliderGrp -label “Angle tolerance” -f true -min 0.001 -max 5.000 -fmn 0.001 -fmx 100.000 -v 0.001 -s 0.001 sliderAngleTol;
floatSliderGrp -label “Value tolerance” -f true -min 0.001 -max 5.000 -fmn 0.001 -fmx 100.000 -v 0.001 -s 0.001 sliderValueTol;
separator -w 240 -h 40;
button -label “Clean keyframes” -c jh_deleteKeys;
separator -w 240 -h 40;
window -e -w 412 -h 268 jh_delFlatKeys;
showWindow jh_delFlatKeys;
}

global proc jh_deleteKeys()
{
//Get the selected object(s)
string $selObj[] = `ls -sl`;
//Create a variable to store the number of keys deleted
int $keysDeleted = 0;
//For each selected object
for($object in $selObj)
{
//Get the animation curves
string $animCurve[] = `keyframe -query -name $object`;
//If the object have animation curves
if (size($animCurve) > 0)
{
//Tangents with angles below this value will be considered flat
float $angleTol = `floatSliderGrp -q -v sliderAngleTol`;
//Neighboring values that fall within this tolerance will be considered matching
float $valueTol = `floatSliderGrp -q -v sliderValueTol`;

//Define the variables
string $keyedAttrs[], $outTanType[], $evalStr, $plural;
float $keyedValues[], $inAngle[], $outAngle[], $keyedFrames[], $prevValDif, $nextValDif;
int $deleteIndex[], $stepped, $i;
int $counter = 0;

//For each animation curve
for ($curve in $animCurve)
{
clear($inAngle);
clear($outAngle);
clear($deleteIndex);
clear($keyedValues);

//Get all of the keyed attributes
$keyedAttrs = `listConnections -plugs yes -source no $curve`;

//Go through the keyframes on the current animation-curve and get inAngle and outAngle
float $inAngle[] = `keyTangent -query -inAngle $curve`;
float $outAngle[] = `keyTangent -query -outAngle $curve`;
string $outTanType[] = `keyTangent -query -outTangentType $curve`;

//Make $inAngle and $outAngle absolute values, for less work on the comparison
for ($i=0;$ifor ($i=0;$i

//Get each keyed frame, and get the value of each keyed frame
$keyedFrames = `keyframe -query -timeChange $curve`;
$keyedValues = `keyframe -query -valueChange $curve`;

//For each keyeframe on the current animation-curve, find deleteable keys
for ($i=0;$i {
//As long as the current keyeframe is between 0, and less than the number of keyframes, continue
if ($i == 0 || $i == (`size $keyedFrames`-1)) continue;

//Get the outTangentType of the previus frame, the current frame, and the next frame.
//The result of this command will go to $stepped. If true, $stepped = 1, if false, $stepped = 0
$stepped = ($outTanType[$i] == “step” && $outTanType[$i-1] == “step” && $outTanType[$i+1] == “step”);

//If one of the following conditions (1 or 2) is true, rund the script inside of the if-brackets:
// 1) $stepped is true (1)
// 2) outAngle of the previous frame is less than angleTol
// & inAngle of the current frame is less than angleTol
// & outAngle of the current frame is less than angleTol
// & inAngle of the next frame is less than angleTol
if ($stepped || ($outAngle[$i-1] < $angleTol && $inAngle[$i] < $angleTol && $outAngle[$i] < $angleTol && $inAngle[$i+1] < $angleTol))
{
//Get the difference of the value of the current attribute between the previous frame and the current frame
$prevValDif = abs($keyedValues[$i-1] – $keyedValues[$i]);
//Get the difference of the value of the current attribute between the current frame and the next frame
$nextValDif = abs($keyedValues[$i+1] – $keyedValues[$i]);

//If one of the following (1 or 2) conditions is true, set $deleteIndex:
// 1) $stepped is true (1)
// & $prevValDif is less than valueTol
// 2) $stepped is false (0)
// & $prevValDif is larger than valueTol
// & $nextValDif is larger than $valueTol
if ($stepped && $prevValDif < $valueTol || !$stepped && $prevValDif < $valueTol && $nextValDif < $valueTol) $deleteIndex = $i; } } //Make the string to delete the keys if (size($deleteIndex) > 0)
{
//The first command of the delete -string
$evalStr = “cutKey -clear”;
//Add the indexes to the string
for ($i in $deleteIndex) $evalStr += ” -index ” + $i;
//Add the animation-curve to the string
$evalStr += ” ” + $curve;
//Execute the generated cutKey -string
eval($evalStr);
//Get the number of keys deleted
$counter += size($deleteIndex);
}
}

//If more than one key has been deleted, set the $plural to true
$plural = ($counter > 1 || $counter == 0) ? “s” : “”;
//Put the number of deleted keys on the current attribute to the total deleted keys -variable
$keysDeleted = $keysDeleted + $counter;
//Print information
print ($counter + ” keyframe” + $plural + ” removed from ” + $object + “.\n”);
}
}
//Print number of keys deleted in total
print (“Deleted keyframes in total: ” + $keysDeleted + “.\n”);
}

jh_delFlatKeys;

Download Script

 

> General Tips

852 total views, 1 views today

Script: Hide/Display Local Axis

Script: Hide/Display Local Axis

It can be a hassle to turn on/off the local axis of multiple objects simultaneously, the reason for this is that by default there’s no on and off option on “Display > Local Rotation Axis“, it’s just a toggle. This means that if you select a bunch of objects and toggle the local axis, the objects that already have the local axis visible will be hidden, while the rest of the selection’s local handles will be visible. This can be frustrating at times, especially if you have joints/objects that have the same position and orientation.

 

This is a really simple script, it basicly just adds the flag that defines on/off to the command that toggle’s the local axis. I’ve also added an option for it to go through the entire hierarchy, definitely a timesaver.

jh_dispLocalAxis (Hide/Display the local axis on multiple objects simultaneously)

//*************************************************************************************************************
// Title: jh_dispLocalAxis.mel
// Author: Jørn-Harald Paulsen
// Created: January 27, 2012
// Last Update: January 27, 2012
// Description: Utility to display or hide the local rotation axis of the selected object(s)
//*************************************************************************************************************
// MAIN WINDOW
//*************************************************************************************************************
global proc jh_dispLocalAxis()
{
//Delete window if it already exists
if (`window -q -ex jh_dispLocalAxis`) deleteUI jh_dispLocalAxis;

//Main Window
window -topEdge 30 -title “Display/Hide Local Axis”
-mxb false -s true -rtf false -mb false -mbv false -w 300 -h 440 jh_dispLocalAxis;

//Window content
columnLayout -adjustableColumn true;
separator -w 240 -h 20;
text -label “Utility to display or hide the local rotation axis of the selected object(s)” -fn boldLabelFont;
separator -w 240 -h 25;
button -label “Display Local Axis on selected object(s)” -c “jh_dispAxis 1 0”;
button -label “Hide Local Axis on selected object(s)” -c “jh_dispAxis 0 0” -bgc 0.23 0.23 0.23;
separator -w 240 -h 25;
button -label “Display Local Axis on selected object(s) (with hierarchy)” -c “jh_dispAxis 1 1”;
button -label “Hide Local Axis on selected object(s) (with hierarchy)” -c “jh_dispAxis 0 1” -bgc 0.23 0.23 0.23;
separator -w 240 -h 25;
window -e -w 300 -h 500 jh_dispLocalAxis;
showWindow jh_dispLocalAxis;
}

global proc jh_dispAxis(int $state, int $hierarchy)
{
//If (with hierarchy) was selected, select the hierarchy
if($hierarchy == 1) select -hi;
//Get the selected object(s)
string $selObj[] = `ls -sl`;
//For each object
for($sel in $selObj)
{
//If the attribute “displayLocalAxis” exist on the current object
if(`attributeExists “displayLocalAxis” $sel`)
{
//Sel the state of the local axis
setAttr ($sel + “.displayLocalAxis”) $state;
}
}
}

jh_dispLocalAxis;

Download Script

 

Script: Find Duplicate Names

Script: Find Duplicate Names

When dealing with rigs you often end up having a ton of nodes, and it’s easy to end up having several objects with the same name. You may think that Maya automaticly renames those objects for you, and it does, but only if the objects are in the same hierarchy. However, if you have the objects in separate hierarchies they can share the same name.

This isn’t really a problem in it self because Maya will just use longnames to distinguish them apart, but you may run into problems if you’re using scripts that doesn’t take longnames into consideration.

Example:

In the image above we have two objects named “pCube”. So what Maya does to distinguish them apart is to look at their parent, if the parent’s name is unique it will use that in the longname when dealing the objects. So if I were to select the pCube in root2, Maya would read it as “grp2|pcube”, and if both of the mid-groups was named “grp2” then maya would read the pCube as “root2|grp2|pCube”.

 

I’ve made a script that finds (and renames) all of the objects in the scene sharing the same name, the download link is at the bottom :)

jh_findDuplicateNames (Find and rename duplicate objects in the scene)

//*************************************************************************************************************
// Title: jh_findDuplicateNames.mel
// Author: Jørn-Harald Paulsen
// Created: May 24, 2012
// Last Update: May 24, 2012
// Description: Utility to find and rename objects with duplicate names in the scene
//*************************************************************************************************************
// MAIN WINDOW
//*************************************************************************************************************
global proc jh_findDuplicateNames()
{
//Delete window if it already exists
if (`window -q -ex jh_findDuplicateNames`) deleteUI jh_findDuplicateNames;

//Main Window
window -topEdge 30 -title “Find Duplicate Names”
-mxb false -s true -rtf false -mb false -mbv false -w 284 -h 194 jh_findDuplicateNames;

//Window content
columnLayout -adjustableColumn true;
text -label “\nUtility to find and rename duplicate names in the scene.”;
separator -w 240 -h 40;
button -label “Just select the objects with duplicate names” -c jh_findDuplicates;
separator -w 240 -h 30;
button -label “Select and rename objects with duplicate names” -c jh_renameDuplicates;
separator -w 240 -h 30;
window -e -w 284 -h 194 jh_findDuplicateNames;
showWindow jh_findDuplicateNames;
}

global proc jh_findDuplicates()
{
//Create a string-array to store the duplicates in
string $duplicateNames[];
//Get all of the transform-nodes in the scene
string $transformNodes[] = `ls -type transform`;

//Create a counter
int $counter = 0;
//For each object
for($node in $transformNodes)
{
//If it’s a duplicate object, put it in the variable for duplicates
if(`match “|” $node` == “|”)
{
$duplicateNames[$counter] = $node;
$counter = $counter + 1;
}
}

//Get the number of duplicate objects
int $numDuplicates = `size $duplicateNames`;
//Print information
print $duplicateNames;
print (“\nThere’s a total of ” + $numDuplicates + ” transform-nodes with the same name in the scene:\n”);
//Select the duplicate names
select -r $duplicateNames;
}

global proc jh_renameDuplicates()
{
//Create a string-array to store the duplicates in
string $duplicateNames[];
//Get all of the transform-nodes in the scene
string $transformNodes[] = `ls -type transform`;

//Create a counter
int $counter = 0;
//For each object
for($node in $transformNodes)
{
//If it’s a duplicate object, put it in the variable for duplicates
if(`match “|” $node` == “|”)
{
$duplicateNames[$counter] = $node;
$counter = $counter + 1;
}
}

//Reset the counter
$counter = 0;
//For each of the duplicate objects
for($duplicate in $duplicateNames)
{
//Tokenize the strings
string $nameBuffer[];
int $numTokens = `tokenize $duplicate “|” $nameBuffer`;
//For each of the tokens
for($a=0; $a < `size $nameBuffer`; $a++) { //Get the duplicates of the current token string $dupToken[] = `ls (“*” + $nameBuffer[$a])`; //Get the number of duplicates with the same name as tokenized int $sizeToken = `size $dupToken`; //If there’s more than one object if($sizeToken > 1)
{
//For each duplicate name
for($i=1; $i < $sizeToken; $i++)
{
$dupToken[$i] = `rename $dupToken[$i] ($nameBuffer[$a] + “_dup_” + $i)`;
$counter = $counter + 1;
}
}
}
}

//Print information
print (“\nA total of ” + $counter + ” transform-nodes was renamed\n”);
}

jh_findDuplicateNames;

Download Script

 

All Posts

1,233 total views, 1 views today