// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 global string $shaveUI_fileVersion = "$Revision$"; source "shaveRelationshipEditor"; source "shaveRenderman"; /* * Written by Steve Galle and Dean Edmonds * Many thanks to Rob Tesdhal for a great set of recommendations * for improvements. */ global int $shave_initOldNodes_done = false; global proc shaveUI() { shaveInit(); shaveUtil -initPlugin; } // // Generic local proc to find the path to the script file containing the // specified proc. // proc string getPathToProc(string $proc) { string $path = whatIs($proc); // // Make sure that it's a script procedure rather than a built-in // command. // string $gibberish = match("^Mel procedure[^:]*: +", $path); if ($gibberish == "") { $gibberish = match("^Script[^:]*: +", $path); if ($gibberish == "") return ""; } // // Strip off the leading gibberish. // $path = substring($path, size($gibberish)+1, size($path)); // // Better turn all backslashes into forward slashes or else things will // get screwed up if someone tries to use it in an eval() call. // $path = substituteAllString($path, "\\", "/"); return $path; } // // We used to have a script of our own called "relationshipEditor.mel", // but it is now obsolete, so if one of its functions exists, tell the user // to get rid of the file. // if(exists("shaveUvLinkingEditor")) { string $path = getPathToProc("shaveUvLinkingEditor"); confirmDialog -title "Warning" -message ( "Please remove the following file from your system:\n" + " " + $path + "\n" + "This file is no longer used by Shave and causes trouble" + " with some versions of Maya.\n" ) -button "Ok"; } global string $shave_coneAngleExpr = ""; global string $shave_coneCapExpr = ""; global string $shave_renderCone = ""; global string $shave_volumeSG = ""; global string $shave_volumeShader = ""; global string $shave_previousRenderer = ""; // // Utility procedures. // // These are local procs, so we can use any names we want. // proc int attrExists(string $node, string $attr) { return (objExists($node) && attributeExists($attr, $node)); } // // Returns the full name of the given attribute. For example, if passed // a mesh node and an attribute name of "vertexColor" it would return // "colorPerVertex.vertexColor". // // If the attribute or any of its ancestors are arrays, then an empty // string will be returned. // proc string getFullAttrName(string $node, string $attr) { if (`attributeQuery -m -n $node $attr`) return ""; string $fullName = $attr; string $tmp[] = `attributeQuery -lp -n $node $attr`; while (size($tmp) > 0) { if (`attributeQuery -m -n $node $tmp[0]`) return ""; $fullName = $tmp[0] + "." + $fullName; $tmp = `attributeQuery -lp -n $node $tmp[0]`; } return $fullName; } proc connectTexturableParm( string $hairShape, string $sliderAttr, string $textureAttr, string $shader, string $valueAttr, string $multAttr ) { // // If the hair shape has a connection to its texture attr, then we // make the same connection to the shader's valueAttr and connect the // hair shape's slider to the shader's multAttr. // // If the hair shape *doesn't* have a connection to its texture attr, // then we connect its slider to the shader's valueAttr and set the // shader's multAttr to 1.0. // string $sliderConn[] = `listConnections -s on -d off -p on -c on ($hairShape + "." + $sliderAttr)`; string $texConn[] = `listConnections -s on -d off -p on -c on ($hairShape + "." + $textureAttr)`; if (size($texConn) > 0) { connectAttr -force $texConn[1] ($shader + "." + $valueAttr); if (size($sliderConn) > 0) connectAttr -force $sliderConn[1] ($shader + "." + $multAttr); else { connectAttr -force ($hairShape + "." + $sliderAttr) ($shader + "." + $multAttr); } } else { if (size($sliderConn) > 0) connectAttr -force $sliderConn[1] ($shader + "." + $valueAttr); else { connectAttr -force ($hairShape + "." + $sliderAttr) ($shader + "." + $valueAttr); } string $multConn[] = `listConnections -s on -d off -p on -c on ($shader + "." + $multAttr)`; if (size($multConn) > 0) disconnectAttr $multConn[1] $multConn[0]; setAttr ($shader + "." + $multAttr) 1.0; } } // // Copies from $srcNode to $destNode the values of and connections to all // attributes which they have in command (based on name and type). // proc copyCommonAttrs( string $srcNode, string $destNode, int $inConnections, int $outConnections ) { // // Loop through all of the attributes of the source node. // string $attrs[] = `listAttr $srcNode`; string $attr; for ($attr in $attrs) { // // We'll be using the shaveUtil command to do the actual copying // and it recurses into compounds, so we only want to deal with // top-level attributes here (i.e. those without parents). // if (match("[.]", $attr) == "") { string $parent[] = `attributeQuery -lp -n $srcNode $attr`; if ($parent[0] == "") { // // If the destination node also has this attribute, then // copy its value or connection. // string $destAttr = $destNode + "." + $attr; string $tmp[] = `ls $destAttr`; if (size($tmp) > 0) { shaveUtil -copyAttr ($srcNode + "." + $attr) $destAttr -duplicateInConnections -moveOutConnections; } } } } } proc string getParent(string $node) { string $temp[] = `listRelatives -parent -fullPath $node`; if (size($temp) == 0) return ""; return $temp[0]; } proc string getReferenceFileFromNode(string $node) { // // In Maya 7.0 the 'reference' command was replaced by the // 'referenceQuery' command. // global int $gShaveMayaVersion[]; if ($gShaveMayaVersion[0] < 7) return eval("reference -q -f " + $node); return eval("referenceQuery -f " + $node); } proc int isCreatedByShave(string $node) { return attrExists($node, "createdByShave"); } proc int isNodeReferenced(string $node) { // // In Maya 7.0 the 'reference' command was replaced by the // 'referenceQuery' command. // global int $gShaveMayaVersion[]; if ($gShaveMayaVersion[0] < 7) return eval("reference -q -inr " + $node); return eval("referenceQuery -inr " + $node); } proc int isSplineHair(string $node) { string $conns[] = `listConnections -s yes -d no ($node + ".inputCurve")`; return (size($conns) > 0); } proc markAsCreatedByShave(string $node) { if (!isCreatedByShave($node)) addAttr -at bool -ln "createdByShave" $node; } proc int nodeExists(string $node) { string $tmp[] = `ls $node`; return (size($tmp) > 0); } proc int nodeTypeExists(string $type) { return (`objectType -tagFromType $type` != 0); } // // Create a render cone which encompasses the entire scene, as viewed by // the given camera. // // The return value is an array of three strings containing the names of // created nodes: // // 0 - render cone // 1 - cone angle expression // 2 - cone cap expression // proc string[] createRenderVolume(string $camera, string $reuse[]) { // // We may have the camera's shape node, but we need the transform as // well. // string $camTransform; if (objectType($camera) != "transform") { $camTransform = getParent($camera); } else $camTransform = $camera; // // If the caller passed us an existing render cone in the first entry of // $reuse, then use that, otherwise create a new one. // string $renderCone = ""; if (size($reuse) > 0) $renderCone = $reuse[0]; if (($renderCone == "") || !objExists($renderCone) || (nodeType($renderCone) != "renderCone")) { $renderCone = `createNode renderCone`; } string $coneTransform = getParent($renderCone); // // If the cone's transform is not yet grouped beneath another transform, // then add that grouping. // string $coneGroup = getParent($coneTransform); if ($coneGroup == "") { // // We don't use the 'group' command here because it will set the // group's pivots to the center of the cone whereas we want them to // remain at the tip. // $coneGroup = `createNode -n renderConeGroup transform`; parent $coneTransform $coneGroup; // // Reget the cone's immediate parent since its path will have changed. // $coneTransform = getParent($renderCone); } // // We would like to parent the render cone under the camera's // transform, but cameras are usually hidden and we need the render // cone to be visible. So we'll use a parentConstraint instead. // // Note that if the constraint already exists, then the commands below // will simply keep the existing one. // parentConstraint $camTransform $coneGroup; // // Some renderers have problems if the render cone sits exactly at the // camera's eye point. // // To get around this, we move the cone just slightly behind the // camera's eye point. For the offset we'll use 0.001 of the distance // to the camera's far clip plane. // float $offset = getAttr($camera + ".fcp") * 0.001; if ($offset <= 0.0) $offset = 0.1; setAttr ($coneTransform + ".tz") $offset; // // Set up an expression to keep the render cone's coneAngle large // enough to encompass the camera's entire viewing frustum. // // If the caller passed us an existing expression node then reuse that. // string $coneAngleExpr = ""; if (size($reuse) > 1) $coneAngleExpr = $reuse[1]; if (($coneAngleExpr == "") || !objExists($coneAngleExpr) || (nodeType($coneAngleExpr) != "expression")) { $coneAngleExpr = `expression -uc "none"`; } expression -e -s ($renderCone + ".coneAngle = 2.0 * atan(18.0 * " + $camera + ".hfa / " + $camera + ".fl + 0.1)") $coneAngleExpr; // // Have the render cone extend out to the camera's far clipping plane. // // If the caller passed us an existing expression node then reuse that. // string $coneCapExpr = ""; if (size($reuse) > 2) $coneCapExpr = $reuse[2]; if (($coneCapExpr == "") || !objExists($coneCapExpr) || (nodeType($coneCapExpr) != "expression")) { $coneCapExpr = `expression`; } // // WARNING: I used to add in $offset to compensate for the // amount by which the cone's apex has been moved back, and // thereby avoid cutting off renderable scene geometry near // the far clipping plane. However, Mental Ray would sometimes // decide that that meant that the base of the cone was // beyond the far clip and therefore that none of the cone // lay within the viewing frustum and therefore it would not // even instantiate the volume shader. What part of "volume" // shader don't they get? It shouldn't matter that the // containing geometry is outside the frustum if it's got a // volume shader on it. // expression -e -s ($renderCone + ".coneCap = " + $camera + ".fcp") $coneCapExpr; // // Return the three key nodes to the caller in a string array. // string $result[3]; $result[0] = $renderCone; $result[1] = $coneAngleExpr; $result[2] = $coneCapExpr; return $result; } proc string createShaveGlobals(string $preferredName) { // // Create a shared shaveGlobals node of the given name. // // string $actualName = `createNode -n $preferredName -shared shaveGlobals`; string $actualName = shave_createNode("shaveGlobals", $preferredName, false, true); // If the RenderMan plugin is loaded then initialize 'Inherit // Settings' to 'Renderman' and 'Time Basis' to 'Relative'. if (shave_rmanPluginIsLoaded()) { setAttr ($actualName + ".ribBlurInheritSettings") 2; setAttr ($actualName + ".ribBlurTimeBasis") 1; } // Set the RIB string to use Shave's rman shader. // // Normally we would set this as a default value for the attribute, // but the default used to be for the attr to be empty. Maya's // annoying habit of not saving attrs which hold default values means // that old scenes would suddenly start getting this string, which we // don't want them to do. So we set the value here, instead, so that // only new shaveGlobals nodes pick it up. setAttr -type "string" ($actualName + ".ribStuff") "Surface \"Shave\""; return $actualName; } // // Try to delete a node without getting any errors. // proc int deleteNode(string $node) { if (($node == "") || !objExists($node)) return true; if (isNodeReferenced($node)) return false; // // A bug in the lockNode command means that a set cannot be locked or // unlocked: any attempt to do so actually operates on all of the set's // members instead. // if (objectType($node) != "objectSet") catch(`lockNode -l off $node`); return !catch(`delete $node`); } proc deleteShaveNode(string $shaveHairShape) { // Deleting a shaveHair node while liveMode is active causes Maya to crash, // so let's turn liveMode off. shave_cancelLive(); // Delete the growth and collision sets. string $sets[] = `listConnections -type objectSet -s yes -d no ($shaveHairShape + ".growthSet") ($shaveHairShape + ".collisionSet")`; string $set; for ($set in $sets) deleteNode($set); // Make sure that this is a shaveHair node and not a transform, then // get its parent transform. string $hairShapeTransform; if (nodeType($shaveHairShape) == "shaveHair") $hairShapeTransform = getParent($shaveHairShape); // Delete the shaveHair node and its display node. string $displayNode = shave_getDisplayNode($shaveHairShape); if (!deleteNode($shaveHairShape)) warning("Could not delete shaveHair node '" + $shaveHairShape + "'."); if ($displayNode != "") { // Get the display node's parent node, if it has one. string $displayParent = getParent($displayNode); // Delete the display node. if (!deleteNode($displayNode)) warning("Could not delete shave display node '" + $displayNode + "'."); // If the parent no longer has any children, delete it, too. if ($displayParent != "") { string $children[] = `listRelatives -children $displayParent`; if ((size($children) == 0) && !deleteNode($displayParent)) { warning( "Could not delete shave display grouping node '" + $displayParent + "'." ); } } } // If the hair shape's transform no longer has any children, delete it // too. if (objExists($hairShapeTransform)) { string $children[] = `listRelatives -children $hairShapeTransform`; if (size($children) == 0) deleteNode($hairShapeTransform); } } proc destroyRenderVolume( string $renderCone, string $coneAngleExpr, string $coneCapExpr ) { if (($coneAngleExpr != "") && objExists($coneAngleExpr)) delete $coneAngleExpr; if (($coneCapExpr != "") && objExists($coneCapExpr)) delete $coneCapExpr; if (($renderCone != "") && objExists($renderCone)) { string $parent = getParent($renderCone); if ($parent != "") { string $group = getParent($parent); if ($group != "") delete $group; else delete $parent; } else delete $renderCone; } } proc forceConnection(string $from, string $to) { string $conns[] = `listConnections -s yes -d no -p yes $to`; if ((size($conns) == 0) || ($conns[0] != $from)) connectAttr -force $from $to; } proc string getShader(string $node) { if ($node != "") { int $i; string $connections[] = `listConnections -destination yes -source no -type shadingEngine $node`; if (size($connections) > 0) return $connections[0]; } return ""; } proc int insertConnection(string $targetNode, string $targetAttr, string $nodeToInsert, string $inputAttr, string $outputAttr) { string $targetPlug = $targetNode + "." + $targetAttr; string $inputPlug = $nodeToInsert + "." + $inputAttr; string $outputPlug = $nodeToInsert + "." + $outputAttr; if (!attributeExists($targetAttr, $targetNode) || !attributeExists($inputAttr, $nodeToInsert) || !attributeExists($outputAttr, $nodeToInsert)) { return -1; } // If targetPlug already has an incoming connection, copy it to // nodeToInsert's input. Otherwise copy its current value to the input. // if (catchQuiet(`shaveUtil -copyAttr $targetPlug $inputPlug -duplicateInConnections`)) { return -2; } // Connect nodeToInsert's output to the targetPlug, replacing any existing // connection. // if (catchQuiet(`forceConnection $outputPlug $targetPlug`)) { return -3; } return 1; } proc int isHideAll() { return nodeExists("shaveGlobals") && getAttr("shaveGlobals.hideHair"); } // When you query a set for its list of members (e.g. "sets -q") it returns // them in DG order, NOT in the order in which they were added to the set. // However, the connections to the set's dagSetMembers attribute array ARE // in the order in which they were added to set (except that multiple // components from the same object get compressed down to a single // connection). // // This routine takes the connections in the $conns array and returns them // sorted in the order in which a "sets -q" command returns their // corresponding objects. // // The only use for this procedure is for preserving the broken ordering // that some old shaveHair nodes used to use: the scene has been built with that // ordering so, even though it doesn't reflect the user's original // intentions, it's too late to change it now. // proc string[] sortConnectionsBySet(string $conns[], string $set) { string $conn; string $sortedObjects[] = `sets -q -nodesOnly $set`; string $object; string $sortedConns[]; int $numSortedConns = 0; for ($object in $sortedObjects) { for ($conn in $conns) { string $temp[] = `ls -o $conn`; // // While the connection is to the if ($temp[0] == $object) $sortedConns[$numSortedConns++] = $conn; } } return $sortedConns; } // // Replace all occurrences of a substring with another. // // Unlike MEL's substituteAllString(), the match string can be more than // one character long. // proc string strReplaceAll(string $inStr, string $matchStr, string $replStr) { int $inStrLen = size($inStr); int $matchLen = size($matchStr); int $replLen = size($replStr); string $outStr = ""; if (($inStrLen > 0) && ($matchLen > 0)) { int $i; for ($i = 1; $i <= $inStrLen; $i++) { if (strcmp(substring($inStr, $i, $i + $matchLen - 1), $matchStr) == 0) { $outStr += $replStr; $i += $matchLen - 1; } else $outStr += substring($inStr, $i, $i); } } else $outStr = $inStr; return $outStr; } proc string[] extendToShapes(string $transform) { string $shapes[] = `ls -o -dag -type mesh -type nurbsCurve -type nurbsSurface -type subdiv $transform`; string $shape; string $result[]; int $numItems = 0; for ($shape in $shapes) { if (!getAttr($shape + ".intermediateObject")) $result[$numItems++] = $shape; } return $result; } // // This should only be used when converting old shaveHair nodes because it does // not preserve the order in which objects were added to the set, which is // important for spline hair. // proc expandTransforms(string $set) { string $members[] = `sets -q $set`; string $obj; string $objType; sets -clear $set; for ($obj in $members) { string $shapes[]; string $shape; clear $shapes; if (objectType($obj) == "transform") $shapes = extendToShapes($obj); else $shapes[0] = $obj; for ($shape in $shapes) { $objType = objectType($shape); if (($objType == "mesh") || ($objType == "nurbsCurve") || ($objType == "nurbsSurface") || ($objType == "subdiv")) { sets -add $set $shape; } } } } proc string[] getFilteredSelection(int $forGrowth) { string $shaveHairShape; int $wrn = 0; int $success = false; int $splineCount = 0; int $polyCount = 0; int $otherCount = 0; string $type = ""; string $selection[] = `ls -sl`; string $filtered[]; if (size($selection) == 0) { $wrn = 3; } // // Let's make sure that the selection is valid. // string $sel; string $shape; for ($sel in $selection) { // // If this is a component selection, get the component name. // string $componentType = ""; string $parts[]; tokenize $sel "." $parts; if (size($parts) > 1) { tokenize $parts[size($parts)-1] "[" $parts; $componentType = $parts[0]; } // // This may be a transform above multiple shapes, in which case we // must expand it out to those shapes. // string $shapes[]; clear $shapes; if (objectType($sel) == "transform") $shapes = extendToShapes($sel); else $shapes[0] = $sel; for ($shape in $shapes) { string $shapeType = objectType($shape); // // Only meshes allow components to be in the growth list. // if ($shapeType == "mesh") { // // The only component type allowed is face. If this is // a non-face component then reject it. // if (($componentType != "") && ($componentType != "f")) { $wrn = 4; } else { $polyCount++; // // '$shape' might contain a face reference, so let's // get the actual node. // string $temp[] = `ls -o $shape`; // // If this is a display mesh, then warn the user. // $temp = `listConnections -type shaveHair -s yes -d no ($temp[0] + ".inMesh")`; if (size($temp) > 0) $wrn = 1; } } else { if ($componentType != "") $wrn = 4; else { switch ($shapeType) { case "nurbsCurve": // We can grow spline hair from curves, but we // don't collide with curves. // if (!$forGrowth) { continue; } $splineCount++; break; case "nurbsSurface": case "subdiv": $otherCount += 1; break; case "shaveHair": continue; break; default: { // Maya 2016 has an annoying bug where the // poly creation commands leave the polyCreator // node first on the selection list instead // of the mesh's transform. // // To get around that, if this is a // polyCreator node, just skip to the next one // on the list. // string $baseTypes[] = `nodeType -inherited $shape`; if (stringArrayContains("polyCreator", $baseTypes)) { continue; } // This is not a valid node for growing hair. // $wrn = 2; } } } } if ($wrn > 1) break; $filtered[size($filtered)] = $shape; } if ($wrn > 1) break; } string $purpose = "collision"; if ($forGrowth) $purpose = "growth"; if ($forGrowth && ($polyCount + $otherCount > 0) && ($splineCount > 0)) { confirmDialog -title "Selection Warning" -message ("You may cannot mix both growth curves and\n" + "growth surfaces in the same hair.") -button "Ok"; } else if ($forGrowth && ($splineCount == 1) && ($wrn == 0)) { confirmDialog -title "Selection Warning" -message ("You must select more than one curve to grow\n" + "spline hair.") -button "Ok"; } else if ($forGrowth && ($wrn == 3)) { confirmDialog -title "Selection Warning" -message ("To grow hair, you must first select one or more\n" + "surfaces or polygonal faces, or select two or\n" + "more curves.") -button "Ok"; } else if ($wrn == 2) { confirmDialog -title "Selection Warning" -message ("'" + $shape + "' is not a NURBS curve, NURBS\n" + "surface, subdivision surface, polygonal surface or\n" + "polygonal face. Only those types may be used as\n" + $purpose + " objects.") -button "Ok"; } else if($wrn == 1) { string $goAhead = `confirmDialog -title "Selection Warning" -message ("You have selected hairs or guides as " + $purpose + "\n" + "objects for hair. This is cool, but unusual,\n" + "and may give unexpected results.\n\n" + "Do you wish to continue?") -button "Yes" -button "No"`; if ($goAhead == "Yes") $success = true; } else if ($wrn == 4) { // // This is subtly different from $wrn == 2 in that we display // $sel rather than $shape because it contains the component. // confirmDialog -title "Selection Warning" -message ("'" + $sel + "' is not a NURBS curve, NURBS\n" + "surface, subdivision surface, polygonal surface or\n" + "polygonal face. Only those types may be used as\n" + $purpose + " objects.") -button "Ok"; } else $success = true; if (!$success) clear $filtered; return $filtered; } proc string[] getStatFiles(string $shaveHairShape) { string $statFiles[]; string $statDir = shaveGetStatDir(); // // If the Shave temporary directory for this scene hasn't been defined // yet, or doesn't exist, then there can't be any stat files either. // if (($statDir != "") && `file -q -exists $statDir`) { // // Get a list of the files in the directory. // string $files[] = `workspace -q -l $statDir`; int $numFiles = size($files); // // The node name may have colons in it due to namespaces. Those // won't work in filenames, so convert them to underscores. // $shaveHairShape = strReplaceAll($shaveHairShape, ":", "_"); // // Record any .stat files which are for this node. // string $file; for ($file in $files) { string $match = match($shaveHairShape + "[.][-0-9]+[.]stat$", $file); if (size($match) > 0) $statFiles[size($statFiles)] = $statDir + $file; } } return $statFiles; } proc removeStaleUVSetAssignments(string $shaveHairShape) { // // Get the current growth list. // string $growthObjects[] = `shaveNode -q -growthList $shaveHairShape`; // // Get rid of components and duplicates. // $growthObjects = `ls -o $growthObjects`; $growthObjects = stringArrayRemoveDuplicates($growthObjects); // // Get a list of the UV set mappings. // string $mappings[] = `listAttr -multi -string "hairUVSetAssignments" ($shaveHairShape + ".hairUVSetAssignments")`; string $temp[]; int $numMappings = size($mappings); int $i; for ($i = 0; $i < $numMappings; $i++) { // // Find the surface which is supplying the UV set for this mapping. // string $uvSetSurface[] = `listConnections -s yes -d no -shapes yes ($shaveHairShape + "." + $mappings[$i] + ".hairUVSetName")`; // // If removing the surface from the shaveHair node's list of growth // objects has no effect, then the surface is no longer a growth // object, so get rid of its entry. // $temp = stringArrayRemove($uvSetSurface, $growthObjects); if (size($temp) == size($growthObjects)) removeMultiInstance -break yes ($shaveHairShape + "." + $mappings[$i]); } } proc setStatDir(string $newDir) { setAttr -type "string" shaveGlobals.tmpDir $newDir; global string $shave_prevStatDir; string $fullPath = shaveGetStatDir(); $shave_prevStatDir = $fullPath; } // // Triangulate the specified mesh. // proc string triangulate(string $mesh) { string $object[] = `ls -sl`; int $faceCount[] = `polyEvaluate -f $mesh`; int $fc = $faceCount[0]; $fc--; polyTriangulate ($mesh + ".f[0:"+$fc+"]"); return $mesh; } global proc int[] shave_getMayaVersion() { int $result[3]; int $api; int $major; int $minor1; int $minor2; $api = `about -api`; // Starting with Maya 2018 the API version is 8 digits, not 6. // if($api > 201800) { $major = $api/10000; $minor1 = ($api % 10000) / 100; $minor2 = $api % 100; } else { $major = $api / 100; $minor1 = ($api % 100) / 10; $minor2 = $api % 10; } $result[0] = $major; $result[1] = $minor1; $result[2] = $minor2; return $result; } global proc string shave_createNode( string $type, string $name, int $select, int $shared ) { global int $gShaveMayaVersion[]; string $cmd = "createNode "; if ($name != "") $cmd += "-n \"" + $name + "\" "; if ($shared) $cmd += "-shared "; if ($select && ($gShaveMayaVersion[0] > 7)) { $cmd += "-skipSelect "; $select = false; } $cmd += $type; if (catch($name = `eval $cmd`)) { warning("shave: unexpected error creating " + $type + " node."); return ""; } return $name; } global proc string[] shave_saveSelection() { return `ls -sl -l`; } global proc shave_restoreSelection(string $sel[]) { select -r -ne $sel; } // // We use a job number of -1 to indicate inactive scriptJobs. Doing a // 'scriptJob -exists' on a negative job number works interactively (returning // false) but not in batch (generating an error). So this wrapper proc // explicitly does the check for -1 itself. // // (Thanx to John Pierson of Vinton Studios for giving us the heads-up on // this one.) // global proc int shave_scriptJobExists(int $jobID) { if ($jobID < 0) return false; int $result = `scriptJob -exists $jobID`; return $result; } global proc shave_killJob(int $jobID) { if (shave_scriptJobExists($jobID)) scriptJob -k $jobID -force; } global proc shaveInit() { // // We no longer support Maya versions prior to 6.0 // global int $gShaveMayaVersion[]; $gShaveMayaVersion = shave_getMayaVersion(); global string $gShaveMayaVersionStr; global int $gShaveOrphanDeletionDisableCount; global int $shave_curRendererAttrJobID; global string $shave_oldSceneName; global string $shave_prevStatDir; global int $shave_rendererChangeJobID; global string $shave_savedStatDir; global int $liveJob; $gShaveMayaVersionStr = string($gShaveMayaVersion[0]); // Special case handling for those Maya "updates" which are really their // own separate versions. // if (($gShaveMayaVersion[0] == 2016) && ($gShaveMayaVersion[1] >= 50)) { $gShaveMayaVersionStr = "2016.5"; } $gShaveOrphanDeletionDisableCount = 0; $shave_curRendererAttrJobID = -1; $shave_oldSceneName = `file -q -sceneName`; $shave_prevStatDir = ""; $shave_rendererChangeJobID = -1; $shave_savedStatDir = ""; $liveJob = 0; shavePrefs(); // // Create runTimeCommands. // shaveRunTimeCommands; // Shave will not work in multithreaded evaluation. // Make sure that threaded evaluation is off (which // in the Preferences Window is known as 'DG' mode). // if (exists("evaluationManager")) { string $mode[] = `evaluationManager -q -mode`; if ((size($mode) == 0) || ($mode[0] != "off")) { evaluationManager -mode "off"; } } // // If we're running interactively then set up the Shave menus and // heads-up displays. // if(!`about -batch`) { shaveMenu; // // We want to be sure that the 'Presets' button on the AE calls our // version of 'psWinSavePreset' rather than Maya's. So we force // Maya's definition now by sourcing its MEL script, then we source // our own to override that definition with our own. // if (!exists("psWinSavePreset")) { eval("source saveAttrPresetWin"); if (!exists("psWinSavePreset")) { warning( "shave: No psWinSavePreset command found after sourcing" + " saveAttrPresetWin.mel" ); warning( "shave: Please send a bug report to shave-haircut-support@epicgames.com" ); } } if (!exists("deleteSelectedAttrPresets")) { eval("source attrPresetEditWin"); if (!exists("deleteSelectedAttrPresets")) { warning( "shave: No deleteSelectedAttrPresets command found" + " after sourcing attrPresetEditWin.mel" ); warning( "shave: Please send a bug report to shave-haircut-support@epicgames.com" ); } } eval("source shavePresetWin"); shave_doAEOverrides(); shaveBrush(); shaveShelf(); shave_checkOldPresets(); shave_prepVP2(); } shave_initRenderers; } global proc shaveCleanup(int $mayaExiting) { if (!`about -batch`) { if (`menu -exists shaveSelectMenu`) { deleteUI shaveSelectMenu; } if (`menu -exists shaveMenu`) { deleteUI shaveMenu; } shave_cleanupContexts($mayaExiting); shaveBrush_cleanup(); shaveRunTimeCommands_cleanup(); } global int $shave_curRendererAttrJobID; global int $shave_rendererChangeJobID; shave_killJob($shave_curRendererAttrJobID); shave_killJob($shave_rendererChangeJobID); if (exists("shave_rmanCleanup")) eval("shave_rmanCleanup()"); if (exists("AEshaveGlobals_cleanup")) eval("AEshaveGlobals_cleanup()"); } global proc shave_initRenderers() { shave_rmanInit(); shave_catchSelectedRendererChanges(); } // // Event handlers. // global proc shaveSceneLoaded() { global string $shaveBatchSourceScene; $shaveBatchSourceScene = ""; // // The newly-loaded file may have some old shaveHair nodes in it, so let's // get them up-to-date before they can do any damage. :-) // shave_initOldNodes(); // // Save the existing temp directory path, in fully expanded form. // global string $shave_oldSceneName; global string $shave_prevStatDir; global string $shave_savedStatDir; $shave_oldSceneName = `file -q -sceneName`; $shave_prevStatDir = shaveGetStatDir(); $shave_savedStatDir = $shave_prevStatDir; shave_enableMenus(); // Set up third-party renderers. shave_initRenderers(); // // Set up a script job to monitor changes in the stat dir path. // eval("shaveMonitorStatDir"); } global proc shaveSceneCleared() { global string $shave_oldSceneName; global string $shave_prevStatDir; global string $shave_savedStatDir; $shave_oldSceneName = ""; $shave_prevStatDir = ""; $shave_savedStatDir = ""; shave_initRenderers(); shave_enableMenus(); } // // Callback from plugin.cpp on "After Save" event. // // Note that this does not get called when the scene is written out for // the purposes of a batch render. // global proc shaveSceneWritten() { global string $shave_oldSceneName; global string $shave_prevStatDir; global string $shave_savedStatDir; string $newSceneName = `file -q -sceneName`; string $newStatDir = shaveGetStatDir(); if (objExists("shaveGlobals")) { if (($newStatDir != "") && ($newSceneName != $shave_oldSceneName) && ($newStatDir == $shave_savedStatDir)) { string $msg; $msg = "Please note the the scene just written will have\n" + "the same Stat File Directory as the original scene.\n\n" + "If you do not want them to share the same stat\n" + "files, you must use the Shave Globals window to\n" + "specify a different directory for one of them."; if (`about -batch`) warning($msg + "\n"); else { confirmDialog -title "New And Old Scenes Share Stat Files" -message $msg; } } } $shave_oldSceneName = $newSceneName; $shave_prevStatDir = $newStatDir; $shave_savedStatDir = $newStatDir; } global proc shaveStatDirChanged() { global string $shave_prevStatDir; string $newStatDir = shaveGetStatDir(); if ($newStatDir == $shave_prevStatDir) return; // // Let's set this now, because side-effects may cause other scripts // which depend upon it to run. // $shave_prevStatDir = $newStatDir; // // If there's no stat dir then there can't be any dynamics, either. // if ($newStatDir == "") shave_clearDynamics("all"); else { // // Are there any stat files in the new directory? // string $statFiles[] = getStatFiles(""); // // No dynamics without stat files, either. // if (size($statFiles) == 0) shave_clearDynamics("all"); else { // // Do any of the stat files match shaveHair nodes in the scene which // already have dynamics? // string $disposition = "Ignore"; string $nodes[] = `ls -type shaveHair`; string $node; int $haveAMatch = false; for ($node in $nodes) { if (getAttr($node + ".runDynamics") == 2) { $statFiles = getStatFiles($node); if (size($statFiles) > 0) { $haveAMatch = true; break; } } } // // Let's ask the user what zie wants to do. // if ($haveAMatch) { if (`about -batch`) $disposition = "Use"; else { $disposition = `confirmDialog -title "Existing stat Files Found" -message ("The new stat directory contains files from a Shave" + " dynamics run, some\n" + " of which match the names of Shave nodes in the" + " scene which have dynamics.\n\n" + "What would you like to do with the files: ignore" + " them, delete them, or try to use them?") -button "Ignore" -button "Delete" -button "Use"`; } } else { if (`about -batch`) $disposition = "Ignore"; else { $disposition = `confirmDialog -title "Existing stat Files Found" -message ("The new stat directory contains files from a Shave" + " dynamics run\n" + " but none of the Shave nodes in the scene currently" + " have dynamics.\n\n" + "What would you like to do with the files: ignore " + " them or delete them?") -button "Ignore" -button "Delete"`; } } if ($disposition == "Delete") shave_clearDynamics("all"); else { for ($node in $nodes) { // // We sometimes lock shaveHair nodes (e.g. when they're from // a newer version of shave), so it behooves us to make // sure that we don't trip over that locking. // // We *don't* ever lock the runDynamics attr, though, // so we don't need to check for attr-level locking: if // the user fiddles with it, zie burns. // int $nodeWasLocked = false; int $tmp[] = `lockNode -q -l $node`; $nodeWasLocked = $tmp[0]; if ($nodeWasLocked) lockNode -l off $node; if (getAttr($node + ".runDynamics") == 2) { if ($disposition == "Ignore") catch(`setAttr ($node + ".runDynamics") 0`); else { // // If there aren't any stat files for this // node, then turn its dynamics off. // $statFiles = getStatFiles($node); if (size($statFiles) == 0) catch(`setAttr ($node + ".runDynamics") 0`); } } if ($nodeWasLocked) lockNode -l on $node; } } } // // Make sure the new stat directory exists. // if (!`sysFile -makeDir $newStatDir`) { confirmDialog -title "Cannot Create Stat File Directory" -message ("The directory '" + $newStatDir + "'\n" + "does not exist and could not be created.\n\n" + "Please check your access permissions or choose\n" + "another directory."); } } } proc string getCameraPanel(string $camera) { string $panelCamera; string $modelPanel; // // The 'modelPanel' command below will return the *transform* of the // panel's camera rather than the camera shape itself. So let's make // sure that we have our input camera's transform as well. // if (objectType($camera) == "camera") { $camera = getParent($camera); } for ($modelPanel in `getPanel -type "modelPanel"`) { $panelCamera = `modelPanel -q -cam $modelPanel`; // // $camera is in full path name format, so make sure that the // panel's camera name is in the same format so that we can compare // them. // if ($panelCamera != "") { string $tmp[] = `ls -l $panelCamera`; $panelCamera = $tmp[0]; } if ($panelCamera == $camera) return $modelPanel; } return ""; } global proc string shave_getRenderCamera() { // // There's no way to know for sure which camera is being used by the // renderer, but this procedure makes its best guess. // string $camera = ""; // // If we doing an interactive render, find the camera being used by the // Render View. // if (!`about -batch`) { string $tmp[] = `getPanel -scriptType "renderWindowPanel"`; // // Do we even have a render view? // if (size($tmp) > 0) $camera = `renderWindowEditor -q -currentCamera $tmp[0]`; if ($camera == "") { // Okay, no render view, so assume that we're rendering from // whatever Maya considers to be the current camera. $camera = getCurrentCamera(); } if (($camera != "") && (objectType($camera) == "transform")) { $tmp = `listRelatives -fullPath -children $camera`; for ($camera in $tmp) if (objectType($camera) == "camera") break; } } if ($camera == "") { // // Let's look for an appropriate renderable camera. // string $cameras[] = `ls -type camera`; string $renderablePerspCam = ""; string $renderableCam = ""; string $perspCam = ""; for ($camera in $cameras) { if (getAttr($camera + ".renderable")) { if ($renderableCam == "") $renderableCam = $camera; if (($renderablePerspCam == "") && !getAttr($camera + ".orthographic")) { $renderablePerspCam = $camera; } } if (($perspCam == "") && !getAttr($camera + ".orthographic")) $perspCam = $camera; } // // If we have a renderable perspective camera, use it. // if ($renderablePerspCam != "") { $camera = $renderablePerspCam; } // // If we have any renderable cameras at all, use the first of // those. // else if ($renderableCam != "") { $camera = $renderableCam; } // // Nothing renderable at all? Then use a non-renderable perspective // camera, if we have one. // else if ($perspCam != "") { $camera = $perspCam; } // // Any cameras at all? // else if (size($cameras) > 0) $camera = $cameras[0]; } // // Give up! // return $camera; } proc int[] getPre70TestResolution() { int $res[2] = { 320, 240 }; if (`optionVar -exists renderViewTestResolution`) { $testRes = `optionVar -query renderViewTestResolution`; if ($testRes == 0) { string $camera = shave_getRenderCamera(); string $panel = getCameraPanel($camera); if ($panel != "") { string $modelViewControl = `modelEditor -q -control $panel`; if (size($modelViewControl) && `control -ex $modelViewControl`) { $res[0] = `control -q -w $modelViewControl`; $res[1] = `control -q -h $modelViewControl`; } } } else { string $globals[] = `ls -renderGlobals`; if (size($globals[0]) > 0) { string $connect[]; $connect = listConnections($globals[0] + ".resolution"); if (size($connect[0]) > 0) { $res[0] = getAttr($connect[0] + ".width"); $res[1] = getAttr($connect[0] + ".height"); if ($testRes == 2) { $res[0] /= 2; $res[1] /= 2; } else if ($testRes == 3) { $res[0] /= 4; $res[1] /= 4; } else if ($testRes == 4) { $res[0] /= 10; $res[1] /= 10; } } } } } return $res; } // // Mostly taken from getTestResolution in renderWindowPanel.mel // proc int[] get70TestResolution() { global int $renderViewResolutions[]; if (!exists("setRenderOptionVars")) eval("source renderWindowPanel.mel"); int $res[2] = { 640, 480 }; if( `optionVar -exists renderViewTestResolution` ) { int $testRes = `optionVar -query renderViewTestResolution`; if( $testRes == 0 ) { string $camera = shave_getRenderCamera(); string $panel = getCameraPanel($camera); if( size($panel) ) { string $modelViewControl = `modelEditor -q -control $panel`; if(size($modelViewControl) && `control -ex $modelViewControl`) { $res[0] = `control -q -w $modelViewControl`; $res[1] = `control -q -h $modelViewControl`; } } } else { string $hasCommonGlobalValueProc = `renderer -query -hasCommonGlobalValueProcedure (currentRenderer())`; string $getCommonGlobalValueProc = `renderer -query -getCommonGlobalValueProcedure (currentRenderer())`; if( $hasCommonGlobalValueProc != "" && $getCommonGlobalValueProc != "" && eval($hasCommonGlobalValueProc+" width") && eval($hasCommonGlobalValueProc+" height")) { // Use getCommonGlobalValueProc to get the width and height. // $res[0] = eval($getCommonGlobalValueProc+" width"); $res[1] = eval($getCommonGlobalValueProc+" height"); if ($testRes > 1) { $res[0] *= $renderViewResolutions[$testRes]; $res[1] *= $renderViewResolutions[$testRes]; $res[0] /= 100; $res[1] /= 100; } } } } return $res; } // // Mostly taken from getTestResolution in renderWindowPanel.mel // proc int[] get80TestResolution() { if (!exists("setRenderOptionVars")) eval("source renderWindowPanel.mel"); int $res[2] = { 640, 480 }; if( `optionVar -exists renderViewTestResolution` ) { int $testRes = `optionVar -query renderViewTestResolution`; if( $testRes == 0 ) { string $camera = shave_getRenderCamera(); string $panel = getCameraPanel($camera); if( size($panel) ) { string $modelViewControl = `modelEditor -q -control $panel`; if(size($modelViewControl) && `control -ex $modelViewControl`) { $res[0] = `control -q -w $modelViewControl`; $res[1] = `control -q -h $modelViewControl`; } } } else { global int $renderViewResolutions[]; // Get the width and height. // $res[0] = `getAttr defaultResolution.width`; $res[1] = `getAttr defaultResolution.height`; if ($testRes > 1) { $res[0] *= $renderViewResolutions[$testRes]; $res[1] *= $renderViewResolutions[$testRes]; $res[0] /= 100; $res[1] /= 100; } } } return $res; } global proc int[] shave_getTestResolution() { global int $gShaveMayaVersion[]; if ($gShaveMayaVersion[0] < 7) return getPre70TestResolution(); if ($gShaveMayaVersion[0] == 7) return get70TestResolution(); return get80TestResolution(); } proc string getVrayVersion(int $numeric, int $full) { string $result = ""; if (exists("vray")) { $result = `vray version`; if ($numeric || !$full) { string $parts[]; tokenize($result, ".", $parts); if ($numeric) { $result = $parts[0] + $parts[1] + $parts[2]; } else { $result = $parts[0] + "." + $parts[1] + "." + $parts[2]; } } } return $result; } // vlad|15Apr2010 // global proc int shave_rendererIsVray() { // // Maya does not maintain the selected renderer in batch mode. // So if we're in batch mode, and we're not actually in the process of // doing a render, we don't really know which renderer will be used. // // But there's not much else we can do, so even though we know it may // not be true, we will nonetheless use the currently selected // renderer. // return (shave_selectedRenderer() == "vray"); } // vlad|19Jan2012 // global proc int shave_rendererIsPrMan(string $renderer) { return (($renderer == "renderman") || ($renderer == "renderMan") || ($renderer == "renderManRIS")); } global proc int shave_curRendererIsPrMan() { return shave_rendererIsPrMan(shave_selectedRenderer()); } global proc string shave_selectedRenderer() { string $renderer = "mayaSoftware"; if(attrExists("defaultRenderGlobals", "currentRenderer")) $renderer = getAttr("defaultRenderGlobals.currentRenderer"); return $renderer; } global proc string shave_removeShaveCallbacksFromStr(string $cbk, string $prefix) { string $cbkIn[]; string $cbkOut[]; tokenize($cbk, ";", $cbkIn); int $numIn = size($cbkIn); int $i = 0; int $j = 0; for($i = 0; $i < $numIn; $i++) { string $ashave = match($prefix, $cbkIn[$i]); if($ashave == "") { $cbkOut[$j] = $cbkIn[$i]; $j++; } } $cbk = ""; int $numOut = size($cbkOut); for($i = 0; $i < $numOut; $i++) { $cbk += $cbkOut[$i]+ ";"; } return $cbk; } global proc shave_removeShaveRenderCallbacks() { string $pre = `getAttr defaultRenderGlobals.preMel`; if($pre != "") { $pre = shave_removeShaveCallbacksFromStr($pre,"shave"); setAttr defaultRenderGlobals.preMel -type "string" $pre; } string $post = `getAttr defaultRenderGlobals.postMel`; if($post != "") { $post = shave_removeShaveCallbacksFromStr($post,"shave"); setAttr defaultRenderGlobals.postMel -type "string" $post; } string $preRender = `getAttr defaultRenderGlobals.preRenderMel`; if($preRender != "") { $preRender = shave_removeShaveCallbacksFromStr($preRender,"shave"); setAttr defaultRenderGlobals.preRenderMel -type "string" $preRender; } string $postRender = `getAttr defaultRenderGlobals.postRenderMel`; if($postRender != "") { $postRender = shave_removeShaveCallbacksFromStr($postRender,"shave"); setAttr defaultRenderGlobals.postRenderMel -type "string" $postRender; } } global proc shave_selectedRendererChanged() { global string $gShaveMayaVersionStr; global string $shave_previousRenderer; string $newRenderer = shave_selectedRenderer(); // // If the new and old renderers are the same, then there's nothing to // do. // if ($shave_previousRenderer == $newRenderer) return; //clen up all shave* callbacks to avoid confussions - dub|22Jun2012 shave_removeShaveRenderCallbacks; shave_rebuildShaveGlobalsAE(); // // Maya's own 'rendererChanged' proc may or may not have // run yet, in which case the previous renderer's MEL scripts may or // may not have already been copied to the new renderer. // // We can eliminate that uncertainty by forcing it to run now. (It may // run again later, but if so it will detect that it has already run // and do nothing, so any changes we make here will be safe.) // if (`renderer -q -exists $newRenderer`) rendererChanged(); // Find the directory where we store our databases of supported renderers. // string $dbPath; if (`about -mac`) { $dbPath = "/Users/Shared/Epic Games/shaveHaircut/maya" + $gShaveMayaVersionStr; } else { $dbPath = dirname(dirname(`pluginInfo -q -path shaveNode`)); } int $st; string $errors[]; if ($newRenderer == "arnold") { if (`pluginInfo -q -loaded mtoa`) { // Get mtoa's version without the periods. // string $mtoaVersion = `pluginInfo -q -v mtoa`; // Check if we support the currently loaded version of mtoa. // $st = shaveCheckVersion( "mtoa", $mtoaVersion, $dbPath + "/supportedMtoAVersions.txt", "shave_mtoaCompareVersion", $errors ); // If we don't support it, tell the user what to do about it. // if ($st == 0) { $errors[size($errors)] = "Please download from SolidAngle's web site one of the supported versions of"; $errors[size($errors)] = "Arnold for Maya " + $gShaveMayaVersionStr + " listed above. After you have installed"; $errors[size($errors)] = "it, you will need to reinstall Shave and Haircut."; } } } else if (shave_rendererIsPrMan($newRenderer)) { if (`pluginInfo -q -loaded RenderMan_for_Maya`) { // Get the version. // // If the name of the renderer is 'renderman' then it's version // 22 or later and we can get the version from the plugin info. // Otherwise it's 21 or earlier and we can get it from the // 'rman getversion prman' command. // string $rmanVersion; if ($newRenderer == "renderman") { $rmanVersion = `pluginInfo -q -v RenderMan_for_Maya`; } else { $rmanVersion = eval("rman getversion prman"); } $rmanVersion = match("[0-9.]+", $rmanVersion); // Check if we support this version. // $st = shaveCheckVersion( $newRenderer, $rmanVersion, $dbPath + "/supportedRManVersions.txt", "shave_rmanCompareVersion", $errors ); if ($st == 1) { shave_rmanSetupCallbacks; } else { // We don't support this version. Tell the user what to do // about it. // $errors[size($errors)] = "Please download from Pixar's web site one of the supported versions of"; $errors[size($errors)] = "RenderMan for Maya " + $gShaveMayaVersionStr + " listed above. After you have installed"; $errors[size($errors)] = "it, you will need to restart Maya."; } } } else if($newRenderer == "vray") { string $vrayVersion = getVrayVersion(false, false); // If we support this version of V-Ray then set up our callbacks. // If not then let the user know what they need to do about that. // $st = shaveCheckVersion( "vrayformaya", $vrayVersion, $dbPath + "/supportedVrayVersions.txt", "shave_vrayCompareVersion", $errors ); if ($st == 1) { shaveVraySetRenderCallbacks; } else if ($st == 0) { $errors[size($errors)] = "Please download from ChaosGroup's web site one the supported versions of"; $errors[size($errors)] = "V-Ray for Maya " + $gShaveMayaVersionStr + " listed above. After you have installed"; $errors[size($errors)] = "it, you will need to reinstall Shave and Haircut."; } } else { $errors[size($errors)] = "No Shave support found for '" + $newRenderer + "' renderer."; $errors[size($errors)] = "Scene will be rendered without Shave hair."; } // If there's an error message, display it in the Script Editor and Output // Window. // if (size($errors) > 0) { int $i; print("--------\n"); fprint(2, "--------\n"); for ($i = 0; $i < size($errors); ++$i) { warning($errors[$i]); if ($i == 0) { fprint(2, "WARNING: "); } fprint(2, $errors[$i] + "\n"); } print("--------\n"); fprint(2, "--------\n"); } $shave_previousRenderer = $newRenderer; } ///////////////////// vlad|14Apr2010 //////////////////////// //global string $shaveVrayOldPreRenderLayer = ""; //global string $shaveVrayOldPostRenderLayer = ""; proc splitMtoAVersionStr(string $versionStr, int $versionComponents[]) { clear $versionComponents; // The version strings may be in mtoaVersion/arnoldVersion format in // which case we need to separate out the mtoaVersion. // string $parts[]; tokenize($versionStr, "/", $parts); $versionStr = $parts[0]; // Split the version into its separate components. // clear $parts; tokenize($versionStr, ".", $parts); for ($i = 0; $i < 4; $i++) { if ($i < size($parts)) { $versionComponents[$i] = int($parts[$i]); } else { // MtoA version numbers sometimes leave off trailing zeroes. // $versionComponents[$i] = 0; } } } global proc int shave_mtoaCompareVersion(string $version1, string $version2) { int $v1Parts[]; int $v2Parts[]; splitMtoAVersionStr($version1, $v1Parts); splitMtoAVersionStr($version2, $v2Parts); // arch if ($v1Parts[0] < $v2Parts[0]) return -1; if ($v1Parts[0] > $v2Parts[0]) return 1; // major if ($v1Parts[1] < $v2Parts[1]) return -1; if ($v1Parts[1] > $v2Parts[1]) return 1; // minor if ($v1Parts[2] < $v2Parts[2]) return -1; if ($v1Parts[2] > $v2Parts[2]) return 1; // fix if ($v1Parts[3] < $v2Parts[3]) return -1; if ($v1Parts[3] > $v2Parts[3]) return 1; return 0; } global proc int shave_rmanCompareVersion(string $version1, string $version2) { string $v1Parts[]; string $v2Parts[]; tokenize($version1, ".", $v1Parts); tokenize($version2, ".", $v2Parts); int $i; int $n1 = size($v1Parts); int $n2 = size($v2Parts); int $numParts = $n1; // Pad the arrays to the same length with zeroes. // if ($n1 > $n2) { for ($i = $n2; $i < $n1; ++$i) { $v2Parts[$i] = "0"; } } else if ($n2 > $n1) { for ($i = $n1; $i < $n2; ++$i) { $v1Parts[$i] = "0"; } $numParts = $n2; } for ($i = 0; $i < $numParts; ++$i) { if ((int)$v1Parts[$i] < (int)$v2Parts[$i]) return -1; if ((int)$v1Parts[$i] > (int)$v2Parts[$i]) return 1; } return 0; } global proc int shave_vrayCompareVersion(string $version1, string $version2) { int $v1 = int(substituteAllString($version1, ".", "")); int $v2 = int(substituteAllString($version2, ".", "")); if ($v1 < $v2) return -1; if ($v1 > $v2) return 1; return 0; } global proc shaveVraySetRenderCallbacks() { //print "shaveVraySetRenderCallbacks\n"; /*global*/ string $shaveVrayOldPreRenderLayer; /*global*/ string $shaveVrayOldPostRenderLayer; // Save the original callback strings. // // In batch renders vray messes with the current layer during its initialization and doesn't // set it back to the correct layer until the preRenderLayerMel callback, so that's the one // we want to use. // $shaveVrayOldPreRenderLayer = `getAttr defaultRenderGlobals.preRenderLayerMel`; $shaveVrayOldPostRenderLayer = `getAttr defaultRenderGlobals.postRenderLayerMel`; // Insert our own callbacks into the start of the strings. { string $parts[]; tokenize($shaveVrayOldPreRenderLayer, ";", $parts); int $n = size($parts); int $found = false; for ($i = 0; $i < $n; $i++) { if("shaveVrayPreRender" == $parts[$i]) { $found = true; } } if(!$found) { setAttr defaultRenderGlobals.preRenderLayerMel -type "string" ("shaveVrayPreRender;"+$shaveVrayOldPreRenderLayer); } } { string $parts[]; tokenize($shaveVrayOldPostRenderLayer, ";", $parts); int $n = size($parts); int $found = false; for ($i = 0; $i < $n; $i++) { if("shaveVrayPostRender" == $parts[$i]) { $found = true; } } if(!$found) { setAttr defaultRenderGlobals.postRenderLayerMel -type "string" ("shaveVrayPostRender;"+$shaveVrayOldPostRenderLayer); } } } /* //not used global proc shaveVrayReSetRenderCallbacks() { //print "shaveVrayReSetRenderCallbacks"; global string $shaveVrayOldPreRenderLayer; global string $shaveVrayOldPostRenderLayer; //set own attributes if("shaveVrayPreRender" != $shaveVrayOldPreRenderLayer) setAttr defaultRenderGlobals.preRenderLayerMel -type "string" $shaveVrayOldPreRenderLayer; else setAttr defaultRenderGlobals.preRenderLayerMel -type "string" ""; if("shaveVrayPostRender" != $shaveVrayOldPostRenderLayer) setAttr defaultRenderGlobals.postRenderLayerMel -type "string" $shaveVrayOldPostRenderLayer; else setAttr defaultRenderGlobals.postRenderLayerMel -type "string" ""; $shaveVrayOldPreRenderLayer = ""; $shaveVrayOldPostRenderLayers = ""; } */ global proc int shaveAreVrayRenderCallbacksSet() { string $pre = `getAttr defaultRenderGlobals.preRenderLayerMel`; string $post = `getAttr defaultRenderGlobals.postRenderLayerMel`; //if($pre == "shaveVrayPreRender" && // $post == "shaveVrayPostRender") // return 1; //else // return 0; { string $parts[]; tokenize($pre, ";", $parts); int $n = size($parts); int $found = false; for ($i = 0; $i < $n; $i++) { if("shaveVrayPreRender" == $parts[$i]) $found = true; } if($found) return true; } { string $parts[]; tokenize($post, ";", $parts); int $n = size($parts); int $found = false; for ($i = 0; $i < $n; $i++) { if("shaveVrayPostRender" == $parts[$i]) $found = true; } if($found) return true; } return false; } global proc shaveEnsureVrayRenderCallbacksSet() { //print "shaveEnsureVrayRenderCallbacksSet\n"; if(shave_rendererIsVray() == 1) { if(shaveAreVrayRenderCallbacksSet() == 0) shaveVraySetRenderCallbacks; } } // clear obsolete preRenderMel/postRenderMel of some were saved within the scene // global proc shaveClearObsoleteVrayCallbacks() { //print "shaveClearObsoleteVrayCallbacks\n"; string $pre = `getAttr defaultRenderGlobals.preRenderMel`; string $post = `getAttr defaultRenderGlobals.postRenderMel`; if($pre != "") { $pre = shave_removeShaveCallbacksFromStr($pre,"shaveVrayPreRenderFrame"); setAttr defaultRenderGlobals.preRenderMel -type "string" $pre; } if($post != "") { $post = shave_removeShaveCallbacksFromStr($post,"shaveVrayPostRenderFrame"); setAttr defaultRenderGlobals.postRenderMel -type "string" $post; } $pre = `getAttr defaultRenderGlobals.preMel`; $post = `getAttr defaultRenderGlobals.postMel`; if($pre != "") { $pre = shave_removeShaveCallbacksFromStr($pre,"shaveVrayPreRender"); setAttr defaultRenderGlobals.preMel -type "string" $pre; } if($post != "") { $post = shave_removeShaveCallbacksFromStr($post,"shaveVrayPostRender"); setAttr defaultRenderGlobals.postMel -type "string" $post; } } ///////////////////////////////////////////////////////////// global proc shave_resetSelectedRenderer() { global string $shave_previousRenderer; $shave_previousRenderer = ""; shave_selectedRendererChanged(); } global proc shave_catchSelectedRendererChanges() { global int $shave_curRendererAttrJobID; global int $shave_rendererChangeJobID; string $renderer = ""; // // Maya doesn't maintain the selected renderer settings in batch mode, // so there's no point in catching changes to them. // if (`about -batch`) return; // // If defaultRenderGlobals.currentRenderer doesn't exist yet, then make // sure that we get called again when it's created. // if (!attrExists("defaultRenderGlobals", "currentRenderer")) { if (!shave_scriptJobExists($shave_curRendererAttrJobID)) { $shave_curRendererAttrJobID = `scriptJob -runOnce yes -killWithScene -attributeAdded "defaultRenderGlobals.currentRenderer" "shave_catchSelectedRendererChanges"`; } return; } // // Initialize the selected renderer. // global string $shave_previousRenderer; //$shave_previousRenderer = shave_selectedRenderer(); shave_selectedRendererChanged(); //need it if default renderre set to something else that Maya Software // // If there is no script job currently monitoring renderer changes, // then create one. // if (!shave_scriptJobExists($shave_rendererChangeJobID)) { $shave_rendererChangeJobID = `scriptJob -attributeChange "defaultRenderGlobals.currentRenderer" "shave_selectedRendererChanged" -killWithScene`; } } global proc int shave_hasCallback(string $a, string $b) { string $token; string $tokens[]; $numTokens = `tokenize $a ";" $tokens`; for($token in $tokens) { if($token == $b) return 1; } return 0; } // // Chain an attribute by saving its old value, if there is one, before // setting the new value. // global proc shave_chainStringAttr(string $node, string $attr, string $newValue) { if (objExists($node)) { // // Get the old value. // string $oldValue = getAttr($node + "." + $attr); // // If it's the same as the new value, then do nothing. This will // prevent us from accidentally kicking the old value out of the store. // //this test is not good //if ($oldValue != $newValue) if(shave_hasCallback($oldValue, $newValue) == 0) { string $oldValueAttr = "shave_old_" + $attr; // // If the node does not yet have an attribute for storing the old // value, then create one. // if (!attributeExists($oldValueAttr, $node)) addAttr -dt "string" -ln $oldValueAttr $node; // // Save the old value. // setAttr -type "string" ($node + "." + $oldValueAttr) $oldValue; // // Set the new value. // //setAttr -type "string" ($node + "." + $attr) $newValue; setAttr -type "string" ($node + "." + $attr) ($newValue+";"+$oldValue); } } return; } // // Unchain an attribute by restoring its old value, if there is one. // global proc shave_unchainStringAttr(string $node, string $attr) { if (objExists($node)) { string $oldValueAttr = "shave_old_" + $attr; string $oldValue = ""; if (attributeExists($oldValueAttr, $node)) { $oldValue = getAttr($node + "." + $oldValueAttr); setAttr -type "string" ($node + "." + $oldValueAttr) ""; } setAttr -type "string" ($node + "." + $attr) $oldValue; } return; } // // Return the previous value of a chained string attribute. // global proc string shave_getChainedStringAttr(string $node, string $attr) { string $value = ""; if (objExists($node)) { string $oldValueAttr = "shave_old_" + $attr; if (!attributeExists($oldValueAttr, $node)) return ""; $value = getAttr($node + "." + $oldValueAttr); } return $value; } global proc shave_exportStart() { global int $shave_exportingFile; $shave_exportingFile = true; } global proc shave_exportEnd() { global int $shave_exportingFile; $shave_exportingFile = false; } // // Procedures for handling Live mode. // global proc shave_toggleLive() { global int $liveJob; // // Make sure that the shaveGlobals node is there. // shaveGlobals(); if($liveJob == 0) { // For Live Mode we only need the vertex-level textures to be // up-to-date. shaveUpdateTextures -vertexParams; $liveJob = `scriptJob -e "idle" "shave_liveUpdate"`; setAttr shaveGlobals.liveMode 1; } else shave_cancelLive(); } global proc shave_cancelLive() { global int $liveJob; if($liveJob != 0) { // // Make sure that the shaveGlobals node is there. // shaveGlobals(); scriptJob -f -kill $liveJob; $liveJob = 0; setAttr shaveGlobals.liveMode 0; // Pop all the hair back to its original position. currentTime `currentTime -q`; } } global proc shave_liveUpdate() { currentTime `currentTime -q`; } // // Helper live recording and playback // global proc shaveGlobalsEditor () { // Make sure that the shaveGlobals node is there. shaveGlobals(); // The 'shaveGlobals' call above may have modified the selection list. // As a result, Maya will be waiting to refresh the AE with the // current selection on the next idle cycle. So if we make the AE // display shaveGlobals right now, it could immediately get wiped and // replaced by the current selection. So we do it during the next idle // cycle so that it will happen *after* Maya has refreshed the AE. evalDeferred("showEditor shaveGlobals"); } global proc shave_reapairShape(string $shaveHairShape) { string $groupNode; if (!objExists("|shaveDisplayGroup")) { $groupNode = "|" + `group -em -w -n shaveDisplayGroup`; } // // If it does exist, but is from a referenced file, then we need to // create a seperate one anyway. // else if (isNodeReferenced("|shaveDisplayGroup")) { $groupNode = "|" + `group -em -w -n shaveDisplayGroup`; } // // We can use the group node already in the scene. // else $groupNode = "|shaveDisplayGroup"; string $shaveDisplay = `createNode transform -n shavedisplay -p $groupNode`; string $shaveDisplayShape = `createNode mesh -n shaveDisplayShape -p $shaveDisplay`; toggle -template -state on $shaveDisplayShape; sets -add initialShadingGroup $shaveDisplay; setAttr -lock true ($shaveDisplay+".translate"); setAttr -lock true ($shaveDisplay+".rotate"); setAttr -lock true ($shaveDisplay+".scale"); connectAttr ($shaveHairShape + ".outputMesh") ($shaveDisplayShape + ".inMesh"); connectAttr ($shaveDisplayShape + ".msg") ($shaveHairShape + ".displayNode"); } global proc shave_Repair() { string $nodes[] = `ls -type shaveHair`; string $node; for ($node in $nodes) { string $plug = $node + ".outputMesh"; string $cmd = "listConnections -d off -s on "+ $plug; string $list[] = eval($cmd); if(size($list)==0); shave_reapairShape($node); } } global proc int shave_createHair () { string $shaveHairShape; // // Some of the growth meshes may consist of just a subset of the mesh's // faces. For these we need to create a groupParts node to add the // component info to the mesh geom, and a groupId node which links the // groupParts node to a specific 'objectGroups' slot in the mesh's // instObjGroups entry. // // That's a lot of work. Fortunately, if we put all the selections // into a temporary set, the 'sets' command will do most of that work // for us and we can just steal what we need. // string $growthSel[] = getFilteredSelection(true); if (size($growthSel) == 0) return false; // // Make sure we have a shaveGlobals node. // shaveGlobals(); // // If this is the first shaveHair node in this scene, then delete any // shave-created files which are sitting in the stat and temp // directories for this scene. // string $shaveHairShapes[] = `ls -type shaveHair`; if (size($shaveHairShapes) == 0) shaveCleanupTmpDirs(); // // If the user has 'Hide All Hair' turned on, warn zir that zie won't // be seeing the newly-created hair. // int $isHidden = isHideAll(); if ($isHidden) { warning( "'Shave -> Hide All Hair' is currently on, so you won't" + " see the newly-created hair." ); } // // We try to keep all shave display nodes parented under a common group // node, just to help keep the hypergraph and outliner clean. // // However, if a file containing hair has been referenced into the // scene, then we won't be able to use its group node, so we create a // separate one. // // First, we check if the group exists, and create it if it doesn't. // string $groupNode; if (!objExists("|shaveDisplayGroup")) { $groupNode = "|" + `group -em -w -n shaveDisplayGroup`; } // // If it does exist, but is from a referenced file, then we need to // create a seperate one anyway. // else if (isNodeReferenced("|shaveDisplayGroup")) { $groupNode = "|" + `group -em -w -n shaveDisplayGroup`; } // // We can use the group node already in the scene. // else $groupNode = "|shaveDisplayGroup"; // // Set up the shavedisplay node hierarchy. We set the state to // templated to discourage the inadvertent selection and transformation // of these nodes. // $shaveDisplay = `createNode transform -n shavedisplay -p $groupNode`; $shaveDisplayShape = `createNode mesh -n shaveDisplayShape -p $shaveDisplay`; toggle -template -state on $shaveDisplayShape; sets -add initialShadingGroup $shaveDisplay; // // There's a callback out there which deletes orphaned shaveNodes, to // keep the scene clean. We don't want it to delete the shaveNode // we're about to create, so disable deletion until we're done. // shave_enableOrphanDeletion(false); // // Create the new shaveHair node. // $shaveHairShape = shave_createNode("shaveHair", "shaveHairShape#", false, false); // // Lock all the transform attributes so the shaveDisplay node does not // get inadvertantly moved away from the growth object. // setAttr -lock true ($shaveDisplay+".translate"); setAttr -lock true ($shaveDisplay+".rotate"); setAttr -lock true ($shaveDisplay+".scale"); // // Connect ourselves to time so that even in the case that the growth // mesh has not been updated, our node is evalled. // connectAttr time1.outTime ($shaveHairShape + ".time"); // // Select the growth list and tell the hair node to use it. // select -r $growthSel; shaveNode -e -gl $shaveHairShape; // // The hair node no longer looks like an orphan, so it's safe to // re-enable orphan deletion. // shave_enableOrphanDeletion(true); // // Connect Shave Node to the Display mesh // connectAttr ($shaveHairShape + ".outputMesh") ($shaveDisplayShape + ".inMesh"); // // Maya may insert other nodes (e.g. copyUVSet) into the connection // between the shaveHair node and its display node, so let's make a // connection which won't change, making it easier to find the display // node later on. // connectAttr ($shaveDisplayShape + ".msg") ($shaveHairShape + ".displayNode"); // Tell the shaveHairShape that it is just being created for the first // time (as opposed to being created as part of a file load). setAttr -type "string" ($shaveHairShape + ".evalOption") "create"; // // Make the newly created shaveHair node current. // shaveSetCurrentNode($shaveHairShape); // // If we're hiding hair, then set this one to hidden as well. // if ($isHidden) setAttr ($shaveHairShape + ".displayAs") 0; // // Update the menu items to reflect the new shaveHair node. // shave_enableMenus; return true; } global proc purgeShave() { error("The 'purgeShave' command has been deprecated. Please use 'shavePurge' instead."); } global proc shave_Purge() { if (!`about -batch`) { string $answer = `confirmDialog -m ("This will delete all hair in the scene,\n" + " except for that from referenced files.\n\n" + "It is not undoable.\n\n" + "Do you wish to continue?") -button "Yes" -button "No"`; if ($answer != "Yes") return; } // // Delete the shaveHair nodes and their entourages. // string $nodes[] = `ls -type shaveHair`; string $node; for ($node in $nodes) deleteShaveNode($node); // // Delete the shaveGlobals node(s). // $nodes = `ls -type shaveGlobals`; for ($node in $nodes) { if (!deleteNode($node)) warning("Could not delete shaveGlobals node '" + $node + "'."); } // // Delete shaveVrayShader node(s) if any. // shaveVrayShader -delete -all; //extra check $nodes = `ls -type shaveVrayShader`; for ($node in $nodes) { if (!deleteNode($node)) warning("Could not delete shaveVrayShader node '" + $node + "'."); } global string $shave_prevStatDir; $shave_prevStatDir = ""; shave_enableMenus(); flushUndo; } global proc shave_selectionChanged() { // // If one of the shave styling tools is active, do component // conversion. // string $ctx = `currentCtx`; string $ctxType = `contextInfo -class $ctx`; if (substring($ctxType, 1, 5) == "shave") shaveUtil -convertComponentSelections; global int $gShaveOrphanDeletionDisableCount; if ($gShaveOrphanDeletionDisableCount == 0) { // A node could be removed as part of an undo. Doing orphan // deletion during an undo can cause Maya to crash, so let's defer // it to an idle cycle. evalDeferred shaveDeleteOrphans; } } global proc shave_enableOrphanDeletion(int $enable) { global int $gShaveOrphanDeletionDisableCount; if ($enable) $gShaveOrphanDeletionDisableCount--; else $gShaveOrphanDeletionDisableCount++; } global proc shaveDeleteOrphans() { string $shaveNodes[]; string $shaveNode; string $growths[]; $shaveNodes = `ls -typ shaveHair`; for($shaveNode in $shaveNodes) { if (!getAttr($shaveNode + ".instancingStatus")) { $growths = `listConnections -s yes -d no ($shaveNode + ".inputCurve") ($shaveNode + ".inputMesh") ($shaveNode + ".inputSurface")`; if (size($growths) == 0) deleteShaveNode($shaveNode); } } if (!`about -batch`) { shave_enableMenus; shave_refreshGlobalsAE(); } } global proc shave_rebuildNodeMenu( string $menu, string $cmd, int $markCurrent, int $disableCurrent ) { string $currentShaveNode = shave_getCurrentNode(); // // Clear out any old items. // menu -e -deleteAllItems $menu; // // Add all of the shaveHair nodes as menu items. // string $nodes[] = `ls -type shaveHair`; string $node; for ($node in $nodes) { int $isCurrent = ($node == $currentShaveNode); int $enabled = !($disableCurrent && $isCurrent); if ($markCurrent) { menuItem -label $node -parent $menu -checkBox $isCurrent -enable $enabled -command ("evalEcho " + $cmd + "(\"" + $node + "\")"); } else { menuItem -label $node -parent $menu -enable $enabled -command ("evalEcho " + $cmd + "(\"" + $node + "\")"); } } } global proc shave_enableMenus() { // // The plugin sometimes calls this proc on a deferred basis. This // means that it is possible for the plugin to be unloaded before this // proc gets round to being run. So we need to be sure that the plugin // is still loaded before doing anything. // if (`pluginInfo -q -l shaveNode` && !`about -batch`) { string $shaveHairShapes[] = `ls -type shaveHair`; int $haveShaveNodes = (size($shaveHairShapes) > 0); // // Under Windows, there's a bug such that the Shave Select menu // item doesn't redraw if it's just been enabled or disabled, until // the user rolls the pointer over it. // // So we force an update by toggling the visibility. Note that // since making a menu visible automatically enables it, we have // to do the toggle first, *then* the enable/disable. // if (`about -nt`) { menu -e -visible false shaveSelectMenu; menu -e -visible true shaveSelectMenu; } menu -e -enable $haveShaveNodes shaveSelectMenu; } } global proc shave_brushTool(int $showPropertySheet) { string $tool = "shaveBrushCtx"; if (!`shaveBrushCtx -exists $tool`) shaveBrushCtx $tool; setToolTo $tool; if ($showPropertySheet) toolPropertyWindow; } global proc shave_cutTool(int $showPropertySheet) { string $tool = "shaveCutCtx"; if (!`shaveCutCtx -exists $tool`) shaveCutCtx $tool; setToolTo $tool; if ($showPropertySheet) toolPropertyWindow; } global proc shaveCreateHairCopy(string $sourceNode) { if (shave_createHair()) { shave_copyHair($sourceNode); } } global proc shave_createHairFromPresetSelect() { eval("shavePresetWin shaveCreateHairFromPreset"); } global proc shaveCreateHairFromPreset(string $preset) { //////////////////////////////////////////////// string $collisionSel[] = getFilteredSelection(false); //////////////////////////////////////////////// if (($preset != "") && shave_createHair()) { // // Force the newly-created shaveHair node to evaluate, otherwise it's // first evaluation will simply overwrite its new params with // Shave's defaults. // string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { dgeval ($shaveHairShape + ".outputMesh"); shavePreset_apply($preset, $shaveHairShape); ///////////////////////////// string $selection[] = shave_saveSelection(); shave_clearDynamics($shaveHairShape); shave_resetRest(); select -r $collisionSel; shaveNode -e -cl $shaveHairShape; shave_restoreSelection($selection); ///////////////////////////// // off enableCollision setAttr ($shaveHairShape + ".enableCollision") false; } } } global proc shave_copyHairFromPresetSelect() { eval("shavePresetWin shaveCopyHairFromPreset"); } global proc shaveCopyHairFromPreset(string $preset) { if ($preset != "") { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { shavePreset_apply($preset, $shaveHairShape); } } } // This copies the hair from $sourceNode onto the current shaveHair node. // Copying the hair implies copying the parameters as well. global proc shave_copyHair(string $sourceNode) { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { shave_clearDynamics($shaveHairShape); // // Make sure that the param vals get copied from the shaveHair node's // plugs into its internal param struct. // currentTime `currentTime -q`; shaveNode -e -copyHair $sourceNode $shaveHairShape; setAttr -type "string" ($shaveHairShape + ".evalOption") "doTransplant"; } } // // Same as Maya's "Duplicate Input Graph" option, except that we also // duplicate shaveHair nodes. // global proc shave_multiClone() { // // We'll need to know which duplicated shapes match up with which input // shapes. While we can, in theory, do the match based on their // position within their respective DAG sub-trees, that can get pretty // hairy. So let's just give them all a custom attribute containing an // identifying number. // int $id = 0; string $inShapes[] = `ls -sl -l -dag -leaf`; int $numShaveNodes = 0; string $shape; string $shaveHairShapes[]; string $tmp[]; if (size($inShapes) == 0) return; for ($shape in $inShapes) { // // What, if any, shaveHair nodes is this surface attached to, either as // a growth or collision? // if (nodeType($shape) == "mesh") { $tmp = `listConnections -type shaveHair -sh on ($shape + ".worldMesh")`; } else if (nodeType($shape) == "nurbsSurface") { $tmp = `listConnections -type shaveHair -sh on ($shape + ".worldSpace")`; } else if (nodeType($shape) == "subdiv") { $tmp = `listConnections -type shaveHair -sh on ($shape + ".worldSubdiv")`; } int $n = size($tmp); if ($n > 0) { // // Give this surface a unique ID by which we can recognize its // duplicate later on. // if (!attributeExists("shaveCounter", $shape)) addAttr -at "long" -ln "shaveCounter" $shape; setAttr ($shape + ".shaveCounter") $id; $id++; // // Add any new shaveHair nodes to our list. // int $i, $j; for ($i = 0; $i < $n; $i++) { // // Have we seen this one yet? // for ($j = 0; $j < $numShaveNodes; $j++) if ($tmp[$i] == $shaveHairShapes[$j]) break; // // If not, add it to the array. // if ($j == $numShaveNodes) $shaveHairShapes[$numShaveNodes++] = $tmp[$i]; } } clear $tmp; } // If the user has parented shaveHair nodes with geometry then the // 'duplicate' cmd may end up creating duplicate shaveHair nodes which // we don't want. So we take 'before' and 'after' snapshots of the // shaveHair nodes in the scene and delete any new ones. string $shaveHairBefore[] = `ls -l -type shaveHair`; // Do the duplication. duplicate -returnRootsOnly -upstreamNodes; // Preserve the selection list. string $savedSelection[] = shave_saveSelection(); // Get rid of any new shaveHair nodes created by the 'duplicate' cmd. string $shaveHairAfter[] = `ls -l -type shaveHair`; if (size($shaveHairAfter) > size($shaveHairBefore)) { string $newNode; string $oldNode; for ($newNode in $shaveHairAfter) { int $foundIt = false; for ($oldNode in $shaveHairBefore) { if ($oldNode == $newNode) { $foundIt = true; break; } } if (!$foundIt) deleteShaveNode($newNode); } } // // Find the matching duplicate shapes. // string $node; string $outShapes[]; $tmp = `ls -sl -l -dag -leaf`; for ($node in $tmp) { if (attributeExists("shaveCounter", $node)) { $id = getAttr($node + ".shaveCounter"); $outShapes[$id] = $node; } } // // Step through each shaveHair node and duplicate those which have one or // more of the original surfaces in their growth set. // for ($node in $shaveHairShapes) { // // Get the shaveHair node's growth surfaces. // string $growthIn[] = `shaveNode -q -growthList $node`; string $growthOut[]; string $growth; // // MEL BUG: Even though $growthOut is declared inside the loop, it // does not get recreated on each iteration. So we must // be sure to clear it ourselves. // clear $growthOut; for ($growth in $growthIn) { // // The growth list may contain components as well as entire // surfaces, so make sure that we only have the surface name. // $tmp = `ls -l -objectsOnly $growth`; // // Does this growth surface have a cloning ID? // if (attributeExists("shaveCounter", $tmp[0])) { // // Get the name of its duplicate. // $id = getAttr($tmp[0] + ".shaveCounter"); string $dup = $outShapes[$id]; // // If the growth item had a component specification, add // that on to the end of the duplicate's name. // string $component = match("[.].*$", $growth); if (size($component) > 0) $dup = $dup + $component; $growthOut[size($growthOut)] = $dup; } } // // If this shaveHair node actually had one or more of its growth // surfaces duplicated, then duplicate it as well. // if (size($growthOut) > 0) { select -r $growthOut; shaveCreateHairCopy($node); // // We need to give the new shaveHair node the same collision set as // the original. However, if any of the collision surfaces were // duplicated, then we should replace it with its duplicate in // the new shaveHair node's collision set. // string $collIn[] = `shaveNode -q -collisionList $node`; string $collOut[]; string $coll; clear $collOut; for ($coll in $collIn) { // // The collision list may contain components as well as // entire surfaces, so make sure that we only have the // surface name. // $tmp = `ls -l -objectsOnly $coll`; // // Does this collision surface have a cloning ID? // if (attributeExists("shaveCounter", $tmp[0])) { // // Get the name of its duplicate. // string $dup; $id = getAttr($tmp[0] + ".shaveCounter"); $dup = $outShapes[$id]; // // If the collision item had a component specification, // add that on to the end of the duplicate's name. // string $component = match("[.].*$", $coll); if (size($component) > 0) { $dup = $dup + $component; } $collOut[size($collOut)] = $dup; } else { // // This collision surface was not duplicated, so just // use it as-is. // $collOut[size($collOut)] = $coll; } } // // If we have a collision list, then apply it to the new // shaveHair node. // if (size($collOut) > 0) { select -r $collOut; shave_setCollisionMesh(); } } } // // Remove the cloning ID attribute from all of the original and // duplicate shapes. // for ($node in $inShapes) { if (attributeExists("shaveCounter", $node)) deleteAttr -at "shaveCounter" $node; } for ($node in $outShapes) { if (($node != "") && attributeExists("shaveCounter", $node)) deleteAttr -at "shaveCounter" $node; } // // Restore the selection list. // shave_restoreSelection($savedSelection); } // This resets the rest state of the hair to match the current state. If // liveMode is enabled, then it is disabled. global proc shave_resetRest() { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { select $shaveHairShape; setAttr -type "string" ($shaveHairShape + ".evalOption") "resetRest"; currentTime `currentTime -q`; } shave_cancelLive(); } global proc shave_doScaleSelectAll() { if(`window -exists shaveScaleAllDlg`) { if(`floatSliderGrp -exists shaveScaleAllSlider`) { $val = `floatSliderGrp -query -value shaveScaleAllSlider`; shaveUtil -scaleAll $val; } deleteUI -window shaveScaleAllDlg; } } global proc shave_ScaleSelectAll() { if(`window -exists shaveScaleAllDlg`) { deleteUI -window shaveScaleAllDlg; } window -title "Scale All" -iconName "Scale All" -widthHeight 360 140 -sizeable false -minimizeButton false -maximizeButton false shaveScaleAllDlg; columnLayout -adjustableColumn true -columnAttach "both" 20 -columnAlign "center"; { separator -style "none" -height 12; floatSliderGrp -label "Scale" -annotation "Soft Scale" -minValue 0.0001 -maxValue 2.0 -fieldMaxValue 1000.0 -field true -value 1.0 -columnAttach 1 "left" 5 -columnWidth3 40 30 120 shaveScaleAllSlider; separator -style "none" -height 12; rowLayout -numberOfColumns 4 -columnWidth 1 26 -columnWidth 3 10 -columnAlign 2 "center" -columnAlign 4 "center"; { separator -style "none"; button -label "OK" -align "center" -width 80 -command shave_doScaleSelectAll ; separator -style "none"; button -label "Cancel" -width 80 -align "center" -command ("deleteUI -window shaveScaleAllDlg") ; setParent ..; } separator -style "none" -height 12; setParent ..; } showWindow shaveScaleAllDlg; } global proc shave_doScaleSelectCurrent() { if(`window -exists shaveScaleCurDlg`) { if(`floatSliderGrp -exists shaveScaleCurSlider`) { $val = `floatSliderGrp -query -value shaveScaleCurSlider`; shaveUtil -scaleCur $val; } deleteUI -window shaveScaleCurDlg; } } global proc shave_ScaleSelectCurrent() { if(`window -exists shaveScaleCurDlg`) { deleteUI -window shaveScaleCurDlg; } window -title "Scale Current" -iconName "Scale Current" -widthHeight 360 140 -sizeable false -minimizeButton false -maximizeButton false shaveScaleCurDlg; columnLayout -adjustableColumn true -columnAttach "both" 20 -columnAlign "center"; { separator -style "none" -height 12; floatSliderGrp -label "Scale" -annotation "Soft Scale" -minValue 0.0001 -maxValue 1.0 -field true -value 1.0 -columnAttach 1 "left" 5 -columnWidth3 40 30 120 shaveScaleCurSlider; separator -style "none" -height 12; rowLayout -numberOfColumns 4 -columnWidth 1 26 -columnWidth 3 10 -columnAlign 2 "center" -columnAlign 4 "center"; { separator -style "none"; button -label "OK" -align "center" -width 80 -command shave_doScaleSelectCurrent ; separator -style "none"; button -label "Cancel" -width 80 -align "center" -command ("deleteUI -window shaveScaleCurDlg") ; setParent ..; } separator -style "none" -height 12; setParent ..; } showWindow shaveScaleCurDlg; } // This procedure sets the contents of the skull set associated with the current // shaveHair node to be the current selection global proc shave_setCollisionMesh () { string $collisionSel[] = getFilteredSelection(false); if (size($collisionSel) > 0) { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { string $selection[] = shave_saveSelection(); shave_clearDynamics($shaveHairShape); shave_resetRest(); select -r $collisionSel; shaveNode -e -cl $shaveHairShape; shave_restoreSelection($selection); // on enableCollision setAttr ($shaveHairShape + ".enableCollision") true; } } } global proc shave_clearCollisionMesh() { string $shaveHairShape = shave_getCurrentNode(); string $selection[] = shave_saveSelection(); select -cl; shaveNode -e -collisionList $shaveHairShape; shave_restoreSelection($selection); } // This procedure sets the contents of the hair set associated with the current // shaveHair node to be the current selection global proc shave_setHairMesh () { string $growthSel[] = getFilteredSelection(true); if (size($growthSel) > 0) { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { shave_clearDynamics($shaveHairShape); currentTime `currentTime -q`; shave_resetRest(); select -r $growthSel; shaveNode -e -gl $shaveHairShape; // // We may now have UV set assignments which refer to objects // which are no longer in the hair's growth set. So get rid of // them. // removeStaleUVSetAssignments($shaveHairShape); select $shaveHairShape; } } } // // Select the growth or collision objects for the current shaveHair node. // global proc shave_selectMesh(string $sel) { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { if($sel == "growth") select -r `shaveNode -q -growthList $shaveHairShape`; else select -r `shaveNode -q -collisionList $shaveHairShape`; } } global proc shave_createSplineLocks() { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") { string $curves[] = `ls -type nurbsCurve -dag -l -sl`; if (size($curves) > 0) { string $cmd = "shaveNode -e -splineLock"; string $curve; for ($curve in $curves) $cmd += " -curve " + $curve; $cmd += " " + $shaveHairShape; eval($cmd); } else error("Shave: No curves selected."); } else error("Shave: No hair node selected."); } global proc shave_deleteSplineLocks() { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape != "") shaveNode -e -splineLock $shaveHairShape; else error("Shave: No hair node selected."); } proc string removeOldInstanceObject(string $shaveHairShape) { // // If we already have a connection to an instance node, get rid of it. // string $connections[]; string $instancePlug = $shaveHairShape + ".instanceMesh"; $connections = `listConnections -plugs yes -source yes -destination no $instancePlug`; if (size($connections) > 0) disconnectAttr $connections[0] $instancePlug; // // Get the shaveHair node's display shape. // string $displayShape = shave_getDisplayShape($shaveHairShape); // // If the display shape is connected to a shader, break the connection. // if ($displayShape != "") { $connections = `listConnections -destination yes -source no -plugs yes -connections yes -type shadingEngine $displayShape`; if (size($connections) > 0) disconnectAttr $connections[0] $connections[1]; } return $displayShape; } // // Update the instance object. // proc setInstanceObject(string $shaveHairShape, string $newInstanceMesh) { string $selection[] = shave_saveSelection(); // // Remove the old instance object. // string $displayShape = removeOldInstanceObject($shaveHairShape); // // Make sure that the new instance object is in a form that we can use. // select -r $newInstanceMesh; performFreezeTransformations(0); $newInstanceMesh = triangulate($newInstanceMesh); // // Connect the new instance object to the shaveHair node, then disconnect // it. We want to copy the mesh info but not have it update whenever // the instance object changes. // connectAttr ($newInstanceMesh + ".worldMesh") ($shaveHairShape + ".instanceMesh"); dgeval ($shaveHairShape + ".outputMesh"); disconnectAttr ($newInstanceMesh + ".worldMesh") ($shaveHairShape + ".instanceMesh"); // // Get the new instance object's shader. // string $shader = getShader($newInstanceMesh); // // Connect the shaveHair node's display node to the same shader as the // new instance object. // if ($shader != "") { connectAttr -nextAvailable ($displayShape + ".instObjGroups") ($shader + ".dagSetMembers"); } // // Turn on instancing in the shaveHair node. // setAttr ($shaveHairShape + ".instancingStatus") true; shave_restoreSelection($selection); } global proc shave_clearInstance() { string $shaveHairShape = shave_getCurrentNode(); // // Turn off instancing in the shaveHair node. // setAttr ($shaveHairShape + ".instancingStatus") false; // // Remove the old instance object. // string $displayShape = removeOldInstanceObject($shaveHairShape); // // Find the shaveHair node's base mesh. // string $connections[] = `listConnections -source yes -destination no -shapes yes ($shaveHairShape + ".inputMesh")`; if (size($connections) > 0) { // // Connect the display node to the same shader as the shaveHair node's // base mesh. // string $shader = getShader($connections[0]); if ($shader != "") { connectAttr -nextAvailable ($displayShape + ".instObjGroups") ($shader + ".dagSetMembers"); } } // // If the AE is displaying the shaveGlobals node, this may change the // state of some of its controls. // shave_refreshGlobalsAE(); } global proc shave_setInstance() { // // If we're setting a new instance object, let's first make sure that // we have a valid mesh. // string $selection[] = `ls -sl -dag -leaf -type mesh -noIntermediate`; if (size($selection) == 0) { error("You must select a poly mesh as the instance object."); } else if (size($selection) > 1) { error("You have more than one poly mesh selected."); } setInstanceObject(shave_getCurrentNode(), $selection[0]); // // If the AE is displaying the shaveGlobals node, this may change the // state of some of its controls. // shave_refreshGlobalsAE(); } /* * Compute dynamics. We just loop through the frames, switching time. * We've set the "runDynamics" attribute, and let our internal compute() * function handle the rest. */ global proc shaveDynamics (string $mode) { // // Can't do dynamics without a stat file directory. // string $statDir = shaveGetStatDir(); if ($statDir == "") { confirmDialog -title "No Stat File Directory Specified" -message ("To use Dynamics, you must first use the Shave Globals\n" + "window to specify a directory for the state files to\n" + "be stored in."); return; } // // Can we actually write to that directory? // string $testFileName = "shaveStatFile_testing.stat"; string $file = shaveExpandStatFilePath($testFileName); if ($file == "") { confirmDialog -title "Cannot Create Stat File Directory" -message ("The directory '" + $statDir + "'\n" + "could not be created. Please check your access\n" + "privileges or use the Shave Globals window to\n" + "specify a different directory."); return; } int $fd = 0; catchQuiet($fd = fopen($file, "w")); if ($fd != 0) { fclose($fd); sysFile -del $file; } else { confirmDialog -title "Cannot Create Stat Files" -message ("Stat files cannot be created in the directory\n" + "'" + $statDir + "'\n" + "Please check your access to this directory or use\n" + "the Shave Globals window to specify a different one."); return; } global int $liveJob; shave_cancelLive(); int $i; $startF = `playbackOptions -q -minTime`; $endF = `playbackOptions -q -maxTime`; string $shaveHairShapes[]; clear $shaveHairShapes; if($mode == "all") $shaveHairShapes = `ls -type shaveHair`; else { string $curNode = shave_getCurrentNode(); if ($curNode != "") $shaveHairShapes[0] = $curNode; } if (size($shaveHairShapes) > 0) { for ($shaveHairShape in $shaveHairShapes) setAttr ($shaveHairShape + ".runDynamics") -1; // // Go to the start time and trigger the base frame calculation for // all affected nodes. // currentTime $startF; // // In interactive mode, simply setting the time will generally cause // the hair nodes to update because of the resulting redraw. However // this is not the case in batch or prompt mode since there are no // redraws. So instead we pull the 'trigger' attr to force the update. // for ($shaveHairShape in $shaveHairShapes) getAttr ($shaveHairShape + ".trigger"); // // Ok, we did the first frame, now run with runDyamics set to 1 // for ($shaveHairShape in $shaveHairShapes) setAttr ($shaveHairShape + ".runDynamics") 1; // // Switching frames, calculating dynamics // shave_progressBarInit($endF-$startF, "Calculating Dynamics"); for ( $i = $startF ; $i <= $endF; $i++ ) { shave_progressBarStep($i - $startF); if(shave_progressBarQuery()) break; currentTime $i; for ($shaveHairShape in $shaveHairShapes) getAttr ($shaveHairShape + ".trigger"); } shave_closeProgressBar(); // // Setting runDynmaics to 2 will allow viewing playback of dynamics // for($shaveHairShape in $shaveHairShapes) setAttr ($shaveHairShape + ".runDynamics") 2; } } /* * In our UI we have the concept of a "current" shave node. This because * we can have more than one, and by setting a current, it makes the other * menu options easier (ie the user doesn't have to pick which node every * time he/she wants to do a particular operation). */ global proc shaveSetCurrentNode(string $newNode) { // // Make sure that we have the shaveHair node itself and not its // transform. // string $tmp[] = `ls -dag -type shaveHair $newNode`; if (size($tmp) == 0) { error("Shave: attempt to assign '" + $newNode + "' as current shaveHair node."); return; } $newNode = $tmp[0]; // // If we're in component selection mode then hilite the shaveNode, // otherwise select it. // if (`selectMode -q -co`) { select -cl; hilite -r $newNode; } else select -r $newNode; } global proc string shave_getCurrentNode() { string $nodes[]; if (`selectMode -q -co`) $nodes = `ls -hilite -dag -type shaveHair`; else $nodes = `ls -sl -dag -type shaveHair`; if (size($nodes) > 0) return $nodes[0]; return ""; } global proc string[] shave_getDisplayGroups() { string $groups[]; int $numGroups= 0; string $shaveHairShape; string $shaveHairShapes[] = `ls -type shaveHair`; for ($shaveHairShape in $shaveHairShapes) { // // Get the parent group of this shaveHair node's display node. // $displayNode = shave_getDisplayNode($shaveHairShape); string $tmp[] = `listRelatives -parent $displayNode`; // // If there are no parent groups, then the display node itself will // act as its own group. // if (size($tmp) == 0) $tmp[0] = $displayNode; // // Step through each parent group. (Normally there is only one, // but Maya allows more so we need to handle it.) // string $group; for ($group in $tmp) { int $i; // // Have we already processed this group node? // for ($i = 0; $i < $numGroups; $i++) if ($group == $groups[$i]) break; // // If it's not already on the list, add it. // if ($i == $numGroups) $groups[$numGroups++] = $group; } } return $groups; } global proc string shave_getDisplayNode(string $shaveHairShape) { // // Note that while the connection is really to the shape node, the // listConnections command below will return its transform because // we haven't used the '-shapes true' flag. // string $sel[] = `listConnections ($shaveHairShape + ".displayNode")`; // // If we didn't find a display node, and haven't converted old // shaveHair nodes yet, then this might be an old node with old display // connections. So update its display connections and see if we get // the display node back. // global int $shave_initOldNodes_done; if ((size($sel) == 0) && !$shave_initOldNodes_done) { $sel[0] = shave_adjustDisplayConnection($shaveHairShape); } return $sel[0]; } global proc string shave_getDisplayShape(string $shaveHairShape) { string $displayNode = shave_getDisplayNode($shaveHairShape); if ($displayNode != "") { string $tmp[] = `listRelatives -children -fullPath $displayNode`; if (size($tmp) > 0) return $tmp[0]; } return ""; } global proc shaveDeleteCurrent() { string $shaveHairShape = shave_getCurrentNode(); select -r $shaveHairShape; if (!`about -batch`) { string $message = "You are about to delete " + $shaveHairShape + " and all associated nodes."; string $answer = `confirmDialog -title "Delete Confirmation" -message $message -button "Continue" -button "Cancel"`; if ($answer != "Continue") return; } deleteShaveNode($shaveHairShape); shave_enableMenus; shave_refreshGlobalsAE(); } global proc shaveAboutUs () { string $message = "Shave and a Haircut for Maya\n"; $message += "(c) 2019 Epic Games\n"; $message += "US Patent #6,720,962\n"; $message += "Version "; $message += shaveVersion(); $message += "\n\nMaya port by:\nJoe Alter\nDean Edmonds\nSteve Galle\nBijan Forutanpour\nVladimir Dubovoy\nYosuke Katusura"; confirmDialog -title "Shave & A Haircut" -message $message -button "OK" ; } global proc shave_refreshGlobalsAE() { global string $gAttributeEditorWindowName; global string $gAEBaseLayoutName; global string $gAETabLayoutName; global string $gAEFocusNode; if ((`window -exists $gAttributeEditorWindowName` && `window -q -vis $gAttributeEditorWindowName`) || (`isAttributeEditorVisible`)) { if (objExists($gAEFocusNode) && (nodeType($gAEFocusNode) == "shaveGlobals")) { // // Simply setting the render mode to its current value will // cause the AE to refresh the enable state of its controls. // int $mode = `getAttr ($gAEFocusNode + ".renderMode")`; setAttr ($gAEFocusNode + ".renderMode") $mode; } } } /* * Create the Shave menus in Maya. In the toplevel menubar */ global proc shaveMenu() { // The global string variable gMainWindow contains the name of top // Level Maya window. Using this as the parent in a menu command // will create a new menu at the same level as "File", "Edit", etc. global string $gMainWindow; // Create a top level menu called "shave". Its only menu item // is called "Move in shave", and when invoked by the user, it // will call the createSphereAndAttachshaveHair node procedure shown above. // menu -label "Shave" -parent $gMainWindow -tearOff false -allowOptionBoxes true -postMenuCommand "shave_rebuildMenu" shaveMenu; menu -label "Shave Select" -parent $gMainWindow -tearOff false -enable true -postMenuCommand "shave_rebuildNodeMenu(\"shaveSelectMenu\",\"shaveSetCurrentNode\",true,false)" shaveSelectMenu; // // As of Maya 8.0, we now have separate menu sets. We want our // menus to show up for all. // global int $gShaveMayaVersion[]; if ($gShaveMayaVersion[0] >= 8) { menuSet -e -addMenu shaveMenu commonMenuSet; menuSet -e -addMenu shaveSelectMenu commonMenuSet; } // // Set the enabled/disabled state of the menu items appropriately. // shave_enableMenus(); } global proc shave_rebuildMenu() { string $shaveHairShapes[] = `ls -type shaveHair`; string $currentNode = shave_getCurrentNode(); int $haveShaveNodes = (size($shaveHairShapes) > 0); int $haveCurrent = ($currentNode != ""); int $isOSX = `about -mac`; int $isSplineLocked = false; if ($haveCurrent) $isSplineLocked = getAttr($currentNode + ".splineLock"); // // Clear out any old items. // menu -e -deleteAllItems shaveMenu; // // Create the menu. // setParent -menu shaveMenu; { menuItem -label "Create New Hair" -c "shaveSelectPresetAndCreateHair" shaveMenuItem1; menuItem -label "Create Hair Copy" -subMenu true -enable $haveShaveNodes -postMenuCommand "shave_rebuildNodeMenu(\"shaveCreateHairCopyItem\",\"shaveCreateHairCopy\",false,false)" shaveCreateHairCopyItem; { setParent -menu ..; } menuItem -label "Multi-Clone" -enable true -c "evalEcho shaveMultiClone" shaveMultiCloneItem; menuItem -label "Brush Tool" -enable $haveCurrent -c "evalEcho shaveBrushTool" shaveBrushToolItem; // // There is a bug on OSX such that using '-enable false' on a menu // optionBox can cause other menu items to be disabled (but not // greyed out) when the menu is rebuilt. Fortunately, OSX is also // different from the other platforms in that disabling the main // item also disables its optionBox, so we can work around the // problem by not using the '-enable' flag on optionBoxes on OSX. // if ($isOSX) { menuItem -optionBox true -l "Shave Brush Tool Options" -c "evalEcho shaveBrushToolOptions"; } else { menuItem -optionBox true -l "Shave Brush Tool Options" -enable $haveCurrent -c "evalEcho shaveBrushToolOptions"; } menuItem -label "Cut Tool" -enable $haveCurrent -c "evalEcho shaveCutTool" shaveCutToolItem; if ($isOSX) { menuItem -optionBox true -l "Shave Cut Tool Options" -c "evalEcho shaveCutToolOptions"; } else { menuItem -optionBox true -l "Shave Cut Tool Options" -enable $haveCurrent -c "evalEcho shaveCutToolOptions"; } menuItem -label "Edit Current" -subMenu true -enable $haveCurrent shaveMenuItem2; { menuItem -label "Comb Using Curves" -c "evalEcho shaveCombFromCurves" shaveCombUsingCurves; menuItem -label "Apply Preset" -enable true -c "evalEcho shaveSelectAndApplyPreset" shaveCopyFromPresetItem; menuItem -label "Instancing" -subMenu true shaveInstanceMenu; { menuItem -label "Set Instance Obj" -c "evalEcho shaveSetInstance" shaveSetInstanceObject; menuItem -label "Clear Instance Obj" -c "evalEcho shaveClearInstance" shaveClearInstanceObject; setParent -menu ..; } menuItem -label "Update Growth Mesh" -c "evalEcho shaveUpdateGrowthSurfaces"; menuItem -label "Update Collision Mesh" -c "evalEcho shaveUpdateCollisionSurfaces"; menuItem -label "Clear Collision Mesh" -c "evalEcho shaveClearCollisionSurfaces"; menuItem -label "Spline Locks" -subMenu true shaveSplineLocksMenu; { menuItem -label "Create Locks" -enable $haveCurrent -c "evalEcho shaveCreateSplineLocks" shaveCreateSplineLocksItem; menuItem -label "Delete Locks" -enable ($haveCurrent && $isSplineLocked) -c "evalEcho shaveDeleteSplineLocks" shaveDeleteSplineLocksItem; setParent -menu ..; } menuItem -label "Convert" -subMenu true shaveConvertMenu; { menuItem -label "Guides to Curves" -enable $haveCurrent -c "evalEcho shaveCreateCurvesFromGuides" shaveGuidesToCurves; menuItem -label "Hairs to Curves" -c "evalEcho shaveCreateCurvesFromHairs" shaveHairsToCurves; menuItem -label "Hairs to Polygons" -enable $haveCurrent -c "evalEcho shaveCreatePolysFromHairs" shaveHairsToPolys; setParent -menu ..; } /* menuItem -label "Poly Shading" -subMenu true shaveShadingSubmenu; { menuItem -label "Create Vertex Shader" -enable true -c "evalEcho shaveCreateVertexShader" shaveCreateVertexShaderItem; menuItem -label "Re-Synch Shader" -enable true -c "evalEcho shaveSynchVertexShader" shaveReSynchShaderItem; setParent -menu ..; } */ menuItem -label "Shave UV Linking" -enable true -c "evalEcho shaveUVLinkingEditor" shaveLinkMenuItem; menuItem -label "Reset Rest Position" -enable $haveCurrent -c "evalEcho shaveResetRestPose" shaveResetRestItem; menuItem -label "Delete" -c "evalEcho shaveDelete" shaveMenuItem15; setParent -menu ..; } menuItem -label "Scale" -subMenu true //-enable $haveCurrent shaveMenuItemScale; { menuItem -label "All" -enable true -c "evalEcho shaveScaleSelectAll" shaveMenuItemScaleAll; menuItem -label "Current" -enable $haveCurrent -c "evalEcho shaveScaleSelectCurrent" shaveMenuItemScaleCurrent; setParent -menu ..; } menuItem -label "Shave Globals..." -enable true -c "evalEcho shaveGlobalsEditor" shaveMenuItem7; menuItem -label "Shadow Attributes" -enable true -tearOff true -subMenu true shaveShadowAttributesMenu; { menuItem -label "Add To Selected Lights" -c "evalEcho shaveAddShadowParamsToLights" shaveAddShadowParamsItem; menuItem -label "Remove From Selected Lights" -c "evalEcho shaveRemoveShadowParamsFromLights" shaveRemoveShadowParamsItem; setParent -menu ..; } menuItem -label "Dynamics" -enable $haveShaveNodes -tearOff true -subMenu true shaveDynamicsMenu; { global int $liveJob; menuItem -label "Live Mode" -cb ($liveJob != 0) -c "evalEcho shaveToggleLiveMode" shaveDynamicsLiveItem; menuItem -label "Run Dynamics Current Node" -enable $haveCurrent -c "evalEcho shaveRunDynamicsCurrent" shaveDynamicsRunCurrentItem; menuItem -label "Clear Dynamics Current Node" -enable $haveCurrent -c "evalEcho shaveClearDynamicsCurrent" shaveDynamicsClearCurrentItem; menuItem -label "Run Dynamics All Nodes" -c "evalEcho shaveRunDynamicsAll" shaveDynamicsRunAllItem; menuItem -label "Clear Dynamics All Nodes" -c "evalEcho shaveClearDynamicsAll" shaveDynamicsClearAllItem; setParent -menu ..; } menuItem -label "Reselect Surfaces" -subMenu true -enable $haveCurrent shaveMenuItem4; { menuItem -label "Hair growth polys" -c "evalEcho shaveSelectGrowthSurfaces"; menuItem -label "Collision polys" -c "evalEcho shaveSelectCollisionSurfaces"; setParent -menu ..; } menuItem -label "Update Textures" -enable $haveShaveNodes -c "evalEcho shaveSynchToTextures" shaveUpdateTexMenu; menuItem -label "Hide All Hair" -enable true -cb `isHideAll` -c "evalEcho shaveToggleHideHair" shaveHideHairItem; menuItem -label "Create Shelf" -enable true -c "evalEcho shaveCreateShelf" shaveShelfItem; menuItem -label "About..." -c "shaveAboutUs" shaveMenuItem8; setParent -menu ..; } } global proc shave_toggleHideHair() { int $hideIt = !isHideAll(); // // Make sure that we have a shaveGlobals node. // shaveGlobals(); // // Set the Hide Hair flag. // setAttr shaveGlobals.hideHair $hideIt; } global proc shave_toggleFallbackHair() { // // Make sure that we have a shaveGlobals node. // shaveGlobals(); if( getAttr("shaveGlobals.doFallback")) setAttr shaveGlobals.doFallback 0; else setAttr shaveGlobals.doFallback 1; } global proc shaveGlobals() { string $objs[] = `ls shaveGlobals`; if (size($objs) == 0) { string $sel[] = shave_saveSelection(); string $hfn = `workspace -q -rd` + "shaveHair"; string $namespace = `namespaceInfo -cur`; catch(`namespace -set ":"`); // // Create the shaveGlobals node. // createShaveGlobals("shaveGlobals"); setAttr -type "string" shaveGlobals.hairFilenamePrefix $hfn ; setAttr -type "string" shaveGlobals.sso all ; setStatDir(""); catch(`namespace -set $namespace`); // // Create a script job to monitor changes in the stat directory // path for this scene. // evalDeferred("shaveMonitorStatDir"); // // Lock the shaveGlobals node. // lockNode -lock on shaveGlobals; // Set up third-party renderers. shave_initRenderers(); shave_restoreSelection($sel); } } global proc shaveMonitorStatDir() { global int $shave_tmpDirMonitorJob = -1; if (objExists("shaveGlobals")) { // // If we have an existing job, it's *probably* good, but just to be // safe we'll kill it off and create a new one. // shave_killJob($shave_tmpDirMonitorJob); $shave_tmpDirMonitorJob = `scriptJob -killWithScene -ac shaveGlobals.tmpDir shaveStatDirChanged`; } } global proc string shave_getSatDataDir() { string $satDataDir = getenv("SHAVE_SATELLITE_DATA"); if ($satDataDir == "") return ""; // // If the directory does not yet exist, try to create it. // if (!`filetest -d $satDataDir` && !`sysFile -makeDir $satDataDir`) { warning( "Shave: The SHAVE_SATELLITE_DATA environment variable points to '" + $satDataDir + "' which does not exist and cannot be created." ); return ""; } // // It's only useful if we can write to it. // if (!`filetest -w $satDataDir`) { warning( "Shave: The SHAVE_SATELLITE_DATA environment variable points to '" + $satDataDir + "' to which you do not have write permission." ); return ""; } // // If it doesn't end with a slash, add one. // if (match("/$", $satDataDir) == "") $satDataDir += "/"; return $satDataDir; } global proc string shaveGetStatDir() { if (!objExists("shaveGlobals")) return ""; string $path = getAttr("shaveGlobals.tmpDir"); // // If the path is empty or full of blanks, then return an empty path. // if (match("^ +$", $path) == $path) return ""; // // Make sure that only forward slashes are used. // $path = strReplaceAll($path, "\\", "/"); // // The path may be relative to the project directory, so expand it. // $path = `workspace -expandName $path`; // // If it doesn't end with a slash, add one. // if (match("/$", $path) == "") $path += "/"; return $path; } global proc string shaveGetTempDir() { string $tempDir = getenv("TMPDIR"); string $altDir = `workspace -expandName "./data/"`; // // If the path doesn't end with a slash, add one. // if (($tempDir != "") && (match("/$", $tempDir) == "")) $tempDir += "/"; // // If the directory doesn't exist, see if we can create it. // if (($tempDir != "") && (!`filetest -d $tempDir`)) { if (!`sysFile -makeDir $tempDir`) { warning( "Shave: The TMPDIR environment variable points to '" + $tempDir + "' which does not exist." ); warning("Shave: Using '" + $altDir + "' instead."); return $altDir; } } // // If we can't write to the temp directory, use the alternate. // if (!`filetest -w $tempDir`) { warning( "Shave: The TMPDIR environment variable points to '" + $tempDir + "' to which you do not have write permission." ); warning("Shave: Using '" + $altDir + "' instead."); return $altDir; } return $tempDir; } global proc string shaveExpandStatFilePath(string $filename) { string $statDir = shaveGetStatDir(); if ($statDir == "") return ""; // // Make sure the directory exists. // if (!`sysFile -makeDir $statDir`) return ""; return ($statDir + $filename); } global proc string shaveExpandTempFilePath(string $filename) { string $tempDir = shaveGetTempDir(); if ($tempDir == "") return ""; return ($tempDir + $filename); } proc cleanupTmpDir(string $dir) { if (($dir != "") && `file -q -exists $dir`) { // // Get rid of any shaveHair node object files. // string $files[] = `getFileList -fld $dir -fs "shaveObjFile_*.obj"`; string $file; for ($file in $files) sysFile -del ($dir + $file); // // Get rid of any instance object files. // $files = `getFileList -fld $dir -fs "shaveInstance_*.obj"`; for ($file in $files) sysFile -del ($dir + $file); // // Get rid of any stat files. // $files = `getFileList -fld $dir -fs "shaveStatFile_*.stat"`; for ($file in $files) sysFile -del ($dir + $file); } } global proc shaveCleanupTmpDirs() { cleanupTmpDir(shaveGetStatDir()); cleanupTmpDir(shaveGetTempDir()); } global proc shave_deleteFile(string $file) { ////////// debug //////// //print $file; print "\n"; if (($file != "") && `filetest -r $file`) sysFile -del $file; } // // Wipe out dynamics for the specified node(s). // global proc shave_clearDynamics(string $node) { string $shaveHairShapes[]; if ($node == "all") $shaveHairShapes = `ls -type shaveHair`; else if ($node != "") $shaveHairShapes[0] = $node; else { string $curNode = shave_getCurrentNode(); if ($curNode != "") $shaveHairShapes[0] = $curNode; } for ($shaveHairShape in $shaveHairShapes) { if (getAttr($shaveHairShape + ".runDynamics") != 0) { setAttr ($shaveHairShape + ".runDynamics") 0; shave_removeStatFiles($shaveHairShape); } } } global proc shave_removeStatFiles(string $shaveHairShape) { string $statFiles[] = getStatFiles($shaveHairShape); string $file; for ($file in $statFiles) sysFile -del $file; } global proc shave_deferCmd( string $command ) { eval($command); } global proc shave_connectShadowShader(string $lightName, int $lightID) { string $selected[] = shave_saveSelection(); // // Create a shaveShadowNode for this light. // // string $tmpStr = "createNode shaveShadowNode -n shaveShadowNode"+$lightID; // string $shadowNode = eval($tmpStr); string $shadowNode = shave_createNode("shaveShadowNode", ("shaveShadowNode" + $lightID), false, false); // // Copy the light's existing shadow colour over to the shaveShadowNode. // float $colour[] = getAttr($lightName + ".shadowColor"); setAttr ($shadowNode + ".shadColor") $colour[0] $colour[1] $colour[2]; // // We are going to be connecting our shaveShadowNode to the light's // 'color' attribute. Does it already have another connection there? // string $lightColourInput = $lightName + ".color"; string $lightColourSource = `connectionInfo -sfd $lightColourInput`; if ($lightColourSource != "") { // // The light already has an incoming colour connection, so redirect // it to run through our shaveShadowNode. // connectAttr $lightColourSource ($shadowNode + ".color"); disconnectAttr $lightColourSource $lightColourInput; } else { // // The light doesn't have an incoming colour connection, so copy // its current colour values over to the shaveShadowNode. // $colour = getAttr($lightName + ".color"); setAttr ($shadowNode + ".color") $colour[0] $colour[1] $colour[2]; } setAttr ($shadowNode + ".lid") $lightID; // // Connect the shaveShadowNode's output colour to the light's input // colour. // connectAttr ($shadowNode + ".outColor") $lightColourInput; shave_restoreSelection($selected); } global proc shave_disconnectShadowShaders() { string $drivenPlug; string $shadowNode; string $shadowNodes[] = `ls -type shaveShadowNode`; string $tmp[]; for ($shadowNode in $shadowNodes) { // // Due to past bugs, the scene may have a chain of shadow nodes // feeding into the light. When we delete one node in that // chain, some of the others may disappear as well. So we should // make sure that this shadow node still exists. // if (objExists($shadowNode)) { // // Get the name of the colour plug which this shadow node is // driving. // string $shadowColourIn = $shadowNode + ".color"; string $shadowColourOut = $shadowNode + ".outColor"; $tmp = `listConnections -p on -s no -d yes $shadowColourOut`; // // If this shadow node is simply driving another shadow node, // then step down until we find the non-shadow node at the end // of the chain. // while ((size($tmp) > 0) && (objectType($tmp[0]) == "shaveShadowNode")) { $tmp = `ls -o $tmp[0]`; $shadowColourOut = $tmp[0] + ".outColor"; $tmp = `listConnections -p on -s no -d yes $shadowColourOut`; } if (size($tmp) > 0) $drivenPlug = $tmp[0]; else $drivenPlug = ""; // // Does the shadow node have someone else feeding it's input // colour? // string $shadowColourSrc = `connectionInfo -sfd $shadowColourIn`; while ($shadowColourSrc != "") { // // If the driving node is not a shadow node as well, then // we're done. // if (objectType($shadowColourSrc) != "shaveShadowNode") break; // // Looks like we have a chain of shadow nodes, due to some // earlier bug in the plugin. So step back to the driving // node's input. // $tmp = `ls -o $shadowColourSrc`; $shadowColourIn = $tmp[0] + ".color"; $shadowColourSrc = `connectionInfo -sfd $shadowColourIn`; } if ($shadowColourSrc != "") { // // Move the connection over to the driven plug. // if ($drivenPlug != "") connectAttr -force $shadowColourSrc $drivenPlug; delete $shadowNode; } else { // // A light's 'color' attribute will reset to white upon // losing an input connection, so we need to preserve the final // colour value *before* deleting the shadowNode, then restore // it to the light. // float $colour[] = getAttr($shadowColourIn); delete $shadowNode; if ($drivenPlug != "") setAttr $drivenPlug $colour[0] $colour[1] $colour[2]; } } } } global proc shave_progressBarInit(int $totalExpected, string $progName) { global string $gMainProgressBar; // // The progressBar command fails if the maxValue is less than the // minValue, so let's make sure that that doesn't happen. // if ($totalExpected <= 0) $totalExpected = 1; progressBar -edit -beginProgress -isInterruptable true -status $progName -minValue 0 -maxValue $totalExpected $gMainProgressBar; } global proc int shave_progressBarQuery() { global string $gMainProgressBar; int $result = `progressBar -query -isCancelled $gMainProgressBar`; return $result; } global proc shave_progressBarStep(int $newValue) { global string $gMainProgressBar; progressBar -edit -pr $newValue $gMainProgressBar; } global proc shave_closeProgressBar() { global string $gMainProgressBar; progressBar -edit -endProgress $gMainProgressBar; } global proc shave_synchShader(string $shaveHairShape) { if ($shaveHairShape == "") $shaveHairShape = shave_getCurrentNode(); // // Get the material for the shader to which the current shaveHair node is // assigned. // string $displayShape = shave_getDisplayShape($shaveHairShape); string $shader = getShader($displayShape); if ($shader == "") return; string $tmp[] = `listConnections -s yes -d no ($shader+".surfaceShader")`; if (size($tmp) == 0) return; string $material = $tmp[0]; float $specular = getAttr($shaveHairShape + ".specular"); float $specularTint[] = getAttr($shaveHairShape + ".specularTint"); float $specularTint2[] = getAttr($shaveHairShape + ".specularTint2"); float $diffuse = getAttr($shaveHairShape + ".amb/diff"); int $numPasses = getAttr($shaveHairShape + ".hairPasses"); float $transparency = 1.0 - (1.0 / (float)$numPasses); float $gloss = getAttr($shaveHairShape + ".gloss"); string $materialType = nodeType($material); switch ($materialType) { case "phongE": // // Gloss is always in the range 0.0 - 0.1 (non-inclusive). // setAttr ($material + ".roughness") (1.0 - 10.0 * $gloss); setAttr ($material + ".whiteness") -type double3 1.0 1.0 1.0; // Fall-thru... case "blinn": case "phong": setAttr ($material + ".reflectivity") 0.0; setAttr ($material + ".specularColor") -type double3 ($specularTint[0]*$specular) ($specularTint[1]*$specular) ($specularTint[2]*$specular); //setAttr ($material + ".specularColor2") -type double3 // ($specularTint2[0]*$specular) // ($specularTint2[1]*$specular) // ($specularTint2[2]*$specular); // Fall-thru... case "lambert": setAttr ($material + ".diffuse") $diffuse; setAttr ($material + ".ambientColor") (1.0 - $diffuse) (1.0 - $diffuse) (1.0 - $diffuse); setAttr ($material + ".transparency") -type double3 $transparency $transparency $transparency; break; } } global proc string shave_createMayaVertexShader(string $shaveHairShape) { // // Get the shading group to which the shaveHair node's display node is // assigned. // string $displayNode = shave_getDisplayNode($shaveHairShape); string $displayShape = shave_getDisplayShape($shaveHairShape); string $shadingGroup = getShader($displayShape); if ($shadingGroup == "") { error( "shave: " + $displayShape + " is not assigned to any shading group." ); } // // Get the surface shader material used by the shading group. // string $tmp[]; $tmp = `listConnections -s yes -d no ($shadingGroup + ".surfaceShader")`; if (size($tmp) == 0) { error("shave: " + $shadingGroup + " does not have a surfaceShader."); } // // Get the utility node, if any, which is driving the surface shader // material's colour. // string $material = $tmp[0]; string $vertexShader = ""; $tmp = `listConnections -p on -s yes -d no ($material + ".color")`; if (size($tmp[0]) > 0) { // // If the source of the colour is not a shaveVertexShader, then // disconnect it. // tokenize($tmp[0], ".", $tmp); if (nodeType($tmp[0]) != "shaveVertexShader") { disconnectAttr ($tmp[0] + "." + $tmp[1]) ($material + ".color"); } else $vertexShader = $tmp[0]; } // // If we don't have a vertex shader already, create one and connect it // to the material's color input. // if ($vertexShader == "") { $vertexShader = `createNode -n ($shaveHairShape + "VertexShader") shaveVertexShader`; connectAttr ($vertexShader + ".outColor") ($material + ".color"); // // The shaveVertexShader was designed to support multiple surfaces // by connecting a singleShadingSwitch to its 'surfaceIn' attr. // Since we're assigning each surface its own shader, we don't need // the switch and can just hard-wire the surface number to 0. // setAttr ($vertexShader + ".surfaceIndex") 0; } // // Copy the relevant attributes from the shaveHair node to the shader. // // We could do connections, but that would make it difficult for the // user to change shaders. So instead, if the user want's connections // then it's up to zir to set them up and maintain them. That also // makes *our* lives a lot simpler. // shave_synchShader($shaveHairShape); return $vertexShader; } global proc shave_createVertexShader(string $shaveHairShape) { if ($shaveHairShape == "") $shaveHairShape = shave_getCurrentNode(); string $displayNode = shave_getDisplayNode($shaveHairShape); if ($displayNode != "") { shave_createMayaVertexShader($shaveHairShape); } } global proc shave_prepareVertexShaders() { string $savedSelection[] = shave_saveSelection(); // // Find all the shaveHair nodes which are using automatic vertex shaders. // string $shaveHairShapes[] = `ls -type shaveHair`; string $shaveHairShape; for ($shaveHairShape in $shaveHairShapes) { string $displayNode = shave_getDisplayNode($shaveHairShape); if (getAttr($shaveHairShape + ".overrideGeomShader")) { // // If the shaveHair node is using instance hair, we don't want to // touch its shader. // if (!getAttr($shaveHairShape + ".instancingStatus")) { // // If the shaveHair node doesn't have an override shading group, // then create one and assign it to it. // string $shadingGroup = ""; string $tmp[] = `listConnections -p yes -type shadingEngine ($shaveHairShape + ".message")`; if (size($tmp) > 0) { tokenize($tmp[0], ".", $tmp); if ((size($tmp) == 2) && ($tmp[1] == "shaveOverrideShader")) $shadingGroup = $tmp[0]; } string $material = ""; if ($shadingGroup == "") { $shadingGroup = `sets -renderable true -noSurfaceShader true -empty -n ($shaveHairShape + "VertexShaderSG")`; markAsCreatedByShave($shadingGroup); // // Give the shading group a 'shaveOverrideShader' // attribute and connect the shaveHair node to it. // addAttr -at message -ln "shaveOverrideShader" $shadingGroup; connectAttr ($shaveHairShape + ".message") ($shadingGroup + ".shaveOverrideShader"); } else { // // Get the shadingGroup's surface material. // $tmp = `listConnections -s yes -d no ($shadingGroup + ".surfaceShader")`; if (size($tmp) > 0) $material = $tmp[0]; } // // Make sure that the shaveHair node's display node is assigned // to the override shading group. // sets -edit -fe $shadingGroup $displayNode; // // If the shading group does not yet have a surface // material, give it a PhongE. // if ($material == "") { $material = `shadingNode -asShader phongE -n ($shaveHairShape + "Material")`; markAsCreatedByShave($material); connectAttr ($material + ".outColor") ($shadingGroup + ".surfaceShader"); } // Create the vertex shader. // $shader = shave_createMayaVertexShader($shaveHairShape); markAsCreatedByShave($shader); } } else { // // 'overrideGeomShader' is off meaning that the user wants us // to use zir own shader, not ours. Zie will have assigned // that shader to the hairNode, not the display node, so we // have to put the display node into the same shading group as // the hairNode. // string $shader = getShader($shaveHairShape); if ($shader != "") sets -edit -fe $shader $displayNode; } } shave_restoreSelection($savedSelection); } global proc shave_destroyVertexShaders() { // // Find all the Maya vertex shaders. // string $mat; string $mats[]; string $sg; string $sgs[]; string $shader; string $shaders[] = `ls -type shaveVertexShader`; for ($shader in $shaders) { if (isCreatedByShave($shader)) { // // Get the materials to which the vertex shader is connected. // $mats = `listConnections -s no -d yes ($shader + ".outColor")`; delete $shader; for ($mat in $mats) { if (isCreatedByShave($mat)) { // // Get the shading groups to which the material is // connected. // $sgs = `listConnections -s no -d yes ($mat + ".outColor")`; delete $mat; for ($sg in $sgs) if (isCreatedByShave($sg)) delete $sg; } } } } } // // Returns an array of three strings: // // 0: the name of the background shader node // 1: the name of the output plug to be used on the shader // 2: the name of the input plug to be used on the shading group // // If there's an error, or if $create is false and the background shader node // does not currently exist, then string 0 will be empty (""). // proc string[] getBackgroundShader(int $create) { string $shader = "shaveBackgroundShader"; string $shaderType; string $result[3]; $shaderType = "shaveBackground"; $result[1] = "outColor"; $result[2] = "surfaceShader"; if (objExists($shader) && (nodeType($shader) != $shaderType)) delete $shader; if (!objExists($shader)) { if (!$create) $shader = ""; else createNode -n $shader $shaderType; } // Set the shader's 'includeBackfacing' parameter according to the // corresponding shaveGlobals setting. int $includeBackfacing = getAttr("shaveGlobals.shadowMatteIncludeBackfacing"); setAttr ($shader + ".includeBackfacing") $includeBackfacing; $result[0] = $shader; return $result; } global proc shave_setupShadowMatte() { string $savedSelection[] = shave_saveSelection(); string $shadingGroups[] = `ls -type shadingEngine`; string $shadingGroup; // // Create a Shave background shader to use in place of the // existing surface shaders. // string $bgShader[] = getBackgroundShader(true); for ($shadingGroup in $shadingGroups) { // // If this shading group doesn't have any surfaces in it, then // ignore it. // string $tmp[] = `sets -q -nodesOnly $shadingGroup`; if (size($tmp) > 0) { // Does this shading group have a surface shader? // string $conn[]; int $hasAttr = attributeExists($bgShader[2], $shadingGroup); if ($hasAttr) { $conn = `listConnections -s yes -d no ($shadingGroup + "." + $bgShader[2])`; } if (size($conn) > 0) { connectAttr -force ($bgShader[0] + "." + $bgShader[1]) ($shadingGroup + "." + $bgShader[2]); // // Add a message attribute to the shading group and connect // the old shader to it so that we can easily restore it // after the render is done. // if (!attributeExists("shaveOldShader", $shadingGroup)) addAttr -at message -ln "shaveOldShader" $shadingGroup; string $oldShader = $conn[0]; if ($oldShader != "") { connectAttr ($oldShader + ".message") ($shadingGroup + ".shaveOldShader"); } } } } shave_restoreSelection($savedSelection); } global proc shave_cleanupShadowMatte() { // // Some renderers misbehave if we delete shader here so we defer the // cleanup to the next idle cycle instead. // evalDeferred("shave_cleanupShadowMatte_Part2"); } global proc shave_cleanupShadowMatte_Part2() { string $bgShader[] = getBackgroundShader(false); if ($bgShader[0] != "") { // // Get all of the shading groups which this background shader is // feeding. // string $groups[]; string $sg; $groups = `listConnections -s no -d yes ($bgShader[0] + "." + $bgShader[1])`; for ($sg in $groups) { // // Find the shading group's old shader and reconnect it. // if (attributeExists("shaveOldShader", $sg)) { string $tmp[] = `listConnections -s yes -d no ($sg + ".shaveOldShader")`; if (size($tmp) > 0) { connectAttr -force ($tmp[0] + "." + $bgShader[1]) ($sg + "." + $bgShader[2]); disconnectAttr ($tmp[0] + ".message") ($sg + ".shaveOldShader"); } } } // // Delete the background shader. // delete $bgShader[0]; } } global proc shave_setupVolumeShader(string $camera) { global string $shave_coneAngleExpr; global string $shave_coneCapExpr; global string $shave_renderCone; global string $shave_volumeSG; global string $shave_volumeShader; string $savedSelection[] = shave_saveSelection(); // // Create a render volume around the scene. // string $tmp[]; $tmp = createRenderVolume($camera, $tmp); $shave_renderCone = $tmp[0]; $shave_coneAngleExpression = $tmp[1]; $shave_coneCapExpression = $tmp[2]; setAttr ($shave_renderCone + ".visibleInReflections") true; setAttr ($shave_renderCone + ".visibleInRefractions") true; setAttr ($shave_renderCone + ".castsShadows") false; // // Create a shaveVolumeShader and a shading group which uses it. // $shave_volumeShader = `shadingNode -asShader shaveVolumeShader`; $shave_volumeSG = `sets -renderable true -noSurfaceShader true -empty`; connectAttr -f ($shave_volumeShader + ".outColor") ($shave_volumeSG + ".volumeShader"); // // Assign the render volume to the shading group. // sets -e -add $shave_volumeSG $shave_renderCone; shave_restoreSelection($savedSelection); } global proc shave_cleanupVolumeShader() { global string $shave_coneAngleExpr; global string $shave_coneCapExpr; global string $shave_renderCone; global string $shave_volumeSG; global string $shave_volumeShader; destroyRenderVolume( $shave_renderCone, $shave_coneAngleExpr, $shave_coneCapExpr ); if (($shave_volumeSG != "") && objExists($shave_volumeSG)) delete $shave_volumeSG; if (($shave_volumeShader != "") && objExists($shave_volumeShader)) delete $shave_volumeShader; $shave_coneAngleExpr = ""; $shave_coneCapExpr = ""; $shave_renderCone = ""; $shave_volumeSG = ""; $shave_volumeShader = ""; } // // Upgrade old scenes to meet the requirements of the current version of // Shave. // global proc shave_initOldNodes() { string $nodes[]; string $node; shave_enableOrphanDeletion(false); //****************************************************************** // // MERGE shaveGlobals NODES // // There should only be one shaveGlobals node, but early versions of // Shave did not enforce that, with the result that we now have to go // to all this bother to tidy up older scenes. // //****************************************************************** // // Do we have any shaveGlobals nodes in this scene? // $nodes = `ls -type shaveGlobals`; if (size($nodes) > 0) { // // A modern shaveGlobals node is called 'shaveGlobals', is shared, // has the current shaveGlobals version number and doesn't come // from a reference file (which must be true if it's shared). // // Let's find the one which best matches those characteristics. // string $keeper = ""; string $winner = ""; int $winnerIsReferenced; int $winnerHasTmpDir; int $curGlobalsVersion = `shaveInfo -globalsVersion`; for ($node in $nodes) { int $hasRightName = ($node == "shaveGlobals"); int $isShared = isShared($node); int $isReferenced = isNodeReferenced($node); string $tmpDir = getAttr($node + ".tmpDir"); int $nodeVersion = getAttr($node + ".nodeVersion"); int $hasTmpDir = ($tmpDir != ""); if ($hasRightName && $isShared && ($nodeVersion == $curGlobalsVersion)) { $keeper = $node; if ($hasTmpDir) { $winner = $node; $winnerIsReferenced = false; $winnerHasTmpDir = true; } } else if ($hasTmpDir) { // // If there is no other winner, or if the current winner // has no tmpDir value, or if the current winner is // referenced and we are not, then we become the new // winner. // if (($winner == "") || !$winnerHasTmpDir || ($winnerIsReferenced && !$isReferenced)) { $winner = $node; $winnerIsReferenced = $isReferenced; $winnerHasTmpDir = true; } } // // It may be that none of the shaveGlobals have a tmpDir value, // in which case we should fall back to the first // non-referenced node we find. // if (($winner == "") && !$isReferenced) { $winner = $node; $winnerIsReferenced = false; $winnerHasTmpDir = false; } } // // If we don't have a keeper, then create one. Give it a temporary // name which won't clash. // if ($keeper == "") { $keeper = createShaveGlobals("tempShaveGlobals"); } // // If we don't have a winner, then all the shaveGlobals nodes that // we have are referenced and don't have a tmpDir value, so just // take the first node. // if ($winner == "") { $winner = $nodes[0]; $winnerIsReferenced = true; $winnerHasTmpDir = false; } int $winnerVersion = getAttr($winner + ".nodeVersion"); // // If the winner and the keeper are different nodes, then copy the // values and connections from the winner to the keeper. // if ($keeper != $winner) { if (!catch(`lockNode -l off $node`)) copyCommonAttrs($winner, $keeper, true, true); } // // Delete everyone except the keeper (if we can). // int $someAreReferenced = false; for ($node in $nodes) { if ($node != $keeper) { if (!catch(`lockNode -l off $node`)) { if (isNodeReferenced($node)) { $someAreReferenced = true; } else if (catch(`delete $node`)) { warning( "Could not delete extranous shaveGlobals node '" + $node + "'" ); } } } } // // If the keeper doesn't already have the correct name, then try to // rename it. // int $alreadyWarned = false; if ($keeper != "shaveGlobals") { catch(`lockNode -l off $keeper`); catch($keeper = `rename $keeper shaveGlobals`); if ($keeper != "shaveGlobals") { string $msg; $msg = "This scene contains multiple shaveGlobals nodes. Newer\n"+ "versions of Shave prevent this from happening, but\n"+ "this scene, or one of its reference files, apparently\n"+ "predates those changes.\n\n"; if (isNodeReferenced("shaveGlobals")) { string $refFile = getReferenceFileFromNode("shaveGlobals"); $msg += "We've tried to merge all the shaveGlobals nodes into\n"+ "a single node, to be shared by all files. However,\n"+ "the node named 'shaveGlobals' is from the referenced\n"+ "file " + $refFile + " and cannot be deleted.\n\n"+ "You should exit this scene and correct the problem by\n" + "loading the offending reference file into Maya by\n" + "itself, so that Shave can convert it, then save it back\n"+ "out.\n\n"; } else { $msg += "We cannot create a new, shared shaveGlobals node for\n"+ "this scene because we cannot delete the existing node\n" + "named 'shaveGlobals'. Please contact Shave support\n" + "for further help in resolving this problem.\n\n"; } $msg += "You should avoid using or saving this scene until the\n" + "the undeletable shaveGlobals node has been updated or\n" + "removed.\n"; if (`about -batch`) warning($msg); else confirmDialog -title "Duplicate shaveGlobals" -message $msg; $alreadyWarned = true; } } if ((size($nodes) > 1) && !$alreadyWarned) { string $msg = "This scene contains multiple shaveGlobals nodes, most\n" + "likely as a result of having referenced in other files\n"+ "which contain their own Shave hair.\n\n" + "Newer versions of Shave prevent this from happening,\n" + "but this scene apparently predates those changes.\n\n" + "We have merged the extra nodes down to a single node\n" + "named 'shaveGlobals'"; if ($winner == "shaveGlobals") $msg += ".\n"; else { $msg += "and have used the old node named\n" + "'" + $winner + "' for its defaults.\n"; } if ($someAreReferenced) { $msg += "\nSome of the extra shaveGlobals nodes could not be deleted\n"+ "because they come from referenced files. They will not\n"+ "interfere with Shave's operation, but you will continue\n"+ "to see this message whenever you load the scene.\n\n"+ "To eliminate this message, load each of the referenced\n"+ "files into Maya, by itself, and then save it back out.\n"+ "This will allow Shave to convert their shaveGlobals nodes.\n"; } if (`about -batch`) warning($msg); else confirmDialog -title "Duplicate shaveGlobals" -message $msg; } string $statDir = getAttr("shaveGlobals.tmpDir"); if ($statDir != "") { // // If the winner was a pre-version 1 shaveGlobals node, then // its tmpDir entry will be pointing to the directory *above* // the one containing the stat files. We now want it to point // to the directory itself, which will be named after the // scene. // if ($winnerVersion < 1) { // // Convert all of the backslashes to forward slashes. // $statDir = strReplaceAll($statDir, "\\", "/"); // // Make sure the path ends with a slash. // if (match("/$", $statDir) == "") $statDir += "/"; // // Get the bare scene name, without any path or extension. // string $sceneName = `file -q -sceneName`; if ($sceneName == "") $sceneName = "untitled"; else { $sceneName = basename($sceneName, ""); string $extension = match("[.][^.]*$", $sceneName); int $len = size($sceneName) - size($extension); if ($len > 0) $sceneName = substring($sceneName, 1, $len); } // // Add the scene name to the end of the path and make that // the new stat directory. // $statDir += $sceneName; setStatDir($statDir); } // // If the winner was a version 1 shaveGlobals node, then a // relative tmpDir was being interpreted as relative to the // scene file's location, whereas we now interpret it relative // to the project directory, the same as before version 1. // // Since version 1 was never released, only Joe and I should // have any of these lying around, so we can just silently // clear the tmpDir. // else if ($winnerVersion == 1) { // // Is it relative? // if (match("^[.]", $statDir) != "") { setStatDir(""); shave_clearDynamics("all"); } } } if ($winnerVersion < 9) { // // As of shaveGlobals version 9, there is no longer an // instanceRenderMode. There is just an enableInstanceGeometry // flag and if it is on then instances are rendered as geometry. // int $instanceMode = getAttr("shaveGlobals.instanceRenderMode"); // // If the render mode was kNoRender, then we can just disable // instance geometry. // if ($instanceMode == 3) setAttr shaveGlobals.enableInstanceGeometry false; else { // // Enable instance geometry. // setAttr shaveGlobals.enableInstanceGeometry true; // // If the old render mode was kBufferRender, then let the // user know that that's no longer supported. // if ($instanceMode == 0) { warning("shaveGlobals: Buffer rendering of instanced shaveHair nodes is no longer"); warning("shaveGlobals: supported. Instances will be rendered as geometry."); } } // // As of shaveGlobals version 9, shaveLightList, // shadowResolution and shaveShadFuzz no longer exist. // Instead, shadowResolution and shaveShadFuzz are added to // individual lights as dynamic attributes and the light list // consists of those lights which have those attributes. // float $fuzz = getAttr("shaveGlobals.shaveShadFuzz"); int $res = getAttr("shaveGlobals.shadowResolution"); string $lightList = getAttr("shaveGlobals.shaveLightList"); // // If the light list was "all" then we do all spotlights, // otherwise we only do those on the list. Note that we only // do spotLights because prior to version 4.0 of Shave, that // was the only light type supported. // string $lights[]; clear $lights; if (tolower($lightList) == "all") { $lights = `ls -type spotLight`; setAttr shaveGlobals.useAllLights true; } else { string $tmp[]; tokenize($lightList, ", ", $tmp); string $light1; for ($light1 in $tmp) { // // This might be a transform, so drill down to the // light beneath it. // string $tmp2[] = `ls -dag -lf $light1`; string $light2; for ($light2 in $tmp2) { if (nodeType($light2) == "spotLight") $lights[size($lights)] = $light2; } } setAttr shaveGlobals.useAllLights false; } // // Step through each light and add its shadow parameters. // string $light; for ($light in $lights) { shave_addShadowParamsToLight($light); // // Copy the settings from the old shaveGlobals attributes. // setAttr ($light + ".shaveShadowResolution") $res; setAttr ($light + ".shaveShadowFuzz") $fuzz; } // // We no longer allow hairs to be output to RIB as geometry. // Now hairs are always curves and instances are always // geometry. So if the user has selected geometry, change it // to cubic curves. // if (getAttr("shaveGlobals.hairPrimitiveType") == 2) setAttr shaveGlobals.hairPrimitiveType 1; } if ($winnerVersion < 10) { // Prior to version 10 RIB export used the same voxel // resolution and renders. From version 10 onward it has its // own resolution. Since this is a pre-10 node, initialize // the RIB resolution to the same value as the render // resolution. int $voxRes = getAttr("shaveGlobals.voxelResolution"); setAttr shaveGlobals.ribVoxelResolution $voxRes; } if ($winnerVersion < 11) { // Prior to version 11 we had a 'ribBlurUseMayaDefaults' flag. // Now we have an enum of which Maya is just one option. if (getAttr("shaveGlobals.ribBlurUseMayaDefaults")) setAttr shaveGlobals.ribBlurInheritSettings 1; else setAttr shaveGlobals.ribBlurInheritSettings 0; } // // 3D compositing won't work with native illumination, so if // they're both on switch to 2D compositing. // if (getAttr("shaveGlobals.doCompositing") && !getAttr("shaveGlobals.composite2d") && getAttr("shaveGlobals.nativeIllumination")) { warning("shaveGlobals: 3D compositing incompatible with native illumination. Switching to 2D."); setAttr shaveGlobals.composite2d true; } // // Geom shadows are now semi-deprecated, meaning that the // functionality is hidden but still present. However we still // want to force the users away from it, so turn it off if it's on, // and lock the attr. // if (getAttr("shaveGlobals.useGeomForShadows")) { if (`getAttr -lock shaveGlobals.useGeomForShadows`) setAttr -lock off shaveGlobals.useGeomForShadows; setAttr shaveGlobals.useGeomForShadows false; setAttr -lock on shaveGlobals.useGeomForShadows; } // // Lock the shaveGlobals node. The test to see if it is already // locked is so that we don't set the scene's "changed" flag if we // don't have to. Otherwise, every time the user tries to exit a // scene which zie hasn't changed, zie'll still be prompted to save // it. // int $tmp[] = eval("lockNode -q -lock shaveGlobals"); if (!$tmp[0]) catch(`lockNode -lock on shaveGlobals`); // // Make sure that this scene's stat directory exists. // $statDir = shaveGetStatDir(); if ($statDir != "") sysFile -makeDir $statDir; } //****************************************************************** // // UPGRADE OLD shaveNode NODES // // Although shaveNodes have been deprecated in favour of shaveHair // nodes, we still might encounter them in an old scene. So we need to // upgrade them then convert them. // //****************************************************************** int $pre40ReferenceWarningGiven = false; int $somethingVisible = false; $nodes = `ls -type shaveNode`; for ($node in $nodes) { int $nodeVersion = getAttr($node + ".nodeVersion"); string $birthName = getAttr($node + ".birthName"); string $oldCollisionGeom[]; string $oldGrowthGeom[]; int $useOldGeom = false; if ($nodeVersion < 2) { // // If the display node isn't connected to the shaveNode's // 'displayNode' attr, then make it so. // shave_adjustDisplayConnection($node); // // Prior to node version 2, growth objects were stored in a // separate set which was named _hairSet, and // collision objects in one named _skullSet. // // In really old nodes, there was no connection from the // shaveNode to the sets, so we had to find the sets by name. // Since shaveNodes can be renamed, a 'birthName' attr was used // to store the original name of the shaveNode. // // So let's first see what node name we should be using if we // have to look for the sets by name. // string $origName = $birthName; if ($origName == "") $origName = $node; $useOldGeom = true; // // Does the shaveNode have a connection to its growth set? // string $setName[] = listConnections($node + ".growthSet"); if (size($setName) == 0) { // // No connection, so let's see if we can find the set by // name. // $setName[0] = ls($origName + "_hairSet"); } // // If we found a growth set, get its contents. // if ((size($setName) > 0) && ($setName[0] != "")) { $oldGrowthGeom = `sets -q $setName[0]`; deleteNode($setName[0]); } // // Does the shaveNode have a connection to its collision set? // $setName = listConnections($node + ".skullSet"); if (size($setName) == 0) { // // No connection, so let's see if we can find the set by // name. // $setName[0] = ls($origName + "_skullSet"); } // // If we found a growth set, get its contents. // if ((size($setName) > 0) && ($setName[0] != "")) { $oldCollisionGeom = `sets -q $setName[0]`; deleteNode($setName[0]); } // // If the shaveNode has a mesh connected to its 'instanceSource' // attr, then assign that as its instance object. // string $oldAttr = $node + ".instanceSource"; string $meshes[] = `listConnections -s yes -d no -shapes yes -type mesh $oldAttr`; if (size($meshes) > 0) setInstanceObject($node, $meshes[0]); // // Since the 'instanceSource' attribute has been deprecated, break // all connections to it. // string $connections[] = `listConnections -p yes -s yes -d no $oldAttr`; string $connection; for ($connection in $connections) disconnectAttr $connection $oldAttr; $connections = `listConnections -p yes -s no -d yes $oldAttr`; for ($connection in $connections) disconnectAttr $oldAttr $connection; } // // Prior to shaveNode version 3, UV set to texture mappings were // stored as strings, which meant that they became invalid if the // user renamed or deleted a UV set, growth object or texture. // // We now store the mappings as connections. So if this is a // pre-version-3 shaveNode, we must convert the mappings. // if ($nodeVersion < 3) { string $growthObjects[]; string $mappingAttr; string $mapping; string $mappingParts[]; string $uvSetAttr; string $uvSetAttrParts[]; string $uvSetsSoFar[]; string $mappedEntries[] = `listAttr -multi ($node + ".shaveUvSets")`; int $i; int $j; int $numGrowthObjects; int $numMappings = size($mappedEntries); int $numUVSetsSoFar = 0; int $numNewMappings = 0; // // MEL BUG: Do not remove these 'clear' statements as MEL does // not recreate arrays declared within loops on each // new iteration. // clear $growthObjects; clear $mappingParts; clear $uvSetAttrParts; clear $uvSetsSoFar; // // If there are any mappings we'll need to verify them against // the growth list. So get the list. // if ($numMappings > 0) { if ($useOldGeom) $growthObjects = $oldGrowthGeom; else $growthObjects = `shaveNode -q -growthList $node`; // // We only want the objects, not components. // $growthObjects = `ls -o $growthObjects`; // // Get rid of duplicates. // $growthObjects = stringArrayRemoveDuplicates($growthObjects); $numGrowthObjects = size($growthObjects); } for ($i = 0; $i < $numMappings; $i++) { $mappingAttr = $node + "." + $mappedEntries[$i]; $mapping = getAttr($mappingAttr); tokenizeList($mapping, $mappingParts); // // We need at least 2 items (a UV set and a texture) for a // valid mapping. // if (size($mappingParts) > 1) { // // The first part of the mapping is the name of the UV // set plug and the rest are textures it drives. Remove // the UV set plug name from $mappingParts so that we // only have textures left in the array. // $uvSetAttr = $mappingParts[0]; $mappingParts[0] = ""; // // Decompose the UV set plug name so that we can find // out which growth object is supplying it. // tokenize($uvSetAttr, ".", $uvSetAttrParts); // // The name of the growth object should be in the first // part. If it's not there, or if there's no attribute // portion, or if the node doesn't exist, then this // mapping is bogus, so ignore it. // if ((size($uvSetAttrParts) >= 2) && ($uvSetAttrParts[0] != "") && objExists($uvSetAttrParts[0])) { // // Does the shaveNode actually use this growth // object? // string $tmp[]; $tmp[0] = $uvSetAttrParts[0]; $tmp = stringArrayRemove($tmp, $growthObjects); if (size($tmp) < $numGrowthObjects) { // // Have we already seen this UV set? // for ($j = 0; $j < $numUVSetsSoFar; $j++) { if ($uvSetsSoFar[$j] == $uvSetAttr) break; } // // We expect that all of the textures driven by // a given UV set will be part of the same // mapping entry. // // If we've already seen this UV set in an // earlier entry, then this entry will never be // used, so only process it if it's new. // if ($j == $numUVSetsSoFar) { // // Which of the entry's textures still // exist? // string $textures[] = `ls $mappingParts`; $textures = stringArrayRemoveDuplicates( $textures ); int $numTextures = size($textures); if ($numTextures > 0) { // // Connect the UV set plug to a new UV // mapping entry. // string $newEntry = $node + ".hairUVSetAssignments[" + $numNewMappings + "]"; connectAttr $uvSetAttr ($newEntry + ".hairUVSetName"); // // Connect each of the textures to the // new entry. // for ($j = 0; $j < $numTextures; $j++) { connectAttr ($textures[$j] + ".message") ($newEntry + ".hairUVSetTextures[" + $j + "]"); } $numNewMappings++; } // // Add this UV set to the list of those seen so // far. // $uvSetsSoFar[$numUVSetsSoFar++] = $uvSetAttr; } } } } // // Remove the old mapping. // removeMultiInstance -break yes $mappingAttr; } } if ($nodeVersion < 6) { // // Node version 6 introduced the 'overrideGeomShader' flag. // // Since older nodes won't have had this attribute, they'll get // it now at its default setting, which is true. // // However, if the shaveNode is already assigned to some // non-vertex shader, then we should turn the flag off so that // we keep the user's existing shader. // string $displayShape = shave_getDisplayShape($node); string $shader = getShader($displayShape); if ($shader != "") { string $tmp[] = `listConnections -s yes -d no ($shader+".surfaceShader")`; if (size($tmp) > 0) { $tmp = `listConnections -s yes -d no -type shaveVertexShader ($tmp[0]+".color")`; if (size($tmp[0]) == 0) setAttr ($node + ".overrideGeomShader") false; } else setAttr ($node + ".overrideGeomShader") false; } } if ($nodeVersion < 12) { // // Prior to version 12, spline hair used a different number of // sample points per spline. So if this is a spline hair node, // we have to force the node to recreate its blind data using // the new number of sample points. // if (isSplineHair($node)) setAttr -type "string" ($node + ".evalOption") "recreate"; } // // We don't use the following attributes any more, so blank them // out. // if ($birthName != "") setAttr -type "string" ($node + ".birthName") ""; string $val = getAttr($node + ".ribFilenameOpen"); if ($val != "") setAttr -type "string" ($node + ".ribFilenameOpen") ""; $val = getAttr($node + ".ribFilenameClose"); if ($val != "") setAttr -type "string" ($node + ".ribFilenameClose") ""; // // Set the collision method to 1. // // %%% Once we support other collision methods, this will have to // be changed. // if (getAttr($node + ".collisionMethod") != 1) setAttr ($node + ".collisionMethod") 1; // // If 'instancingStatus' is true, then clearly this node *has* been // instanced. So if 'neverBeenInstanced' is also true, it should // be set false. // if (getAttr($node + ".instancingStatus") && getAttr($node + ".neverBeenInstanced")) { setAttr ($node + ".neverBeenInstanced") false; } // // Diagnostic display mode has been removed. If the node was set // to diagnostic display, then switch it to guide display. // if (getAttr($node + ".displayAs") == 3) setAttr ($node + ".displayAs") 0; // // Make sure that every display shape is in a shading group and // that the display transform is not. We used to incorrectly // assign the display transform to the shading group, so if it is // assigned then assign the shape to the same group in its stead. // string $displayNode = shave_getDisplayNode($node); string $displayShape = shave_getDisplayShape($node); string $tmp[]; clear $tmp; if ($displayNode != "") { $tmp = `listConnections -p on -c on -s no -d yes -type shadingEngine $displayNode`; } if (size($tmp) > 0) { // // Note that using 'sets -e -rm' won't work for a transform // because the 'sets' command apparently ignores transforms // when dealing with a renderable set. // disconnectAttr $tmp[0] $tmp[1]; $tmp = `ls -o $tmp[1]`; sets -fe $tmp[0] $displayShape; } else if ($displayShape != "") { $tmp = `listSets -t 1 -o $displayShape`; if (size($tmp) == 0) sets -add "initialShadingGroup" $displayShape; } // // If the display shape is visible, make a note of that since it // means that 'Hide All Hair' is not on. // if (!$somethingVisible && getAttr($displayShape + ".visibility") && getAttr($displayNode + ".visibility")) { string $tmp[] = `listRelatives -parent $displayNode`; if ((size($tmp) > 0) && getAttr($tmp[0] + ".visibility")) $somethingVisible = true; } // // This is now an up-to-date shaveNode, so set the version number // accordingly. // int $latestNodeVersion = `shaveInfo -shaveNodeVersion`; if ($nodeVersion < $latestNodeVersion) setAttr ($node + ".nodeVersion") $latestNodeVersion; //********************************************* // // Convert Old shaveNode To New shaveHair Node // //********************************************* // // If the old shaveNode is from a reference file, then we can't // convert it here: the user will have to load the reference file // separately and let it be converted first. // if (isNodeReferenced($node)) { if (!$pre40ReferenceWarningGiven) { warning( "This scene references files containing hair from an older," ); warning( "incompatible version of Shave. That hair will not display" ); warning( "or render until the reference file has been converted to" ); warning("the most recent version of Shave."); $pre40ReferenceWarningGiven = true; } continue; } // // Create a new shaveHair node. // string $hairNode = shave_createNode("shaveHair", "", false, false); // // Copy the shaveNode's attribute values and connections over to // the shaveHair node. // copyCommonAttrs($node, $hairNode, true, true); // If we've carried growth and collision geometry across from the // old node, assign it now. if ($useOldGeom) { select -r $oldGrowthGeom; shaveNode -e -growthList $hairNode; select -r $oldCollisionGeom; shaveNode -e -collisionList $hairNode; select -cl; } // // The UV set assignment attrs changed names, so we have to copy // those explicitly. // shaveUtil -copyAttr ($node + ".uvSetAssignments") ($hairNode + ".hairUVSetAssignments") -duplicateInConnections -moveOutConnections; // // If the display mode is zero (which used to be Guides and is // now None) then turn on guide display. // if (getAttr($hairNode + ".displayAs") == 0) setAttr ($hairNode + ".displayGuides") on; // // Set the displaySegmentLimit according to whether this is spline // or normal hair. // if (isSplineHair($hairNode)) setAttr ($hairNode + ".displaySegmentLimit") 12; else setAttr ($hairNode + ".displaySegmentLimit") 6; // // Delete the shaveNode and give its name to the shaveHair node. // delete $node; $hairNode = `rename $hairNode $node`; // // Set the shaveHair node's version to 1. Code further down will // take care of upgrading it from there to the current version. // setAttr ($hairNode + ".nodeVersion") 1; // %%% Do we need to do something about assigning the hairShape to // a shading group? } //****************************************************************** // // CLEAN UP OLD DISPLAY GROUPS // // We used to try to group all display nodes under a single group node // named 'shavedisplayg'. We would then control the templating of the // display nodes by templating the group node. // // Unfortunately, file referencing and file importing make it // impossible for us to keep all of the display nodes under a single // group node, and instead we end up with multiple group nodes. // // As a result, we now do all templating directly on the display nodes // themselves (well, their transforms, actually). But there may still // be old scenes out there with a 'shavedisplayg' node which is // templated. So we must turn off that templating. // //****************************************************************** if (objExists("|shavedisplayg")) { toggle -template -state off "|shavedisplayg"; // // Rename the group node so that if the user manually templates it // at some future time, we won't accidentally turn it off. // rename "|shavedisplayg" "|shaveDisplayGroup"; } // // We can't just do a straightforward deletion because we want to // restore the original colours of the lights to which the shadow nodes // are attached. // // Fortunately, shave_disconnectShadowShaders() takes that all into // account. // shave_disconnectShadowShaders(); // // If we have shaveNodes and they are all invisible, then we assume // that Hide Hair is turned on. // if ((size($nodes) > 0) && !$somethingVisible) { // // We used to implement Hide Hair by setting all of the display // nodes and display groups to be invisible. Now it is handled // from within the shaveGlobals node, via the 'hideHair' attr. // // So first, we must undo the old visibility settings by // making all of the display nodes and display groups visible. // for ($node in $nodes) { string $displayNode = shave_getDisplayNode($node); if (!getAttr($displayNode + ".visibility")) setAttr ($displayNode + ".visibility") true; string $displayShape = shave_getDisplayShape($node); if (!getAttr($displayShape + ".visibility")) setAttr ($displayShape + ".visibility") true; } $nodes = shave_getDisplayGroups(); for ($node in $nodes) { if (!getAttr($node + ".visibility")) setAttr ($node + ".visibility") true; } // // Next, turn on Hide Hair. The shaveGlobals node will handle the // rest. // setAttr shaveGlobals.hideHair true; } //****************************************************************** // // UPGRADE OLD shaveHair NODES // //****************************************************************** int $needCollisionGeomWarning = false; string $prevOldCollisionList[]; int $latestNodeVersion = `shaveInfo -shaveHairVersion`; $nodes = `ls -type shaveHair`; for ($node in $nodes) { int $nodeVersion = getAttr($node + ".nodeVersion"); if ($nodeVersion < 2) { // Prior to version 2 there were no objectSet nodes holding the // growth and collision surfaces. So let's redo the growth and // collision surface assignments so that the sets get created, // if necessary. // string $geom[] = `shaveNode -q -growthList $node`; if (size($geom) > 0) { select -r $geom; shaveNode -e -growthList $node; } $geom = `shaveNode -q -collisionList $node`; if (size($geom) > 0) { select -r $geom; shaveNode -e -collisionList $node; } select -cl; } if ($nodeVersion < 3) { // We used to include the growth curves for spline hair in its // collision set even though hair cannot collide with curves. // Let's remove from the collision set any curves which it may // contain. // if (getAttr($node + ".hairGroup") == 4) { // spline hair string $oldCollisionGeom[] = `shaveNode -q -collisionList $node`; if (size($oldCollisionGeom) > 0) { string $newCollisionGeom[]; string $geom; // MEL Bug: Arrays declared within loops can retain // data across iterations of the loop, so we need to // clear them. I think this is fixed in Maya 2016. // clear $newCollisionGeom; for ($geom in $oldCollisionGeom) { if (nodeType($geom) != "nurbsCurve") { $newCollisionGeom[size($newCollisionGeom)] = $geom; } } // If the collision geometry changed, update the node // to use the new geom. Note that it's only safe to do // this with spline hair. If we do it with surface-based // hair it will destroy the rest position info. // if (size($newCollisionGeom) != size($oldCollisionGeom)) { select -r $newCollisionGeom; shaveNode -e -collisionList $node; select -cl; } } } } if ($nodeVersion == 3) { // Because of the MEL Bug mentioned above, version 3 shaveHair // nodes ended up with all of the previous nodes' collision // geometry in their own collision lists. If we had nodes A, // B and C then A's collision list would be fine but B's would // contain A's collision items as well and C would contain both // A's and B's collision items. // // There's no way that we can safely undo this because the // user may have edited the list before saving the file out. // The best we can do is warn them. // string $collisionGeom[] = `shaveNode -q -collisionList $node`; if (size($collisionGeom) > 0) { $needCollisionGeomWarning = true; } // Another problem was that the 'shaveNode -e -collisionList' // command above was failing to xplant with the new collision // list properly. That's been fixed but we need to reissue // the command. // // Note that it's only safe to do this with spline hair. If we do // it with surface-based hair it will destroy the rest position // info. // if (getAttr($node + ".hairGroup") == 4) { // spline hair select -r $collisionGeom; shaveNode -e -collisionList $node; select -cl; } } if ($nodeVersion < 5) { // We've deprecated the 'active' attribute in favour of // 'visibility' so move the value and any connections. // shaveUtil -copyAttr ($node + ".active") ($node + ".visibility") -duplicateInConnections -moveOutConnections; shave_disconnectSrc($node + ".active"); } // Sometimes the connections to our growth or collision geometry // get broken. We don't yet know how this happens. For now we'll // check each node for consistency and repair whatever we can. // // Note that if the commands below don't find any errors then they // won't make any changes to the scene. // shaveNode -e -repair -collisionList $node; shaveNode -e -repair -growthList $node; int $mayaVersion = `about -api`; // In Maya 2016.5 the algorithm used for smoothing meshes (e.g. the // polySmooth node) changed in a way that generated vertices in a // different order from before, which breaks our hair. To get around // that we need to do an xplant if there is a polySmooth operator // of any kind in our hair's history. // if ($mayaVersion >= 201650) { if (!isSplineHair($node)) { string $fileVersion[] = `fileInfo -q version`; int $majorVersion = int(match("[0-9]*", $fileVersion[0])); if (($majorVersion <= 2016) && ($fileVersion[0] != "2016 Extension 2")) { // For some reason if we don't use the -allConnections flag // it won't traverse back through the history of the growth // meshes. // string $history[] = `listHistory -allConnections $node`; string $historyNode; for ($historyNode in $history) { string $type = objectType($historyNode); if (startsWith(objectType($historyNode), "polySmooth")) { setAttr -type "string" ($node + ".evalOption") "doTransplant preserveParams"; break; } } } } } // This is now an up-to-date shaveHair node, so set the version // number accordingly. if ($nodeVersion < $latestNodeVersion) { setAttr ($node + ".nodeVersion") $latestNodeVersion; } } if ($needCollisionGeomWarning) { warning( "\n\n" + " !!! URGENT !!!\n" + " This scene was last saved using a Shave version between\n" + " 9.0v26 and 9.0v30, inclusive. Those versions had a bug\n" + " which may have corrupted the collision geometry lists of\n" + " one or more of the scene's shaveHair nodes.\n" + " Please check the collision geometry lists for all of your\n" + " hair nodes before saving this scene back out.\n\n" ); } // // Let's not have this proc run twice on the same scene. // global int $shave_initOldNodes_done = false; $shave_initOldNodes_done = true; shave_enableOrphanDeletion(true); } // // An old shaveHair node may not have its display node connected to the // shaveHair node's "displayNode" attr, in which case we need to set that up. // global proc string shave_adjustDisplayConnection(string $node) { // // If the display node is already connected to the eponymous attribute, // then we're done. // // WARNING: Don't try replacing this with a call to shave_getDisplayNode // because that proc calls *us*, so you can end up with an // infinite loop. // string $connections[] = `listConnections ($node + ".displayNode")`; if (size($connections) > 0) return $connections[0]; // // Okay, it wasn't there, so this is probably an old node. Let's track // down the display node through the shaveHair node's outputMesh connection. // $connections = `listConnections -s no -d yes ($node + ".outputMesh")`; string $displayNode = ""; if (size($connections) > 0) { if (nodeType($connections[0]) == "mesh") $displayNode = $connections[0]; else { // // The user may have accidentally inserted some history, so // let's skip to the final mesh. // string $nodes[] = `listHistory -f yes $connections[0]`; string $node; for ($node in $nodes) { if (nodeType($node) == "mesh") { $displayNode = $node; break; } } } } // // If we eventually found the display node, then we need to connect it // to the shaveHair node's "displayNode" attr. // if ($displayNode != "") connectAttr ($displayNode + ".msg") ($node + ".displayNode"); return $displayNode; } // // This is used by the standalone shaveRibDump command. // global proc shave_dumpRibs(float $frame) { currentTime $frame; shaveWriteRib; } global proc shave_clearmat() { string $oldNode = shave_getCurrentNode(); if ($oldNode == "") { error("No shaveHair node currently selected."); } // // Select the old node's growth surfaces. // shave_selectMesh("growth"); // // Creat the new node. // shave_createHair(); string $newNode = shave_getCurrentNode(); shave_clearDynamics($newNode); // // Make sure that the param vals get copied from the shaveHair node's // plugs into its internal param struct. // currentTime `currentTime -q`; shaveNode -e -copyHair $oldNode $newNode; setAttr -type "string" ($newNode + ".evalOption") "doTransplant nomat"; dgeval ($newNode + ".outputMesh"); // // Delete the old node. // deleteShaveNode($oldNode); // // Give the new node the same name as the old, then make it the current // shaveHair node. // rename $newNode $oldNode; shaveSetCurrentNode($oldNode); } // // %%% There is much which this doesn't handle, such as renumbering // of frames and whether there should be a period between the filename // and the frame number. See the docs for the renderGlobals node for // all the gory details. // // Even worse, when someone initiates a render from the command line, // they can specify a name completely different from that given in the // defaultRenderGlobals, and we'll never know about it. // global proc string shave_getDRAFileName(float $frame) { string $prefix = getAttr("defaultRenderGlobals.imageFilePrefix"); if ($prefix == "") $prefix = getSceneName(); $prefix = `workspace -expandName $prefix`; $prefix = strReplaceAll($prefix, "\\", "/"); return shave_getFrameFileName($frame, $prefix, "srd"); } global proc string shave_getFrameFileName( float $frame, string $prefix, string $extension ) { float $byFrame = getAttr("defaultRenderGlobals.byFrameStep"); // // Maya only handles integral frame numbers, and obviously only one // frame can occupy a given filename. So if the byFrame value is less // than one, we have to spread the frames out to the next available // integral number, not already taken by a previous frame. // if ($byFrame < 1.0) { // // How many steps into the sequence is $frame? // float $startFrame = getAttr("defaultRenderGlobals.startFrame"); float $framePos = ($frame - $startFrame) / $byFrame; int $iFramePos = floor($framePos + 0.5); // // Add that to the startFrame to get the effective frame number. // $frame = $startFrame + $iFramePos; } // // Round the frame number to the nearest integral value. // int $iFrame = floor($frame + 0.5); return ($prefix + "." + $iFrame + "." + $extension); } global proc shave_debug(string $msg) { // // We always display it to the Script Editor/render log. If we don't // want something going there then we should remove the call: all calls // have overhead so these calls should never be in production code anyway. // print($msg + "\n"); string $file = getenv("SHAVE_DEBUG_LOG"); if ($file != "") { // // There's no point in writing to the same log as the plugin: it // keeps the file open so it will just overwrite anything we // write to it. // // So we use a different name. If it is called 'debug.log' then // we'll used 'debug_mel.log'. // string $extension = match("[.][^.]*$", $file); int $extLen = size($extension); $file = substring($file, 1, size($file) - $extLen) + "_mel" + $extension; int $fd = fopen($file, "a"); if ($fd > 0) { fprint($fd, $msg + "\n"); fclose($fd); } } } global proc shave_assignRenderLayers() { string $shaveHairShapes[] = `ls -type shaveHair`; string $shaveHairShape; string $temp[]; for ($shaveHairShape in $shaveHairShapes) { string $displayNode = shave_getDisplayShape($shaveHairShape); string $renderLayer = ""; $temp = `listConnections -s yes -d no -type renderLayer ($displayNode + ".renderInfo")`; if (size($temp) > 0) $renderLayer = $temp[0]; string $growthObjects[] = `shaveNode -q -growthList $shaveHairShape`; // // Get rid of components and duplicates. // $growthObjects = `ls -o $growthObjects`; $growthObjects = stringArrayRemoveDuplicates($growthObjects); string $obj; string $objRenderLayer = ""; for ($obj in $growthObjects) { $temp = `listConnections -s yes -d no -type renderLayer ($obj + ".renderInfo")`; if (size($temp) > 0) $objRenderLayer = $temp[0]; if ($objRenderLayer == $renderLayer) break; } // // If the display node doesn't share a render layer with any of its // growth surfaces, then move it to the same render layer as the // last growth surface. // if ($objRenderLayer != $renderLayer) { if ($renderLayer != "") { disconnectAttr ($renderLayer + ".renderInfo") ($displayNode + ".renderInfo"); } if ($objRenderLayer != "") { connectAttr ($objRenderLayer + ".renderInfo") ($displayNode + ".renderInfo"); } } } } global proc shave_guidesToCurves() { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape == "") { error( "Shave: please select a shave node using the 'Shave Select' menu." ); } shaveNode -e -gtc $shaveHairShape; } global proc shave_hairsToCurves() { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape == "") { error( "Shave: please select a shave node using the 'Shave Select' menu." ); } shaveNode -e -htc $shaveHairShape; } global proc shave_hairsToPolys() { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape == "") { error( "Shave: please select a shave node using the 'Shave Select' menu." ); } string $polyGroup = `shaveNode -e -htp $shaveHairShape`; // // Assign the poly group to the same shading group as the shave node's // display shape. // string $displayShape = shave_getDisplayShape($shaveHairShape); string $shader = getShader($displayShape); if ($shader == "") sets -add initialShadingGroup $polyGroup; else sets -add $shader $polyGroup; } global proc shave_recomb() { string $shaveHairShape = shave_getCurrentNode(); if ($shaveHairShape == "") { error( "Shave: please select a shave node using the 'Shave Select' menu." ); } string $cmd = "shaveNode -e -recomb"; string $curve; string $curves[] = `ls -sl -l -dag -type nurbsCurve`; if (size($curves) == 0) { error( "Shave: please select one or more curves with which to comb" + " the hair." ); } for ($curve in $curves) $cmd += " -crv " + $curve; $cmd += " " + $shaveHairShape; eval($cmd); } global proc int shave_isIPRActive() { // // IPR doesn't work in batch mode. // if (`about -batch`) return false; string $renderer = shave_selectedRenderer(); switch ($renderer) { case "mayaSoftware": { string $proc = `renderer -q -isRunningIprProcedure $renderer`; return ($proc !="" && eval($proc)) ; } break; } return false; } global proc shave_doAEOverrides() { string $shaveScriptPath = getPathToProc("shaveAEOverrides"); // // If we didn't find our own script, then something's wrong. // if ($shaveScriptPath == "") error("Shave: cannot find shaveAEOverrides.mel."); string $existingScriptPath = getPathToProc("AElightCommonShadow2"); // // If we couldn't find AElightCommonShadow2 that's likely because Maya // hasn't sourced its script file yet. So fall back to its default // location, within Maya's AElightCommon script. // if ($existingScriptPath == "") { $existingScriptPath = getPathToProc("AElightCommon"); // // If we didn't find that, then something's wrong. // if ($existingScriptPath == "") error("Shave: cannot find Maya's AElightCommon.mel"); } // // If we're not already using our own script, then start doing so now. // if ($existingScriptPath != $shaveScriptPath) { // // Source Maya's first, to make sure that it doesn't get pulled in // after us, then source in our overrides. // eval("source \"" + $existingScriptPath + "\""); eval("source \"" + $shaveScriptPath + "\""); // // The AE may already be up and displaying a light, in which // case we need to force it to rebuild using our overrides. // string $layouts[] = `lsUI -cl`; string $layout; int $aeNeedsUpdating = false; for ($layout in $layouts) { // // If this is a light layout, then destroy it so that the next // time it is displayed it will be recreated with our // additional controls on it. // if (match("AttrEd.*LightFormLayout", $layout) != "") { deleteUI $layout; $aeNeedsUpdating = true; } } if ($aeNeedsUpdating) autoUpdateAttrEd(); } } global proc shave_AElight(string $node) { // // End the current layout and start a new one for the Shave params. // editorTemplate -endLayout; editorTemplate -beginLayout "Shave Shadows" -collapse true; { editorTemplate -label "Resolution" -addControl "shaveShadowResolution"; editorTemplate -label "Fuzz" -addControl "shaveShadowFuzz"; // editorTemplate -label "Trace" // -addControl "shaveShadowTrace"; } } global proc shave_addShadowParamsToLight(string $light) { // // If a light was passed in then use that, otherwise use any lights // which are on the selection list. // string $lights[]; if ($light != "") $lights[0] = $light; else { $lights = `ls -sl -dag -type light`; if (size($lights) == 0) error("Shave: no lights are selected."); } for ($light in $lights) { int $res = 450; if (nodeType($light) == "spotLight") $res = 600; addAttr -at short -ln shaveShadowResolution -dv $res -s true $light; addAttr -at "float" -ln shaveShadowFuzz -dv 8.0 -s true $light; // addAttr -at bool -ln shaveShadowTrace -dv 0 -s true $light; } } global proc shave_removeShadowParamsFromLight(string $light) { // // If a light was passed in then use that, otherwise use any lights // which are on the selection list. // string $lights[]; if ($light != "") $lights[0] = $light; else { $lights = `ls -sl -dag -type light`; if (size($lights) == 0) error("Shave: no lights are selected."); } for ($light in $lights) { if (attributeExists("shaveShadowResolution", $light)) deleteAttr -at shaveShadowResolution $light; if (attributeExists("shaveShadowFuzz", $light)) deleteAttr -at shaveShadowFuzz $light; // if (attributeExists("shaveShadowTrace", $light)) // deleteAttr -at shaveShadowTrace $light; } } global proc shave_rebuildShaveGlobalsAE() { string $form = "AttrEdshaveGlobalsFormLayout"; int $rebuildRequired = `formLayout -ex $form`; if ($rebuildRequired) { global string $gAttributeEditorWindowName; global string $gAEFocusNode; global string $gAETabLayoutName; string $curTab; string $focusNode; int $formIsVisible = false; if ((`window -exists $gAttributeEditorWindowName` && `window -q -vis $gAttributeEditorWindowName`) || isAttributeEditorVisible()) { string $tab; string $tabs[] = `tabLayout -q -tl $gAETabLayoutName`; int $curTabIndex = `tabLayout -q -sti $gAETabLayoutName`; $curTab = $tabs[$curTabIndex-1]; $focusNode = $gAEFocusNode; $formIsVisible = ($focusNode == "shaveGlobals"); if (!$formIsVisible) { for ($tab in $tabs) { if ($tab == "shaveGlobals") { $formIsVisible = true; break; } } } // // If we delete the shaveGlobals node's formLayout while it's // being displayed, Maya may crash, so clear the AE first. // if ($formIsVisible) updateAE(""); } // // Delete the form so that Maya will be forced to rebuild it // from scratch the next time it is displayed. // deleteUI $form; // // If we previously had to clear the AE, then restore it to the // focus node and tab that it was previously on. // if ($formIsVisible) { updateAE($focusNode); // // If the original tab still exists, then make it current // again. // int $i; $tabs = `tabLayout -q -tl $gAETabLayoutName`; for ($i = 0; $i < size($tabs); $i++) { if ($tabs[$i] == $curTab) { tabLayout -e -sti ($i+1) $gAETabLayoutName; break; } } } } } global proc shave_prepareForEditing() { shave_cancelLive(); shave_clearDynamics(""); } global proc shave_prepareForBrushing() { shave_prepareForEditing(); shaveUtil -convertComponentSelections; } // // In Shave 4.0 the 'shaveNode' was made obsolete and all of its // functionality moved into the 'shaveHair' node. That means that the // users old presets will no longer be in the correct directory (since it // contains the name of the node type) and will not work (because they // internally contain the old node type and refer to attrs with different // names). // // These next two scripts copy and modify those old presets to make them // available for use in Shave 4.0. // proc int convertOldPreset(string $oldFile, string $newFile) { int $success = false; int $in = fopen($oldFile, "r"); if ($in != 0) { int $out = fopen($newFile, "w"); if ($out != 0) { string $line; while (!feof($in)) { $line = fgetline($in); // // If this is the 'startAttrPreset' // command then change the node type // from 'shaveNode' to 'shaveHair'. // if (match("^startAttrPreset", $line) != "") $line = substitute("shaveNode", $line, "shaveHair"); // // The 'displayAs' attribute should // never have been saved in the first // place, so if it's in the preset, // skip it. // else if (match("\"displayAs\"", $line) != "") continue; fprint($out, $line); } fclose($out); $success = true; } fclose($in); } return $success; } global proc shave_checkOldPresets() { // // We need to do user interaction here, so skip it if we're in batch // mode. // if (`about -batch`) return; string $optVarName; global int $gShaveMayaVersion[]; $optVarName = "shaveOldPresetsNoAsk_" + $gShaveMayaVersion[0] + "_" + $gShaveMayaVersion[1]; // // If the optionVar exists, then either the presets have already been // copied or the user has asked that we stop asking about it. In // either case, we're done. // if (`optionVar -exists $optVarName`) return; // // Maya 7.0 introduced the '-userPresetsDir' flag to the 'internalVar' // command, however Maya's presets scripts all still locate the user's // presets dir using the '-userPrefDir' flag, so that's what we will do // as well. // string $presetsDir = `internalVar -userPrefDir`; $presetsDir = substitute("prefs", $presetsDir, "presets/attrPresets"); string $oldDir = $presetsDir + "shaveNode/"; string $newDir = $presetsDir + "shaveHair/"; // // Does the user have any old presets which are not already in the new // directory? // if (`filetest -d $oldDir` && `filetest -r $oldDir`) { int $foundSome = false; string $script; string $scripts[] = `getFileList -fld $oldDir -fs "*.mel"`; for ($script in $scripts) { if (!`filetest -r ($newDir + $script)`) { $foundSome = true; break; } } if ($foundSome) { // // We found some old presets, so let's find out what the user // wants to do about it. // int $canDeleteOld = `filetest -w $oldDir`; string $cmd = "confirmDialog -t \"Copy Shave 3.x Presets\""; $cmd += " -ma \"left\" -ds \"Later\" -m \"" + "Some personal presets have been found from Shave 3. If" + " you would\\n" + " like to use them in Shave 4 they will have to be copied." + " You can:\\n\\n"; if ($canDeleteOld) { $cmd += " - Copy them and delete the old ones\\n" + " - Copy them and keep the old ones\\n"; } else { $cmd += " - Copy them now\\n"; } $cmd += " - Do nothing now but be asked again later\\n" + " - Do nothing now and never be asked again\\n\\n" + "What would you like to do?\""; if ($canDeleteOld) $cmd += " -b \"Copy & Delete\" -b \"Copy & Keep\""; else $cmd += " -b \"Copy Now\""; $cmd += " -b \"Later\" -b \"Never\" -db \"Later\" -cb \"Later\""; string $ans = eval($cmd); if ($ans == "Later") return; if ($ans != "Never") { // // If the new directory doesn't exist, try to create it. // if (!`filetest -d $newDir`) { if (!`sysFile -makeDir $newDir`) { warning( "Shave: could not create preset directory '" + $newDir + "'." ); return; } } // // Is the new directory writable? // if (!`filetest -w $newDir`) { warning( "Shave: preset directory '" + $newDir + "' is not writable." ); return; } int $doDelete = false; if ($ans == "Copy & Delete") $doDelete = true; // // Copy the presets. // string $newScript; string $newSwatch; string $oldScript; string $oldSwatch; string $swatch; for ($script in $scripts) { int $haveCopy = false; $newScript = $newDir + $script; $oldScript = $oldDir + $script; $swatch = basename($script, ".mel") + "_P_swatch.xpm"; $newSwatch = $newDir + $swatch; $oldSwatch = $oldDir + $swatch; // // Only copy the preset if there isn't already one of // the same name in the new directory. // if (!`filetest -f $newScript`) { $haveCopy = convertOldPreset($oldScript, $newScript); // // Copy the swatch. // if ($haveCopy && `filetest -r $oldSwatch`) sysFile -copy $newSwatch $oldSwatch; } else $haveCopy = true; // // Delete the old preset and its swatch. // if ($doDelete && $haveCopy) { sysFile -del ($oldDir + $script); if (`filetest -f $oldSwatch`) sysFile -del $oldSwatch; } } } // // Set the optionVar so that we don't do this again. // optionVar -sv $optVarName "y"; } } } global proc shavePrefs() { if (!`optionVar -exists shave_fastBrush`) { optionVar -iv shave_fastBrush false; } } ////////////////////////////// global proc shave_prepVP2() { // Maya 2016 introduced different OpenGL profiles for Viewport 2.0. // Shave will only display hair in the non-core legacy profile. If VP2 // is not set to that then warn the user to change it and restart Maya. // int $mayaVersion = `about -api`; if ($mayaVersion >= 201600) { eval("source createPrefWndUI"); string $result[] = eval("getPreferredRenderingEngineAndOverrideFlag()"); string $curEngine = $result[0]; string $setInEnvVar = ($result[1] == "true"); string $desiredEngine = "OpenGL"; string $desiredEngineOverrideName = "VirtualDeviceGL"; if ($curEngine != $desiredEngine) { string $curEngineName = renderingEngine_LabelFromPreference($curEngine); string $desiredEngineName = renderingEngine_LabelFromPreference($desiredEngine); string $msg; if ($mayaVersion >= 20180000) { $msg = "The viewport rendering engine is set to '" + $curEngineName + "'. If you want to see Shave hair" + " in the viewport, ";; } else { $msg = "The Viewport 2.0 rendering engine is set to '" + $curEngineName + "'. If you want to see Shave hair" + " in Viewport 2.0, "; } if ($setInEnvVar) { $msg += "set the MAYA_VP2_DEVICE_OVERRIDE environment variable to '" + $desiredEngineOverrideName + "'. "; } else { $msg += "go into Windows->Settings->Preferences->Display and set the Viewport 2.0 rendering engine to '" + $desiredEngineName + "'. "; } $msg += "You will have to restart Maya for the change to take effect."; shave_popupWarning($msg, "shaveEnableVPRenderModeWarning"); } } } global proc shave_closeWarningDlg(string $var) { if(`checkBox -exists shaveWarningChkBox`) { int $doshow = `checkBox -query -value shaveWarningChkBox`; optionVar -intValue $var $doshow; } deleteUI -window shaveWarningDlg; } // Display $msg. If we're not in batch mode and the associated optionVar // is true, or doesn't exist, then the message will be displayed in a // popup window. Otherwise it will be displayed in the Script Editor. // global proc shave_popupWarning(string $msg, string $var) { int $doshow = 1; if(($var != "") && `optionVar -exists $var`) { $doshow = `optionVar -q $var`; } if (($doshow == 0) || `about -batch`) { warning $msg; return; } if(`window -exists shaveWarningDlg`) { deleteUI -window shaveWarningDlg; } window -title "Shave" -iconName "Shave" -resizeToFitChildren true -sizeable true -minimizeButton false -maximizeButton false shaveWarningDlg; columnLayout -adjustableColumn true -columnAttach "both" 20 -columnAlign "left"; { separator -style "none" -height 12; text -align left -wordWrap true -label $msg; separator -style "none" -height 12; checkBox -label "Show this message next time." -value $doshow shaveWarningChkBox; separator -style "none" -height 16; // To stop the button from being stretched across the entire width // of the columnLayout, we put it in its own, unadjustable layout. // columnLayout -adjustableColumn false; { button -label "OK" -width 80 -align "center" -command ("shave_closeWarningDlg \"" + $var + "\""); setParent ..; } separator -style "none" -height 12; setParent ..; } showWindow shaveWarningDlg; return; } global proc shave_showVP2lightWarning() { int $mayaVersion = `about -api`; string $msg; if ($mayaVersion >= 20180000) { $msg = "Shave does not support scene lights in the viewport render."; } else { $msg = "Scene lights are not supported by Shave in Viewport 2.0." + " Switch to the Legacy Viewport for proper illumination."; } shave_popupWarning($msg, "showVP2lightWarning"); } // Disconnects the incoming connection to $destPlug, if any. // // Returns true if a connection was broken. // global proc int shave_disconnectSrc(string $destPlug) { string $srcPlug = `connectionInfo -sourceFromDestination $destPlug`; if ($srcPlug == "") return false; disconnectAttr $srcPlug $destPlug; return true; } global proc shave_cleanupContexts(int $mayaExiting) { // If anyone tries to use one of our contexts after the plugin has // unloaded, Maya will crash. So let's make sure that none of our // contexts is current. // string $ctx = `currentCtx`; string $class = `contextInfo -class $ctx`; if (substring($class, 1, 5) == "shave") { ctxAbort $ctx; setToolTo selectSuperContext; } // Remove our context from the non-sacred tool button at the bottom // of Maya's toolbar (if it is there). // global string $gNonSacredToolWidget; global string $gNonSacredTool; if (`toolButton -exists $gNonSacredToolWidget`) { $ctx = `toolButton -q -t $gNonSacredToolWidget`; $class = `contextInfo -class $ctx`; if (substring($class, 1, 5) == "shave") { toolButton -e -t "selectSuperContext" -enable false -i1 "vacantCell.xpm" $gNonSacredToolWidget; $gNonSacredTool = ""; } } // Delete all of our contexts. // string $contexts[] = `lsUI -contexts`; string $context; for ($context in $contexts) { $class = `contextInfo -class $context`; if (substring($class, 1, 5) == "shave") deleteUI -toolContext $context; } // If the user was in brush mode then the current keyset will have been // switched from Shave's back to Maya's default. That is stored as part // of the user's preferences. However, if Maya is exiting then it will // have already saved preferences before we got here. We have no choice // but to save them again. // if ($mayaExiting) { savePrefs; } }