// // // secondaryMotion v1.0 bora dayioglu http://www.borafx.com // // ---------------------------------------------------------------------------------------- // // Description; // creates seconday motion for the selected joint hierarchy. // // It basically creates a softbody replica of the selected joint hierarchy and keys the transformations based on this replica. when done, it backs up the original animation curves, // and connects the new motion back to selected joints. you can revert back to original keyframe animation whenever you need and re-run the simulation. // It is designed to work under very complex situations like locked channels, character sets etc., so you can use it on any character/creature rig without breaking apart the rig itself. // You will need to play around `conserve` `end weight` and `goal smoothness` to get used to their behaviour but basically they are default particle attributes so if you familiar with // maya dynamics you will have a clear picture of what they internally do. // I tried to make it `animator friedly` as much as possible. For any bugs/suggestions please contact me at; boradayioglu@gmail.com // Also check out http://www.borafx.com for updates and other handy scripts/plugins // // Installation and Usage; // copy this file to your maya sripts folder (ex. my documents/maya/scripts/ on windows). restart maya and type `secondaryMotion` in command line. its window will pop up // // global proc secondaryMotion(){ string $window = `window -title "secondaryMotion v1.0" -w 401 -h 270 -s 1`; columnLayout -adj 1; separator -h 8 ; floatSliderGrp -label "conserve" -fs 0.01 -ss 0.01 -field true -minValue 0.0 -maxValue 1.0 -fieldMinValue 0.0 -fieldMaxValue 1.0 -value 1.0 pconserve; floatSliderGrp -label "end weight" -fs 0.01 -ss 0.01 -field true -minValue 0.0 -maxValue 1.0 -fieldMinValue 0.0 -fieldMaxValue 1.0 -value 0.7 endweight; floatSliderGrp -label "goal smoothness" -fs 0.01 -ss 0.01 -field true -minValue 1.0 -maxValue 10.0 -fieldMinValue 1.0 -fieldMaxValue 10.0 -value 3.0 smoothness; separator -h 8; floatFieldGrp -numberOfFields 1 -label "start frame" -value1 (`playbackOptions -q -ast`) startframe; floatFieldGrp -numberOfFields 1 -label "end frame" -value1 (`playbackOptions -q -aet`) endframe; checkBoxGrp -numberOfCheckBoxes 1 -label "" -label1 "stretchy" -v1 0 stretchy; separator -h 14; button -l "apply secondaryMotion" -c "applySecondaryMotion()"; button -l "revert to keyframe animation" -c "restoreKeys()"; text -label "Description;" -al "left" -font "obliqueLabelFont"; string $info="Select the joint hierarchy and click the `apply` button.\nIf you want to revert to keyframe animation click the `revert` button"; text -label $info -al "left" ; showWindow $window; window -e -w 401 -h 270 $window; } global proc applySecondaryMotion(){ string $sel[]=`ls -sl`; if(`size($sel)`>0){ select -cl; string $root=$sel[0]; float $startFrame=`floatFieldGrp -q -value1 startframe`; float $endFrame=`floatFieldGrp -q -value1 endframe`; float $conserve=`floatSliderGrp -q -value pconserve`; float $smoothness=`floatSliderGrp -q -value smoothness`; float $endWeight=`floatSliderGrp -q -value endweight`; int $stretchy=`checkBoxGrp -q -v1 stretchy`; doSecondaryMotion( $root , $startFrame , $endFrame , $endWeight , $smoothness , $conserve , $stretchy ); select $sel[0]; } } global proc restoreKeys(){ string $sel[]=`ls -l -sl`; if(`size($sel)`>0){ string $tmp[] = `listRelatives -f -ad -c $sel[0]`; string $objects[]; $objects[0]=$sel[0]; for($n=0;$n<`size($tmp)`;$n++){ $objects[($n+1)]=$tmp[$n]; } string $cleanObjects[]; $cleanObjects=cleanNonSkeleton( $objects ); for($n=0;$n<`size($cleanObjects)`;$n++) restoreAnimation($cleanObjects[$n]); } } global proc backupAnimation( string $nodeName ){ $nodeName=shortNameOf($nodeName); string $channel[]={"tx","ty","tz","rx","ry","rz","sx","sy","sz"}; string $channelLong[]={"translateX","translateY","translateZ","rotateX","rotateY","rotateZ","scaleX","scaleY","scaleZ"}; string $conn[],$conn2[]; int $nChannel=6; if(`checkBoxGrp -q -v1 stretchy`) $nChannel=9; for($n=0;$n<$nChannel;$n++){ if (`getAttr -l ($nodeName + "." + $channel[$n])`==0) { if(!`attributeExists ("_"+$channel[$n]) $nodeName`) addAttr -ln ("_"+$channel[$n]) -dt "string" $nodeName; $conn=`listConnections ($nodeName + "." + $channel[$n])`; // if no animation curve is connected then connect one if(`size($conn)`==0){ setKeyframe ($nodeName + "." + $channel[$n]); $conn=`listConnections ($nodeName + "." + $channel[$n])`; } // if there is any character set exists if(`nodeType $conn[0]`=="character"){ $conn2=`listConnections ($conn[0]+"."+$nodeName + "_" + $channelLong[$n])`; // character set exists but no animation curve is connected to the character set if(`size($conn2)`<2){ setKeyframe ($nodeName + "." + $channel[$n]); $conn2=`listConnections ($conn[0]+"."+$nodeName + "_" + $channelLong[$n])`; } disconnectAttr ($conn2[0]+".output") ($conn[0]+"."+$nodeName + "_" + $channelLong[$n]); rename $conn2[0] ($conn2[0]+"_BACKUP"); setAttr -type "string" ($nodeName+"._"+$channel[$n]) ($conn2[0]+"_BACKUP"); } else { // assume a animation curve is connected and no character set than disconnectAttr ($conn[0]+".output") ($nodeName + "." + $channel[$n]); rename $conn[0] ($conn[0]+"_BACKUP"); setAttr -type "string" ($nodeName+"._"+$channel[$n]) ($conn[0]+"_BACKUP"); } } } } global proc restoreAnimation( string $nodeName ){ $nodeName=shortNameOf($nodeName); string $channel[]={"tx","ty","tz","rx","ry","rz","sx","sy","sz"}; string $channelLong[]={"translateX","translateY","translateZ","rotateX","rotateY","rotateZ","scaleX","scaleY","scaleZ"}; string $attr,$conn[]; for($n=0;$n<`size($channel)`;$n++){ if ( `getAttr -l ($nodeName+"."+$channel[$n])`==0 ){ if (`attributeExists ("_"+$channel[$n]) $nodeName`){ $attr=`getAttr ($nodeName+"._"+$channel[$n])`; $conn=`listConnections ($nodeName + "." + $channel[$n])`; if(`size($conn)`>0){ if(`nodeType $conn[0]`=="character"){ cutKey -cl -t ":" -f ":" -at $channel[$n] $nodeName; connectAttr ($attr+".output") ($conn[0]+"."+$nodeName + "_" + $channelLong[$n]); rename $attr (`substitute "_BACKUP" $attr ""`); deleteAttr ($nodeName+"._"+$channel[$n]); } else { cutKey -cl -t ":" -f ":" -at $channel[$n] $nodeName; connectAttr ($attr+".output") ($nodeName+"."+$channel[$n]); rename $attr (`substitute "_BACKUP" $attr ""`); deleteAttr ($nodeName+"._"+$channel[$n]); } } else { // nothing connected ? connectAttr ($attr+".output") ($nodeName+"."+$channel[$n]); rename $attr (`substitute "_BACKUP" $attr ""`); deleteAttr ($nodeName+"._"+$channel[$n]); } } } } } global proc unlockChannels ( string $nodes[] ){ string $channel[]={"tx","ty","tz","rx","ry","rz","sx","sy","sz"}; for($n=0;$n<`size($nodes)`;$n++){ if(`nodeType $nodes[$n]`=="joint"){ for($i=0;$i<`size($channel)`;$i++){ if(`getAttr -l ($nodes[$n]+"."+$channel[$i])`==1){ setAttr -l 0 ($nodes[$n]+"."+$channel[$i]); } } } } } global proc string[] cleanNonSkeleton ( string $nodes[] ){ // // cleans non joint objects from the string array, so we don`t have to deal with group nodes etc. on the joint chain // string $result[]; int $i=0; for($n=0;$n<`size($nodes)`;$n++){ if(`nodeType $nodes[$n]`=="joint"){ $result[$i]=$nodes[$n]; $i++; } } return $result; } global proc doSecondaryMotion( string $source , float $startFrame , float $endFrame , float $finalWeight , float $smoothness , float $conserve , int $stretchy ){ // show warning messages on scriptEditorInfo -sw 0; float $currentTime=`currentTime -q`; currentTime ($startFrame); // create the joint chain that will be used for particles string $tmp[]=`duplicate -rc $source`; hide $tmp[0]; $particleJoints=cleanNonSkeleton( $tmp ); // create the joint chain that the final softbody animation will be baked to clear $tmp; string $tmp[]=`duplicate -rc $source`; hide $tmp[0]; $destJoints=cleanNonSkeleton( $tmp ); int $sizeTmp; int $numOfJoints=`size($particleJoints)`; // find all the childs of the source joint (joint selected) and arrange it from parent to grand child string $sourceJoints[]; string $sourceJointsTMP[]; clear $tmp; $tmp = `listRelatives -f -ad -c $source`; $sizeTmp = size($tmp); $sourceJointsTMP[0] = $source; for($n=0;$n<`size($tmp)`;$n++){ $sourceJointsTMP[($n+1)]=$tmp[($sizeTmp-1-$n)]; } $sourceJoints=cleanNonSkeleton( $sourceJointsTMP ); // create spline curve which will be used as splineIk later on float $pos[],$rot[],$scl[]; string $cmd="curve"; for($n=0;$n<`size($particleJoints)`;$n++){ $pos=`xform -q -ws -t $particleJoints[$n]`; $cmd+=" -p "+$pos[0]+" "+$pos[1]+" "+$pos[2]; } $cmd+=" -d 1"; string $curveName=`eval($cmd)`; $tmp=`listRelatives -f -s $curveName`; string $curveNameShape=$tmp[0]; hide $curveName; // set the ik spline handle string $tmp[]=`ikHandle -ccv 0 -pcv 0 -sj $particleJoints[0] -ee $particleJoints[($numOfJoints-1)] -ap -sol "ikSplineSolver" -c $curveName`; hide $tmp[0]; // make softbody string $particleCurve[]=`soft -c -d -g 1.0 $curveName`; string $tmp[]=`listRelatives -s $particleCurve[0]`; string $particleCurveShape=$tmp[0]; setAttr ($particleCurveShape+".goalSmoothness") $smoothness; // edit particle weights float $inc=(1.0-$finalWeight)/($numOfJoints-1); particle -e -c $conserve $particleCurveShape; for($n=0;$n<$numOfJoints;$n++){ particle -e -or $n -at goalPP -fv (1.0-($n*$inc)) $particleCurveShape ; } // do the main time loop // make the ik spline stretchy if needed string $curveInfo,$multiplyDivide; float $arclen; if($stretchy==1){ $curveInfo = `createNode curveInfo`; connectAttr ($curveNameShape+".local") ($curveInfo+".inputCurve"); $multiplyDivide=`createNode multiplyDivide`; connectAttr ($curveInfo+".arcLength") ($multiplyDivide+".input1X"); $arclen=`getAttr ($curveInfo+".arcLength")`; setAttr ($multiplyDivide+".input2X") $arclen; setAttr ($multiplyDivide+".operation") 2; unlockChannels($particleJoints); for($n=0;$n<`size($particleJoints)`;$n++){ connectAttr ($multiplyDivide+".outputX") ($particleJoints[$n]+".scaleX"); connectAttr ($multiplyDivide+".outputX") ($particleJoints[$n]+".scaleY"); connectAttr ($multiplyDivide+".outputX") ($particleJoints[$n]+".scaleZ"); } } $tmp=`listConnections ($particleCurveShape+".goalGeometry[0]")`; string $goalCurveName=$tmp[0]; $tmp=`listRelatives -s $goalCurveName`; string $goalCurveShape=$tmp[0]; int $amount=0; int $cancel=0; progressWindow -title "computing secondaryMotion" -progress $amount -status "computing: 0%" -isInterruptable true; int $progressInc=100/($endFrame+1-$startFrame); // key the destJoint based on particles for($n=($startFrame);$n<($endFrame+1);$n++){ currentTime $n; // move the curve points to the sourceJoint`s positions first for($i=0;$i<$numOfJoints;$i++){ $pos=`xform -q -ws -t $sourceJoints[$i]`; move -a $pos[0] $pos[1] $pos[2] ($goalCurveShape+".cv["+$i+"]"); } // read the sourceJoint`s transformation and key it to destJoint for($i=0;$i<$numOfJoints;$i++){ if($stretchy==1) $scl=`xform -q -r -s $particleJoints[$i]`; $rot=`xform -q -os -ro $particleJoints[$i]`; $pos=`xform -q -t $particleJoints[$i]`; if(`getAttr -l ($destJoints[$i]+".rx")`==0){ setAttr ($destJoints[$i]+".rx") $rot[0]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".rx"); } if(`getAttr -l ($destJoints[$i]+".ry")`==0){ setAttr ($destJoints[$i]+".ry") $rot[1]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".ry"); } if(`getAttr -l ($destJoints[$i]+".rz")`==0){ setAttr ($destJoints[$i]+".rz") $rot[2]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".rz"); } if(`getAttr -l ($destJoints[$i]+".tx")`==0){ setAttr ($destJoints[$i]+".tx") $pos[0]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".tx"); } if(`getAttr -l ($destJoints[$i]+".ty")`==0){ setAttr ($destJoints[$i]+".ty") $pos[1]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".ty"); } if(`getAttr -l ($destJoints[$i]+".tz")`==0){ setAttr ($destJoints[$i]+".tz") $pos[2]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".tz"); } if($stretchy==1){ if(`getAttr -l ($destJoints[$i]+".sx")`==0){ setAttr ($destJoints[$i]+".sx") $scl[0]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".sx"); } if(`getAttr -l ($destJoints[$i]+".sy")`==0){ setAttr ($destJoints[$i]+".sy") $scl[1]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".sy"); } if(`getAttr -l ($destJoints[$i]+".sz")`==0){ setAttr ($destJoints[$i]+".sz") $scl[2]; setKeyframe -itt spline -ott spline ($destJoints[$i]+".sz"); } } } $amount += $progressInc; progressWindow -edit -progress $amount -status ("computing: "+$amount+"%"); if ( `progressWindow -query -isCancelled` ){ $n=$endFrame+2; $cancel=1; } } if($cancel==0){ progressWindow -edit -progress 100 -status ("computing: 100%"); // connect the destJoints anim curves to sourceJoint string $channel[]={"tx","ty","tz","rx","ry","rz","sx","sy","sz"}; string $channelLong[]={"translateX","translateY","translateZ","rotateX","rotateY","rotateZ","scaleX","scaleY","scaleZ"}; string $conn[],$conn2[]; string $nodeName; int $nChannel=6; if($stretchy==1) $nChannel=9; currentTime $startFrame; for($i=0;$i<$numOfJoints;$i++){ backupAnimation($sourceJoints[$i]); for($n=0;$n<$nChannel;$n++){ if(`getAttr -l ($sourceJoints[$i] + "." + $channel[$n])`==0){ $nodeName=shortNameOf($sourceJoints[$i]); clear $conn; $conn=`listConnections ($nodeName + "." + $channel[$n])`; if(`size($conn)`>0){ if(`nodeType $conn[0]`=="character"){ $conn2=`listConnections ($destJoints[$i] + "." + $channel[$n])`; connectAttr ($conn2[0]+".output") ($conn[0]+"."+$nodeName + "_" + $channelLong[$n]); } } else { $conn2=`listConnections ($destJoints[$i] + "." + $channel[$n])`; connectAttr ($conn2[0]+".output") ($sourceJoints[$i]+"."+$channel[$n]); } } } } // return back to initial time currentTime $currentTime; // cleanup extras delete $goalCurveName; delete $particleJoints[0]; delete $destJoints[0]; if(`objExists $curveInfo`) delete $curveInfo; delete $curveName; } // show warning messages on scriptEditorInfo -sw 1; progressWindow -endProgress; }