aboutsummaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
authorBen Marsh <[email protected]>2019-10-22 09:07:59 -0400
committerBen Marsh <[email protected]>2019-10-22 09:07:59 -0400
commitbd0027e737c6512397f841c22786274ed74b927f (patch)
treef7ffbdb8f3741bb7f24635616cc189cba5cb865c /scripts
downloadarchived-shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.tar.xz
archived-shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.zip
Adding Shave-and-a-Haircut 9.6
Diffstat (limited to 'scripts')
-rw-r--r--scripts/AEshaveGlobalsTemplate.mel924
-rw-r--r--scripts/AEshaveHairTemplate.mel876
-rw-r--r--scripts/AEshaveNodeTemplate.mel520
-rw-r--r--scripts/shaveAEOverrides.mel52
-rw-r--r--scripts/shaveBrush.mel422
-rw-r--r--scripts/shaveBrushProperties.mel21
-rw-r--r--scripts/shaveBrushSetModeButton.mel49
-rw-r--r--scripts/shaveBrushValues.mel19
-rw-r--r--scripts/shaveCheckVersion.mel173
-rw-r--r--scripts/shaveCursorCtxCommonProperties.mel90
-rw-r--r--scripts/shaveCursorCtxCommonValues.mel49
-rw-r--r--scripts/shaveCutProperties.mel12
-rw-r--r--scripts/shaveCutValues.mel12
-rw-r--r--scripts/shaveDiag.mel1266
-rw-r--r--scripts/shavePresetWin.mel456
-rw-r--r--scripts/shaveRelationshipEditor.mel1621
-rw-r--r--scripts/shaveRenderman.mel528
-rw-r--r--scripts/shaveRunTimeCommands.mel501
-rw-r--r--scripts/shaveShelf.mel352
-rw-r--r--scripts/shaveUI.mel8328
-rw-r--r--scripts/shaveVersion.mel10
-rw-r--r--scripts/shaveVrayPostRender.mel24
-rw-r--r--scripts/shaveVrayPreRender.mel29
23 files changed, 16334 insertions, 0 deletions
diff --git a/scripts/AEshaveGlobalsTemplate.mel b/scripts/AEshaveGlobalsTemplate.mel
new file mode 100644
index 0000000..5436c25
--- /dev/null
+++ b/scripts/AEshaveGlobalsTemplate.mel
@@ -0,0 +1,924 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $AEshaveGlobalsTemplate_fileVersion = "$Revision$";
+
+global int $shave_watchDoCompositingJobID = -1;
+global int $shave_watchDo2dCompositingJobID = -1;
+global int $shave_watchDoShadowsJobID = -1;
+global int $shave_watchUseGeomJobID = -1;
+
+
+global proc AEshaveGlobals_compositingAttrsChanged(string $node)
+{
+ int $compositingOn = getAttr($node + ".doCompositing");
+ int $do2d = getAttr($node + ".composite2d");
+ int $oldSelection = `optionMenuGrp -q -sl AEshaveGlobals_compositingFld`;
+ int $newSelection;
+
+ if (!$compositingOn)
+ $newSelection = 1;
+ else if ($do2d)
+ $newSelection = 2;
+ else
+ $newSelection = 3;
+
+ if ($newSelection != $oldSelection)
+ optionMenuGrp -e -sl $newSelection AEshaveGlobals_compositingFld;
+}
+
+
+global proc AEshaveGlobals_compositingChanged(string $node)
+{
+ int $option = `optionMenuGrp -q -sl AEshaveGlobals_compositingFld`;
+ int $oldCompositing = getAttr($node + ".doCompositing");
+ int $old2d = getAttr($node + ".composite2d");
+ int $newCompositing = $oldCompositing;
+ int $new2d = $old2d;
+
+ switch ($option)
+ {
+ case 1:
+ $newCompositing = false;
+ break;
+
+ case 2:
+ $newCompositing = true;
+ $new2d = true;
+ break;
+
+ case 3:
+ $newCompositing = true;
+ $new2d = false;
+ break;
+ }
+
+ if ($newCompositing != $oldCompositing)
+ {
+ setAttr ($node + ".doCompositing") $newCompositing;
+
+ //
+ // If doCompositing has been turned on, turn shadowMatte off.
+ //
+ if ($newCompositing && getAttr($node + ".shadowMatte"))
+ {
+ setAttr ($node + ".shadowMatte") false;
+ }
+ }
+
+ if ($new2d != $old2d) setAttr ($node + ".composite2d") $new2d;
+
+ AEshaveGlobalsDimControls($node);
+}
+
+
+global proc AEshaveGlobals_compositingNew(string $compositingAttr, string $do2dAttr)
+{
+ global int $shave_watchDoCompositingJobID;
+ global int $shave_watchDo2dCompositingJobID;
+
+ string $tmp[];
+
+ tokenize($compositingAttr, ".", $tmp);
+
+ string $node = $tmp[0];
+
+ //
+ // Rather than a confusing pair of checkboxes, let's give the user a
+ // pull-down menu with sensible names for the valid settings.
+ //
+ optionMenuGrp -l "Compositing"
+ -cc ("AEshaveGlobals_compositingChanged \"" + $node + "\"")
+ "AEshaveGlobals_compositingFld";
+
+ menuItem -l "Off";
+ menuItem -l "2D Post";
+ menuItem -l "3D Volumetric" AEshaveGlobals_comp3dMenuItem;
+ setParent ..;
+
+ $shave_watchDoCompositingJobID = -1;
+ $shave_watchDo2dCompositingJobID = -1;
+
+ AEshaveGlobals_compositingReplace($compositingAttr, $do2dAttr);
+}
+
+
+global proc AEshaveGlobals_compositingReplace(
+ string $compositingAttr, string $do2dAttr
+)
+{
+ global int $shave_watchDoCompositingJobID;
+ global int $shave_watchDo2dCompositingJobID;
+
+ string $tmp[];
+
+ tokenize($compositingAttr, ".", $tmp);
+
+ string $node = $tmp[0];
+
+ shave_killJob($shave_watchDoCompositingJobID);
+ shave_killJob($shave_watchDo2dCompositingJobID);
+
+ $shave_watchDoCompositingJobID = `scriptJob -ac $compositingAttr
+ ("AEshaveGlobals_compositingAttrsChanged \"" + $node + "\"")`;
+
+ $shave_watchDo2dCompositingJobID = `scriptJob -ac $do2dAttr
+ ("AEshaveGlobals_compositingAttrsChanged \"" + $node + "\"")`;
+
+ AEshaveGlobals_compositingAttrsChanged($node);
+}
+
+
+global proc AEshaveGlobals_fastBrushNew()
+{
+ global string $gShaveGlobals_fastBrushCB;
+
+ $gShaveGlobals_fastBrushCB = `checkBoxGrp -ncb 1 -l1 "Fast Brush"`;
+ checkBoxGrp -e -cc1 "AEshaveGlobals_fastBrushChanged"
+ $gShaveGlobals_fastBrushCB;
+
+ AEshaveGlobals_fastBrushReplace();
+}
+
+
+global proc AEshaveGlobals_fastBrushReplace()
+{
+ global string $gShaveGlobals_fastBrushCB;
+
+ int $fastBrush = `optionVar -q shave_fastBrush`;
+ checkBoxGrp -e -v1 $fastBrush $gShaveGlobals_fastBrushCB;
+ if(`about -macOS` && `about -is64`)
+ {
+ checkBoxGrp -e -enable false $gShaveGlobals_fastBrushCB;
+ checkBoxGrp -e -v1 false $gShaveGlobals_fastBrushCB;
+ optionVar -iv shave_fastBrush false;
+ }
+ else
+ checkBoxGrp -e -enable true $gShaveGlobals_fastBrushCB;
+}
+
+
+global proc AEshaveGlobals_fastBrushChanged()
+{
+ global string $gShaveGlobals_fastBrushCB;
+
+ int $fastBrush = `checkBoxGrp -q -v1 $gShaveGlobals_fastBrushCB`;
+
+ optionVar -iv shave_fastBrush $fastBrush;
+
+ string $ctx = currentCtx();
+
+ if (($ctx != "") && (`contextInfo -c $ctx` == "shaveBrush"))
+ {
+ // The tool won't pick up the brush change until it has restarted,
+ // so let's force a restart.
+ ctxAbort();
+ setToolTo $ctx;
+ }
+}
+
+// The float slider group provided by 'editorTemplate -addControl' always
+// has 3 digits of precision to the right of the decimal place. This proc
+// is used to create a group which can have a different number of digits of
+// precision.
+//
+global proc AEshaveGlobals_floatPrecNew(string $label, int $prec, string $attr)
+{
+ // This method gets used for several different fields. We use the
+ // attribute name (without the node name) to name the field.
+ //
+ string $tmp[];
+ tokenize($attr, ".", $tmp);
+ string $fieldName = "AEshaveGlobals_" + $tmp[1] + "Fld";
+
+ attrFieldSliderGrp -l $label -pre $prec -at $attr $fieldName;
+}
+
+
+global proc AEshaveGlobals_floatPrecReplace(string $attr)
+{
+ // This method gets used for several different fields. We use the
+ // attribute name (without the node name) to name the field.
+ //
+ string $tmp[];
+ tokenize($attr, ".", $tmp);
+ string $fieldName = "AEshaveGlobals_" + $tmp[1] + "Fld";
+
+ attrFieldSliderGrp -e -at $attr $fieldName;
+}
+
+
+global proc AEshaveGlobals_renderModeNew(string $attr)
+{
+ textFieldGrp -l "Renderer" -ed false AEshaveGlobals_rendererFld;
+
+ optionMenuGrp -l "Hair Render Mode" AEshaveGlobals_renderModeMenu;
+ menuItem -l "Buffer" -data 0;
+ menuItem -l "Geometry" -data 1;
+ menuItem -l "None" -data 3;
+
+ AEshaveGlobals_renderModeReplace($attr);
+}
+
+
+global proc AEshaveGlobals_renderModeReplace(string $attr)
+{
+ string $renderer = shave_selectedRenderer();
+ string $tmp[];
+
+ textFieldGrp -e -tx $renderer AEshaveGlobals_rendererFld;
+
+ tokenize($attr, ".", $tmp);
+
+ string $node = $tmp[0];
+
+ connectControl -in 2 AEshaveGlobals_renderModeMenu $attr;
+}
+
+
+global proc AEshaveGlobals_rendermanReset()
+{
+ string $numericAttrs[] = {
+ "ribBinary",
+ "ribBlurEnable",
+ "ribBlurInheritSettings",
+ "ribBlurRestoreFrame",
+ "ribBlurShutterCloseOffset",
+ "ribBlurShutterOpenOffset",
+ "ribCompress",
+ "ribNormals",
+ "ribOpacities",
+ "hairPrimitiveType",
+ "ribRootTipColors",
+ "ribBlurTimeBasis",
+ "ribTimeUnits",
+ "ribUVs",
+ "ribVertexColors",
+ "ribVoxelEnable",
+ "ribVoxelFullPaths",
+ "ribVoxelResolution",
+ "ribWCoords"
+ };
+ string $stringAttrs[] = {
+ "ribStuff",
+ "ribUVSet"
+ };
+ string $attr;
+
+ for ($attr in $numericAttrs)
+ {
+ float $default[] = `attributeQuery -typ shaveGlobals -ld $attr`;
+ setAttr ("shaveGlobals." + $attr) $default[0];
+ }
+
+ // We cannot query the default value of a string attribute, but
+ // currently they all default to empty.
+ for ($attr in $stringAttrs)
+ setAttr -type "string" ("shaveGlobals." + $attr) "";
+}
+
+
+global proc AEshaveGlobals_rendermanResetNew()
+{
+ rowLayout -nc 1 -cat 1 "left" 100;
+ button -l "Reset To Defaults" -c "AEshaveGlobals_rendermanReset"
+ -w 200
+ AEshaveGlobals_rendermanResetButton;
+ setParent ..;
+}
+
+global proc AEshaveGlobals_rendermanResetReplace()
+{
+ // The Reset button doesn't do anything node-specific, so there's
+ // nothing to do in this func, which is only called when the node
+ // changes.
+}
+
+
+global proc AEshaveGlobalsDimControls(string $nodeName)
+{
+ int $haveNormalHair = false;
+ int $haveInstancedHair = false;
+ string $renderer = shave_selectedRenderer();
+ string $shaveHairs[] = `ls -type shaveHair`;
+ string $shaveHair;
+
+ for ($shaveHair in $shaveHairs)
+ {
+ if (getAttr($shaveHair + ".instancingStatus"))
+ $haveInstancedHair = true;
+ else
+ $haveNormalHair = true;
+ }
+
+ //
+ // The 'Render Mode' pulldown only applies to normal hair, so if there
+ // is no normal hair then disable it.
+ //
+ if (`optionMenuGrp -exists "AEshaveGlobals_renderModeMenu"`)
+ optionMenuGrp -e -enable $haveNormalHair AEshaveGlobals_renderModeMenu;
+
+ editorTemplate -dimControl $nodeName "enableInstanceGeometry"
+ (!$haveInstancedHair);
+
+ if (getAttr($nodeName + ".keepHairPassPics"))
+ editorTemplate -dimControl $nodeName "hairFilenamePrefix" false;
+ else
+ editorTemplate -dimControl $nodeName "hairFilenamePrefix" true;
+
+ if (`optionMenuGrp -exists "AEshaveGlobals_compositingFld"`)
+ optionMenuGrp -e -enable $haveNormalHair AEshaveGlobals_compositingFld;
+
+ //
+ // Shadows
+ //
+ int $shadowsOff = true;
+ int $geomShadowsOff = true;
+
+ if (getAttr($nodeName + ".doHairShadows") == 1)
+ {
+ $shadowsOff = false;
+
+ if (getAttr($nodeName + ".useGeomForShadows") == 1)
+ $geomShadowsOff = false;
+ }
+
+ editorTemplate -dimControl $nodeName "shadowDensity" $shadowsOff;
+ editorTemplate -dimControl $nodeName "useAllLights" $shadowsOff;
+
+ //
+ // 3D compositing doesn't work with native illumination. If shadows
+ // are off then keep the 3D and turn off the native illum. If shadows
+ // are on then keep the native illum and switch to 2D.
+ //
+ int $nativeIllum = getAttr($nodeName + ".nativeIllumination");
+ int $compositing = getAttr($nodeName + ".doCompositing");
+ int $comp2d = getAttr($nodeName + ".composite2d");
+ int $comp3d = ($compositing && !$comp2d);
+
+ if ($comp3d && $nativeIllum)
+ {
+ if ($shadowsOff)
+ {
+ setAttr ($nodeName + ".nativeIllumination") false;
+ $nativeIllum = false;
+ }
+ else
+ {
+ setAttr ($nodeName + ".composite2d") true;
+ $comp3d = false;
+ }
+ }
+
+ //
+ // If native illumination is on then dim the 3D compositing menu
+ // item.
+ //
+ if (`menuItem -exists "AEshaveGlobals_comp3dMenuItem"`)
+ menuItem -e -enable (!$nativeIllum) AEshaveGlobals_comp3dMenuItem;
+
+ //
+ // If 3D compositing is on then dim the nativeIllumination control,
+ // otherwise dim it according to whether shadows are enabled.
+ //
+ editorTemplate -dimControl $nodeName "nativeIllumination"
+ ($comp3d || $shadowsOff);
+
+ //
+ // If threadPerProcessor is on then maxThreads is ignored, so dim it.
+ //
+ int $threadPerProc = getAttr($nodeName + ".threadPerProcessor");
+ editorTemplate -dimControl $nodeName "maxThreads" $threadPerProc;
+
+ // If ribUVs is on off then disable the UV set field.
+ int $disable = !getAttr($nodeName + ".ribUVs");
+ editorTemplate -dimControl $nodeName "ribUVSet" $disable;
+
+ // If we're inheriting settings from Maya or Renderman then get the
+ // blur enable from there. Otherwise take it from our own control.
+ //
+ // Now we should really disable all of the blur controls if blur is
+ // disabled, but that would involve callbacks on both
+ // defaultRenderGlobals and renderManGlobals. Since renderManGlobals
+ // won't necessarily exist yet, that in turn means a callback on node
+ // creation. That's all way too much work so we just leave the
+ // fields enabled all the time.
+ //
+ // However, there are a couple of fields which should only be enabled
+ // if we're not inheriting settings, so we can still do those.
+ int $inherit = getAttr($nodeName + ".ribBlurInheritSettings");
+
+ $disable = ($inherit != 0);
+ editorTemplate -dimControl $nodeName "ribBlurEnable" $disable;
+ editorTemplate -dimControl $nodeName "ribBlurShutterOpenOffset" $disable;
+ editorTemplate -dimControl $nodeName "ribBlurShutterCloseOffset" $disable;
+
+ // If ribVoxelEnable is off then disable the other rib voxel fields.
+ $disable = !getAttr($nodeName + ".ribVoxelEnable");
+ editorTemplate -dimControl $nodeName "ribVoxelFullPaths" $disable;
+ editorTemplate -dimControl $nodeName "ribVoxelResolution" $disable;
+}
+
+
+global proc AEshaveGlobalsShadowMatteChange(string $nodeName)
+{
+ //
+ // If shadowMatte has been turned on, turn doCompositing off.
+ //
+ int $matteOn = getAttr($nodeName + ".shadowMatte");
+
+ if (getAttr($nodeName + ".doCompositing") && $matteOn)
+ {
+ setAttr ($nodeName + ".doCompositing") false;
+ }
+
+ editorTemplate -dimControl $nodeName "shadowMatteIncludeBackfacing" (!$matteOn);
+}
+
+
+global proc AEshaveGlobals_statFileDirBrowse(string $nodeAndAttr)
+{
+ global string $gDefaultFileBrowserDir;
+
+ string $dir = getAttr($nodeAndAttr);
+
+ if (!`filetest -d $dir`) $dir = dirname($dir);
+
+ if ($dir != "") $gDefaultFileBrowserDir = $dir;
+
+ fileBrowser "AEshaveGlobals_statFileDirBrowseDone" "Set Stat Dir" "" 4;
+}
+
+
+global proc AEshaveGlobals_statFileDirBrowseDone(string $dir, string $type)
+{
+ if (!`filetest -d $dir`)
+ {
+ //
+ // Either the directory doesn't exist or it exists but is not a
+ // directory.
+ //
+ // If it's the former then prompt for creation. If the latter then
+ // give an error.
+ //
+ if (!`filetest -f $dir`)
+ {
+ string $answer = `confirmDialog -title "Directory Does Not Exist"
+ -m ( "The directory '" + $dir + "' does not exist.\n\n"
+ + "Do you want to create it?")
+ -b "Yes" -b "No" -db "Yes" -cb "No" -ds "No"`;
+
+ if ($answer == "Yes")
+ {
+ if (!`sysFile -makeDir $dir`)
+ {
+ confirmDialog -title "Cannot Create Directory"
+ -message ("The directory '" + $dir + "'\n"
+ + "could not be created. Please check your access\n"
+ + "privileges or specify a different directory.");
+
+ return;
+ }
+ }
+ }
+ else
+ {
+ confirmDialog -title "Invalid Directory"
+ -m ("'" + $dir + "' is not a directory.");
+ return;
+ }
+ }
+
+ setAttr -type "string" shaveGlobals.tmpDir $dir;
+
+ if (`window -ex projectViewerWindow`)
+ window -e -vis false projectViewerWindow;
+}
+
+
+global proc AEshaveGlobals_statDirNew(string $nodeAndAttr)
+{
+ textFieldButtonGrp -l "Stat File Directory"
+ -bl "Browse..." -bc "AEshaveGlobals_statFileDirBrowse" -cw 3 70
+ "AEshaveGlobals_statDirField";
+
+ AEshaveGlobals_statDirReplace($nodeAndAttr);
+}
+
+
+global proc AEshaveGlobals_statDirReplace(string $nodeAndAttr)
+{
+ textFieldButtonGrp -e
+ -bc ("AEshaveGlobals_statFileDirBrowse \"" + $nodeAndAttr + "\"")
+ "AEshaveGlobals_statDirField";
+
+ connectControl -index 2 "AEshaveGlobals_statDirField" $nodeAndAttr;
+}
+
+
+global proc AEshaveGlobals_tileMemoryLimitNew(string $nodeAndAttr)
+{
+ intFieldGrp -l "Tile Memory Limit" -extraLabel "MB" -cal 3 "left"
+ AEshaveGlobals_tileMemoryLimitField;
+
+ AEshaveGlobals_tileMemoryLimitReplace($nodeAndAttr);
+}
+
+
+global proc AEshaveGlobals_tileMemoryLimitReplace(string $nodeAndAttr)
+{
+ connectControl -index 2 AEshaveGlobals_tileMemoryLimitField $nodeAndAttr;
+}
+
+
+global proc AEshaveGlobalsTemplate(string $nodeName)
+{
+ string $renderer = shave_selectedRenderer();
+ int $mode = getAttr($nodeName + ".renderMode");
+ int $mayaVersion[] = shave_getMayaVersion();
+
+ editorTemplate -beginScrollLayout;
+ editorTemplate -callCustom "AEshaveGlobals_renderModeNew"
+ "AEshaveGlobals_renderModeReplace" "renderMode";
+
+ editorTemplate -beginLayout "General" -collapse false;
+ editorTemplate -addControl "enableInstanceGeometry";
+ editorTemplate -addControl "hideHair";
+ editorTemplate -interruptOptimize;
+ editorTemplate -addControl "voxelResolution";
+ editorTemplate -addControl "threadPerProcessor"
+ "AEshaveGlobalsDimControls";
+ editorTemplate -addControl "maxThreads";
+ editorTemplate -callCustom "AEshaveGlobals_tileMemoryLimitNew"
+ "AEshaveGlobals_tileMemoryLimitReplace" "tileMemoryLimit";
+ editorTemplate -addControl "transparencyDepth";
+ editorTemplate -addControl "verbose";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Display" -collapse true;
+ editorTemplate -addControl "displayGuides";
+ // editorTemplate -l "Display Hair As" -addControl "displayAs";
+ editorTemplate -callCustom
+ "AEshaveGlobals_floatPrecNew \"Preview %\" 2"
+ "AEshaveGlobals_floatPrecReplace" "displayHairRatio";
+ editorTemplate -callCustom
+ "AEshaveGlobals_floatPrecNew \"Interactive %\" 2"
+ "AEshaveGlobals_floatPrecReplace" "displayFallbackRatio";
+ editorTemplate -callCustom
+ "AEshaveGlobals_floatPrecNew \"Segments %\" 2"
+ "AEshaveGlobals_floatPrecReplace" "displaySegmentLimit";
+ editorTemplate -l "Guide Thickness" -addControl "displayGuideThick";
+ editorTemplate -l "Use Transparency" -addControl "displayHairTransprency";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Primitives Visibility" -collapse ($mode == 0);
+ editorTemplate -beginNoOptimize;
+ editorTemplate -label "Camera" -addControl "primCameraVis";
+ editorTemplate -label "Lights" -addControl "primLightVis";
+ editorTemplate -label "GI" -addControl "primGiVis";
+ editorTemplate -endNoOptimize;
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Buffer Mode" -collapse ($mode != 0);
+ editorTemplate -addControl "renderQuality";
+
+ editorTemplate -callCustom "AEshaveGlobals_compositingNew"
+ "AEshaveGlobals_compositingReplace" "doCompositing" "composite2d";
+
+ editorTemplate -label "Occlusion Objects"
+ -addControl "hairOcclusionObjects";
+
+ editorTemplate
+ -addControl "shadowMatte" "AEshaveGlobalsShadowMatteChange";
+
+ editorTemplate -l "Include Backfacing Polys"
+ -addControl "shadowMatteIncludeBackfacing";
+
+ editorTemplate -interruptOptimize;
+
+ editorTemplate -l "Visible In Reflections/Refractions"
+ -addControl "visibleInReflections";
+
+ editorTemplate -addSeparator;
+//
+// Geom Shadows are currently semi-deprecated, meaning that
+// we've hidden the functionality but are keeping it around
+// just-in-case.
+//
+// editorTemplate -callCustom "AEshaveGlobals_shadowsNew"
+// "AEshaveGlobals_shadowsReplace" "doHairShadows" "useGeomForShadows";
+
+ editorTemplate -addControl "doHairShadows"
+ "AEshaveGlobalsDimControls";
+
+ editorTemplate -label "Cast Shadow Density"
+ -addControl "shadowDensity";
+
+ editorTemplate -addControl "nativeIllumination"
+ "AEshaveGlobalsDimControls";
+
+ editorTemplate -interruptOptimize;
+
+ editorTemplate -addControl "useAllLights";
+
+ editorTemplate -addSeparator;
+
+ editorTemplate -label "Output Hair Images"
+ -addControl "keepHairPassPics" "AEshaveGlobalsDimControls";
+
+ editorTemplate -label "Image File Prefix"
+ -addControl "hairFilenamePrefix";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Dynamics" -collapse true;
+ editorTemplate -addControl "gravity";
+
+ editorTemplate -callCustom "AEshaveGlobals_statDirNew"
+ "AEshaveGlobals_statDirReplace" "tmpDir";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Instances" -collapse true;
+ editorTemplate -label "GL/Cg Instances" -addControl "glInstances" ;
+ //editorTemplate -callCustom "glInstancesNew" "glInstancesEdit" "glInstances" /*$nodeName*/;
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Renderman" -collapse true;
+ editorTemplate -addControl "hairPrimitiveType";
+ editorTemplate -label "Global RIB Text" -addControl "ribStuff";
+
+ editorTemplate -label "Keep RIB Files" -addControl "ribKeepRibFiles";
+ editorTemplate -interruptOptimize;
+ editorTemplate -label "Export Root/Tip Colors" -addControl "ribRootTipColors";
+ editorTemplate -label "Export Vert Colors" -addControl "ribVertexColors";
+
+ editorTemplate -label "Export Normals" -addControl "ribNormals";
+ editorTemplate -label "Export Opacity" -addControl "ribOpacities";
+
+ editorTemplate -label "Export W Coords" -addControl "ribWCoords";
+ editorTemplate -label "Export Root Positions" -addControl "ribRootPositions";
+
+ editorTemplate -label "Export UVs" -addControl "ribUVs"
+ "AEshaveGlobalsDimControls";
+ editorTemplate -label "UV Set" -addControl "ribUVSet";
+ editorTemplate -label "Export Time As" -addControl "ribTimeUnits";
+
+ editorTemplate -label "Binary" -addControl "ribBinary";
+ editorTemplate -label "Compress" -addControl "ribCompress";
+
+ editorTemplate -addSeparator;
+
+ editorTemplate -label "Inherit Settings"
+ -addControl "ribBlurInheritSettings"
+ "AEshaveGlobalsDimControls";
+ editorTemplate -label "Enable Motion Blur"
+ -addControl "ribBlurEnable"
+ "AEshaveGlobalsDimControls";
+ editorTemplate -label "Shutter Times Are" -addControl "ribBlurTimeBasis";
+ editorTemplate -label "Shutter Open Offset" -addControl "ribBlurShutterOpenOffset";
+ editorTemplate -label "Shutter Close Offset" -addControl "ribBlurShutterCloseOffset";
+ editorTemplate -label "Restore Frame" -addControl "ribBlurRestoreFrame";
+
+ editorTemplate -addSeparator;
+
+ editorTemplate -label "Enable Voxels" -addControl "ribVoxelEnable"
+ "AEshaveGlobalsDimControls";
+ editorTemplate -label "Use Full Paths" -addControl "ribVoxelFullPaths";
+ editorTemplate -label "Resolution" -addControl "ribVoxelResolution";
+
+ editorTemplate -addSeparator;
+
+ editorTemplate -callCustom "AEshaveGlobals_rendermanResetNew"
+ "AEshaveGlobals_rendermanResetReplace";
+
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Styling" -collapse true;
+ editorTemplate -callCustom "AEshaveGlobals_fastBrushNew"
+ "AEshaveGlobals_fastBrushReplace";
+ editorTemplate -endLayout;
+
+ //
+ // Handle the standard node attributes.
+ //
+ AEdependNodeTemplate $nodeName;
+
+ //
+ // Display any dynamically added attributes.
+ //
+ editorTemplate -addExtraControls;
+
+ //
+ // We're handling these through custom controls, so let's not see
+ // them again.
+ //
+ editorTemplate -suppress "composite2d";
+ editorTemplate -suppress "displayLod";
+ editorTemplate -suppress "doCompositing";
+// editorTemplate -suppress "doHairShadows";
+ editorTemplate -suppress "renderMode";
+ editorTemplate -suppress "tmpDir";
+ editorTemplate -suppress "useGeomForShadows";
+
+ //
+ // We don't want to see these.
+ //
+ editorTemplate -suppress "caching";
+ editorTemplate -suppress "displayHairSsao";
+ editorTemplate -suppress "instanceRenderMode";
+ editorTemplate -suppress "nodeState";
+ editorTemplate -suppress "shadowHairRatio";
+ editorTemplate -suppress "shadowQuality";
+ editorTemplate -suppress "shadowResolution";
+ editorTemplate -suppress "shaveLightList";
+ editorTemplate -suppress "shaveShadFuzz";
+
+ editorTemplate -endScrollLayout;
+
+ AEshaveGlobalsDimControls("shaveGlobals");
+}
+
+global proc forceUpdate(string $nodeName)
+{
+ currentTime `currentTime -q`;
+}
+
+
+global proc AEshaveGlobals_cleanup()
+{
+ global int $shave_watchDoCompositingJobID;
+ global int $shave_watchDo2dCompositingJobID;
+ global int $shave_watchDoShadowsJobID;
+ global int $shave_watchUseGeomJobID;
+
+ shave_killJob($shave_watchDoCompositingJobID);
+ shave_killJob($shave_watchDo2dCompositingJobID);
+ shave_killJob($shave_watchDoShadowsJobID);
+ shave_killJob($shave_watchUseGeomJobID);
+}
+/*
+global proc glInstancesNew(string $node)
+{
+ print $node; print "\n";
+
+ //$use = getAttr($node + ".glInstances");
+ $use = getAttr($node);
+ checkBoxGrp
+ -numberOfCheckBoxes 1
+ -changeCommand1 ("shaveInstModeChanged " + $node)
+ -label "Shaded Instances"
+ -value1 $use
+ glInstnacesCbx;
+}
+
+global proc glInstancesEdit(string $node)
+{
+ if(`checkBoxGrp -exists glInstnacesCbx`)
+ {
+ //$use = getAttr($node + ".glInstances");
+ $use = getAttr($node);
+ checkBoxGrp -e
+ -v1 $use
+ //-changeCommand1 ("shaveInstModeChanged " + $node)
+ glInstnacesCbx;
+ }
+}
+
+global proc shaveInstModeChanged(string $node)
+{
+ if(`checkBoxGrp -exists glInstnacesCbx`)
+ {
+ $use = `checkBoxGrp -q -v1 glInstnacesCbx`;
+ //setAttr ($node + ".glInstances") $use;
+ setAttr $node $use;
+
+ string $shaveHairShapes[];
+ $shaveHairShapes = `ls -type shaveHair`;
+
+ if (size($shaveHairShapes) > 0)
+ {
+ for ($shaveHairShape in $shaveHairShapes)
+ getAttr ($shaveHairShape + ".trigger");
+ }
+ }
+}
+*/
+//---------------------------------------------------------------------
+//
+// Deprecated Procedures
+//
+// These procedure support deprecated functions. We keep them around
+// for a little while just in case we change our minds.
+//
+//---------------------------------------------------------------------
+
+global proc AEshaveGlobals_shadowAttrsChanged(string $node)
+{
+ if (`optionMenuGrp -exists "AEshaveGlobals_shadowsFld"`)
+ {
+ int $shadowsOn = getAttr($node + ".doHairShadows");
+ int $useGeom = getAttr($node + ".useGeomForShadows");
+ int $oldSelection = `optionMenuGrp -q -sl AEshaveGlobals_shadowsFld`;
+ int $newSelection;
+
+ if (!$shadowsOn)
+ $newSelection = 1;
+ else if ($useGeom)
+ $newSelection = 3;
+ else
+ $newSelection = 2;
+
+ if ($newSelection != $oldSelection)
+ optionMenuGrp -e -sl $newSelection AEshaveGlobals_shadowsFld;
+ }
+}
+
+
+global proc AEshaveGlobals_shadowsChanged(string $node)
+{
+ int $option = `optionMenuGrp -q -sl AEshaveGlobals_shadowsFld`;
+ int $oldDoShadows = getAttr($node + ".doHairShadows");
+ int $oldUseGeom = getAttr($node + ".useGeomForShadows");
+ int $newDoShadows = $oldDoShadows;
+ int $newUseGeom = $oldUseGeom;
+
+ switch ($option)
+ {
+ case 1:
+ $newDoShadows = false;
+ break;
+
+ case 2:
+ $newDoShadows = true;
+ $newUseGeom = false;
+ break;
+
+ case 3:
+ $newDoShadows = true;
+ $newUseGeom = true;
+ break;
+ }
+
+ if ($newDoShadows != $oldDoShadows)
+ setAttr ($node + ".doHairShadows") $newDoShadows;
+
+ if ($newUseGeom != $oldUseGeom)
+ setAttr ($node + ".useGeomForShadows") $newUseGeom;
+
+ AEshaveGlobalsDimControls($node);
+}
+
+
+global proc AEshaveGlobals_shadowsNew(string $doShadowsAttr, string $useGeomAttr)
+{
+ global int $shave_watchDoShadowsJobID;
+ global int $shave_watchUseGeomJobID;
+
+ string $tmp[];
+
+ tokenize($doShadowsAttr, ".", $tmp);
+
+ string $node = $tmp[0];
+
+ //
+ // Rather than a confusing pair of checkboxes, let's give the user a
+ // pull-down menu with sensible names for the valid settings.
+ //
+ optionMenuGrp -l "Shadows"
+ -cc ("AEshaveGlobals_shadowsChanged \"" + $node + "\"")
+ "AEshaveGlobals_shadowsFld";
+
+ menuItem -l "Off";
+ menuItem -l "Use Buffer" AEshaveGlobals_useBufferShadowsMenuItem;
+ menuItem -l "Use Geometry";
+ setParent ..;
+
+ $shave_watchDoShadowsJobID = -1;
+ $shave_watchUseGeomJobID = -1;
+
+ AEshaveGlobals_shadowsReplace($doShadowsAttr, $useGeomAttr);
+}
+
+
+global proc AEshaveGlobals_shadowsReplace(string $doShadowsAttr, string $useGeomAttr)
+{
+ global int $shave_watchDoShadowsJobID;
+ global int $shave_watchUseGeomJobID;
+
+ string $tmp[];
+
+ tokenize($doShadowsAttr, ".", $tmp);
+
+ string $node = $tmp[0];
+
+ shave_killJob($shave_watchDoShadowsJobID);
+ shave_killJob($shave_watchUseGeomJobID);
+
+ $shave_watchDoShadowsJobID = `scriptJob -ac $doShadowsAttr
+ ("AEshaveGlobals_shadowAttrsChanged \"" + $node + "\"")`;
+
+ $shave_watchUseGeomJobID = `scriptJob -ac $useGeomAttr
+ ("AEshaveGlobals_shadowAttrsChanged \"" + $node + "\"")`;
+
+ AEshaveGlobals_shadowAttrsChanged($node);
+}
diff --git a/scripts/AEshaveHairTemplate.mel b/scripts/AEshaveHairTemplate.mel
new file mode 100644
index 0000000..6e9b39d
--- /dev/null
+++ b/scripts/AEshaveHairTemplate.mel
@@ -0,0 +1,876 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $AEshaveHairTemplate_fileVersion = "$Revision$";
+
+
+global proc AEshaveHairDimControls(string $node)
+{
+ int $isInstanced = getAttr($node + ".instancingStatus");
+
+ if ($isInstanced)
+ editorTemplate -dimControl $node "overrideGeomShader" true;
+ else
+ editorTemplate -dimControl $node "overrideGeomShader" false;
+}
+
+proc int is3DelightForMayaLoaded()
+{
+ string $plugins[] = `pluginInfo -q -ls`;
+ for ($curr_plugin in $plugins)
+ {
+ if(match("3delight_for_maya", $curr_plugin) != "")
+ return 1;
+ }
+
+ return 0;
+}
+
+global proc AEshaveHairCheckDoubleSided(string $node)
+{
+ string $node_attr = $node + ".doubleSided";
+
+ if (objExists($node_attr))
+ {
+ int $value = `getAttr $node_attr`;
+
+ if (getAttr($node_attr) == 1)
+ editorTemplate -dimControl $node "opposite" true;
+ else
+ editorTemplate -dimControl $node "opposite" false;
+ }
+}
+
+
+
+global proc AEshaveHairTemplate( string $nodeName )
+{
+ // Put our attributes into a scrolled layout field
+ editorTemplate -beginScrollLayout;
+
+ editorTemplate -beginLayout "General Properties" -collapse false;
+ editorTemplate -addControl "hairCount";
+ editorTemplate -addControl "hairSegments";
+ editorTemplate -addControl "hairPasses";
+
+ editorTemplate -callCustom "AEshaveCutTexParmNew"
+ "AEshaveCutTexParmReplace" "shaveTex[29]";
+
+ editorTemplate -callCustom "AEshaveDensityTexParmNew"
+ "AEshaveDensityTexParmReplace" "shaveTex[28]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "scale";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[41]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "randScale";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[36]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "rootThickness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[20]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipThickness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[37]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "displacement";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[43]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Use Instance Obj"
+ -addControl "instancingStatus" "AEshaveHairDimControls";
+ editorTemplate -addControl "interpolateGuides";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Material Properties" -collapse false;
+
+ editorTemplate -addControl "selfShadow";
+ editorTemplate -addControl "geomShadow";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "specular";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[4]";
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "specularTint";
+
+ AEshaveSpacer();
+
+ //editorTemplate -addControl "specular2";
+ //editorTemplate -callCustom "AEshaveTexParmNew"
+ // "AEshaveTexParmReplace" "shaveTex[4]";
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "specularTint2";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "gloss";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[5]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "amb/diff";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[6]";
+
+ AEshaveSpacer();
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "hairColor";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "hairColorTexture";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "hueVariation";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[12]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "valueVariation";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[39]";
+
+ AEshaveSpacer();
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "rootColor";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "rootHairColorTexture";
+
+ AEshaveSpacer();
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "mutantHairColor";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "mutantHairColorTexture";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "percentMutantHairs";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[16]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipFade";
+ editorTemplate -addControl "squirrel";
+ editorTemplate -addControl "overrideGeomShader";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Flyaway Hairs" -collapse false;
+
+ editorTemplate -addControl "flyawayPerc";
+
+ editorTemplate -addControl "flyawayStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[48]";
+
+ editorTemplate -addControl "messStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[47]";
+
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Clumping" -collapse false;
+
+ editorTemplate -addControl "clumps";
+
+ editorTemplate -label "Strength" -addControl "clumpsStrength";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[49]";
+
+ editorTemplate -label "Scruffle" -addControl "clumpsScruffle";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[55]";
+
+
+ editorTemplate -label "Flatness" -addControl "clumpsFlatness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[54]";
+
+ editorTemplate -label "Rotation" -addControl "clumpsRotStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[50]";
+
+ editorTemplate -label "Rotation Offset" -addControl "clumpsRotOffset";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[52]";
+
+
+ editorTemplate -label "Randomize Rotation" -addControl "clumpsRandomize";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[53]";
+
+ editorTemplate -label "Clump Colors" -addControl "clumpsColStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[51]";
+
+
+
+ editorTemplate -endLayout;
+ /*
+ editorTemplate -beginLayout "Procedurals" -collapse false;
+
+ editorTemplate -callCustom "proceduralsAddUI" "proceduralsEditUI" $nodeName;
+
+ editorTemplate -endLayout;
+ */
+
+
+ editorTemplate -beginLayout "Frizz Properties" -collapse true;
+ editorTemplate -addControl "rootFrizz";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[0]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipFrizz";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[24]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzXFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[1]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzYFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[30]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzZFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[31]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzAnim";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[32]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "animSpeed";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[33]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzAnimDir";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Kink Properties" -collapse true;
+ editorTemplate -addControl "rootKink";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[38]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipKink";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[2]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "kinkXFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[3]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "kinkYFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[34]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "kinkZFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[35]";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Multi Strand Properties" -collapse true;
+ editorTemplate -addControl "multiStrandCount";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[25]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "rootSplay";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[26]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipSplay";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[27]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Twist" -addControl "multAsp";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[44]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "Offset";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[45]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "Aspect";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[46]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Randomize" -addControl "randomizeMulti";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[42]";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Dynamics Properties" -collapse true;
+ editorTemplate -addControl "enableCollision";
+ editorTemplate -addControl "disableDynamics";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "stiffness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[8]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Root Hold" -addControl "rootStiffness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[21]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Dampen" -addControl "dampening";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[40]";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Rib Properties" -collapse true;
+ editorTemplate -addControl "ribStuff";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Nurbs Tesselation Properties" -collapse true;
+ editorTemplate -addControl "uSubdivisions";
+ editorTemplate -addControl "vSubdivisions";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Subdiv Tesselation Properties" -collapse true;
+ editorTemplate -addControl "Depth";
+ editorTemplate -addControl "sampleCount";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Hair Display" -collapse true;
+ // editorTemplate -addControl "displayGuides";
+ editorTemplate -l "Display Hair As" -addControl "displayAs";
+ // editorTemplate -l "Max Segments" -addControl "displaySegmentLimit"; //goes to globals
+ // editorTemplate -l "Guide Thickness" -addControl "displayGuideThick"; //goes go globals
+ // editorTemplate -l "Use Transparency" -addControl "displayHairTransprency"; //goes to globals
+ // editorTemplate -l "Use SSAO" -addControl "displayHairSsao"; //goes to globals
+ // editorTemplate -l "Hair Transparency" -addControl "displayHairTransprencyVal";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Random Seed" -collapse true;
+
+ editorTemplate -label "Offset" -addControl "randomSeedOffset";
+
+ editorTemplate -endLayout;
+
+ //
+ // We don't want to see these.
+ //
+ if ( false /*!is3DelightForMayaLoaded()*/)
+ {
+ editorTemplate -suppress "castsShadows";
+ editorTemplate -suppress "receiveShadows";
+ editorTemplate -suppress "motionBlur";
+ editorTemplate -suppress "primaryVisibility";
+ editorTemplate -suppress "smoothShading";
+ editorTemplate -suppress "visibleInReflections";
+ editorTemplate -suppress "visibleInRefractions";
+ editorTemplate -suppress "doubleSided";
+ editorTemplate -suppress "opposite";
+ }
+ else
+ {
+ editorTemplate -beginLayout "Render Stats";
+ editorTemplate -beginNoOptimize;
+ editorTemplate -addControl "castsShadows";
+ editorTemplate -addControl "receiveShadows";
+ editorTemplate -addControl "motionBlur";
+ editorTemplate -addControl "primaryVisibility";
+ editorTemplate -addControl "smoothShading";
+ editorTemplate -addControl "visibleInReflections";
+ editorTemplate -addControl "visibleInRefractions";
+ editorTemplate -interruptOptimize;
+ editorTemplate -addControl "doubleSided" "AEshaveHairCheckDoubleSided";
+ editorTemplate -addControl "opposite";
+ editorTemplate -endNoOptimize;
+ editorTemplate -endLayout;
+ }
+
+ editorTemplate -suppress "active";
+ editorTemplate -suppress "cachedBBox";
+ editorTemplate -suppress "displaceStrength";
+ editorTemplate -suppress "displayHairMax";
+ editorTemplate -suppress "displayHairRatio";
+ editorTemplate -suppress "hairColor";
+ editorTemplate -suppress "hairColorTexture";
+ editorTemplate -suppress "hairColorTextureR";
+ editorTemplate -suppress "hairColorTextureG";
+ editorTemplate -suppress "hairColorTextureB";
+ editorTemplate -suppress "instanceSource";
+ editorTemplate -suppress "mutantHairColor";
+ editorTemplate -suppress "mutantHairColorTexture";
+ editorTemplate -suppress "rootHairColorTexture";
+ editorTemplate -suppress "rootColor";
+ editorTemplate -suppress "rootColor";
+ editorTemplate -suppress "shaveIsShadows";
+ editorTemplate -suppress "shaveTex";
+ editorTemplate -suppress "specularTint";
+ editorTemplate -suppress "specularTint2";
+ editorTemplate -suppress "textureCacheUpdated";
+ editorTemplate -suppress "shaveVersion";
+ editorTemplate -suppress "procedural";
+
+
+ //
+ // We don't want to see these inherited attributes, either.
+ //
+ editorTemplate -suppress "collisionDepthVelocityIncrement";
+ editorTemplate -suppress "collisionDepthVelocityMultiplier";
+ editorTemplate -suppress "collisionOffsetVelocityIncrement";
+ editorTemplate -suppress "collisionOffsetVelocityMultiplier";
+ editorTemplate -suppress "renderLayerInfo";
+ editorTemplate -suppress "maxVisibilitySamplesOverride";
+ editorTemplate -suppress "maxVisibilitySamples";
+ editorTemplate -suppress "geometryAntialiasingOverride";
+ editorTemplate -suppress "antialiasingLevel";
+ editorTemplate -suppress "shadingSamplesOverride";
+ editorTemplate -suppress "shadingSamples";
+ editorTemplate -suppress "maxShadingSamples";
+ editorTemplate -suppress "volumeSamplesOverride";
+ editorTemplate -suppress "volumeSamples";
+ editorTemplate -suppress "depthJitter";
+ editorTemplate -suppress "ignoreSelfShadowing";
+ editorTemplate -suppress "compInstObjGroups";
+ editorTemplate -suppress "tweak";
+ editorTemplate -suppress "relativeTweak";
+ editorTemplate -suppress "controlPoints";
+ editorTemplate -suppress "weights";
+ editorTemplate -suppress "uvSet";
+ editorTemplate -suppress "currentUVSet";
+ editorTemplate -suppress "displayImmediate";
+ editorTemplate -suppress "displayColors";
+ editorTemplate -suppress "displayColorChannel";
+ editorTemplate -suppress "currentColorSet";
+ editorTemplate -suppress "colorSet";
+ editorTemplate -suppress "boundingBoxScale";
+ editorTemplate -suppress "featureDisplacement";
+ editorTemplate -suppress "initialSampleRate";
+ editorTemplate -suppress "extraSampleRate";
+ editorTemplate -suppress "textureThreshold";
+ editorTemplate -suppress "normalThreshold";
+
+
+ //
+ // Handle the standard node attributes.
+ //
+ AEshapeTemplate $nodeName;
+
+ //
+ // Display any dynamically added attributes.
+ //
+ editorTemplate -addExtraControls;
+
+ editorTemplate -endScrollLayout;
+
+ AEshaveHairDimControls($nodeName);
+}
+
+
+global proc AEshaveSpacer()
+{
+ editorTemplate -callCustom "AEshaveSeparator" "AEshaveDummy";
+}
+
+
+global proc AEshaveSeparator()
+{
+ separator -style "none";
+}
+
+global proc AEshaveDummy()
+{
+}
+
+
+global proc AEshaveColourNew(string $attrName)
+{
+ string $tokens[];
+ int $numTokens = tokenize($attrName, ".", $tokens);
+ string $sliderName = $tokens[$numTokens - 1] + "_slider";
+
+ attrColorSliderGrp -showButton no $sliderName;
+
+ AEshaveColourReplace($attrName);
+}
+
+
+global proc AEshaveColourReplace(string $attrName)
+{
+ string $tokens[];
+ int $numTokens = tokenize($attrName, ".", $tokens);
+ string $attrPortion = $tokens[$numTokens-1];
+ string $sliderName = $attrPortion + "_slider";
+ string $label = $attrPortion;
+
+ if ($attrPortion == "hairColor")
+ $label = "Tip Color";
+ else if($attrPortion == "specularTint2")
+ $label = "Secondary Specular";
+ else
+ $label = interToUI($attrPortion);
+
+ attrColorSliderGrp -e -l $label -at $attrName $sliderName;
+}
+
+
+global proc AEshaveTexParmNew (string $attrName)
+{
+ string $buffer[];
+ int $numTokens = `tokenize $attrName "[" $buffer`;
+ string $grpnam;
+ if($numTokens > 1)
+ {
+ $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ $numTokens = `tokenize $attrName "[" $buffer`;
+ $numTokens = `tokenize $buffer[1] "]" $buffer`;
+ $grpnam = $grpnam + $buffer[0];
+ }
+ else
+ {
+ $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ $grpnam = $grpnam + "Grp";
+ }
+ attrNavigationControlGrp
+ -l ""
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ $grpnam;
+}
+
+
+//
+// Procedure Name:
+// AEshaderBumpReplace
+//
+
+global proc AEshaveTexParmReplace (string $attrName)
+{
+ string $buffer[];
+ int $numTokens = `tokenize $attrName "[" $buffer`;
+ string $grpnam;
+ if($numTokens > 1)
+ {
+ $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ $numTokens = `tokenize $attrName "[" $buffer`;
+ $numTokens = `tokenize $buffer[1] "]" $buffer`;
+ $grpnam = $grpnam + $buffer[0];
+ }
+ else
+ {
+ $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ $grpnam = $grpnam + "Grp";
+ }
+ attrNavigationControlGrp -edit
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ $grpnam;
+}
+
+global proc AEshaveDensityTexParmNew (string $attrName)
+{
+ attrNavigationControlGrp
+ -l "Density Map"
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "densityGrp";
+}
+
+
+//
+// Procedure Name:
+// AEshaderBumpReplace
+//
+
+global proc AEshaveDensityTexParmReplace (string $attrName)
+{
+ attrNavigationControlGrp -edit
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "densityGrp";
+}
+
+global proc AEshaveCutTexParmNew (string $attrName)
+{
+ attrNavigationControlGrp
+ -l "Cut Map"
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "cutGrp";
+ ;
+}
+
+
+//
+// Procedure Name:
+// AEshaderBumpReplace
+//
+
+global proc AEshaveCutTexParmReplace (string $attrName)
+{
+ attrNavigationControlGrp -edit
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "cutGrp";
+}
+
+
+global proc AEshaveParmConnect(string $dest, string $dummy, string $src)
+{
+ if ($src == "")
+ {
+ string $conns[] = `listConnections -p yes -s yes -d no $dest`;
+
+ if (size($conns) > 0) disconnectAttr $conns[0] $dest;
+ }
+ else
+ {
+ defaultNavigation -ce -d $dest -s $src;
+ }
+}
+
+global proc proceduralsAddUI(string $shaveNode)
+{
+ columnLayout
+ -columnAttach both 50
+ -columnWidth 400;
+ {
+ textScrollList
+ -numberOfRows 6
+ -selectCommand ("shaveSelectProcedural " + $shaveNode)
+ shaveProceduralList;
+
+ updateShaveProceduralList $shaveNode;
+
+ columnLayout shaveProceduralLayout;
+ {
+ text
+ -label "Procedural UI goes there.";
+
+ setParent ..;
+ }
+
+ separator
+ -style "none"
+ -height 6;
+
+ button
+ -label "Update View";
+
+ separator
+ -style "none"
+ -height 6;
+
+ setParent ..;
+
+ }
+}
+
+global proc proceduralsEditUI(string $shaveNode)
+{
+ updateShaveProceduralList $shaveNode;
+}
+
+global proc updateShaveProceduralList(string $shaveNode)
+{
+ if(`textScrollList -exists shaveProceduralList`)
+ {
+ textScrollList
+ -edit
+ -selectCommand ("shaveSelectProcedural " + $shaveNode)
+ shaveProceduralList;
+
+ textScrollList
+ -edit
+ -removeAll
+ shaveProceduralList;
+
+ //
+ //add items to the list
+ //
+ int $num = `shaveProcedurals -query -total`;
+ for($i = 0; $i < $num; $i++)
+ {
+ string $name = `shaveProcedurals -name $i -query`;
+ textScrollList
+ -edit
+ -append $name
+ shaveProceduralList;
+ }
+ //
+ // update selection
+ //
+ updateShaveProceduralSel $shaveNode;
+ }
+}
+
+global proc updateShaveProceduralSel(string $shaveNode)
+{
+ if(`textScrollList -exists shaveProceduralList`)
+ {
+ textScrollList
+ -edit
+ -deselectAll
+ shaveProceduralList;
+
+ string $buffer[];
+ string $shaveNodeName;
+ $numTokens = `tokenize $shaveNode "." $buffer`;
+ if($numTokens > 0)
+ $shaveNodeName = $buffer[0];
+ else
+ $shaveNodeName = $shaveNode;
+
+ string $pattr = ($shaveNodeName +".procedural");
+ if (`connectionInfo -isDestination $pattr`)
+ {
+ string $src = `connectionInfo -sourceFromDestination $pattr`;
+
+ string $buffer[];
+ string $procedural = "";
+ $numTokens = `tokenize $src "." $buffer`;
+ if($numTokens > 0)
+ $procedural = $buffer[0];
+
+ string $name = `nodeType $procedural`;
+
+ ////////////
+ //print $name; print "\n";
+ ////////////
+
+ int $num = `shaveProcedurals -query -total`;
+ for($i = 0; $i < $num; $i++)
+ {
+ string $name2 = `shaveProcedurals -name $i -query`;
+ if($name == $name2)
+ textScrollList
+ -edit
+ -selectIndexedItem ($i+1)
+ shaveProceduralList;
+ }
+ }
+ }
+}
+
+global proc shaveSelectProcedural(string $shaveNode)
+{
+ if(`textScrollList -exists shaveProceduralList`)
+ {
+ string $buffer[];
+ string $shaveNodeName;
+ $numTokens = `tokenize $shaveNode "." $buffer`;
+ if($numTokens > 0)
+ $shaveNodeName = $buffer[0];
+ else
+ $shaveNodeName = $shaveNode;
+
+ string $dst = ($shaveNodeName +".procedural");
+
+ //
+ // disconnect old
+ //
+ if (`connectionInfo -isDestination $dst`)
+ {
+ string $src = `connectionInfo -sourceFromDestination $dst`;
+ //if( size($srcs) == 1)
+ //{
+ //string $src = $srcs[0];
+ disconnectAttr $src $dst;
+
+ //deleter procedural node
+ string $buffer[];
+ string $proctodel = "";
+ $numTokens = `tokenize $src "." $buffer`;
+ if($numTokens > 0)
+ {
+ $proctodel = $buffer[0];
+ delete $proctodel;
+ }
+ //}
+ }
+ //
+ // connect new one
+ //
+ string $sel[] = `textScrollList -query -selectItem shaveProceduralList`;
+ if(size($sel) == 1)
+ {
+ string $name = $sel[0];
+ $procName = `createNode $name`;
+ if($procName != "")
+ {
+ select $shaveNodeName;
+
+ string $src = ($procName +".shave");
+
+ connectAttr $src $dst;
+ if(`isConnected $src $dst`)
+ {
+ /////
+ //print " -- connected --\n";
+ /////
+
+ }
+ }
+ updateShaveProceduralSel $shaveNode;
+ }
+ }
+}
diff --git a/scripts/AEshaveNodeTemplate.mel b/scripts/AEshaveNodeTemplate.mel
new file mode 100644
index 0000000..7f00f69
--- /dev/null
+++ b/scripts/AEshaveNodeTemplate.mel
@@ -0,0 +1,520 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $AEshaveNodeTemplate_fileVersion = "$Revision$";
+
+
+global proc AEshaveNodeDimControls(string $node)
+{
+ int $isInstanced = getAttr($node + ".instancingStatus");
+
+ if ($isInstanced)
+ editorTemplate -dimControl $node "overrideGeomShader" true;
+ else
+ editorTemplate -dimControl $node "overrideGeomShader" false;
+}
+
+
+global proc AEshaveNodeTemplate( string $nodeName )
+{
+ // Put our attributes into a scrolled layout field
+ editorTemplate -beginScrollLayout;
+
+ editorTemplate -beginLayout "General Properties" -collapse false;
+ editorTemplate -addControl "hairCount";
+ editorTemplate -addControl "hairSegments";
+ editorTemplate -addControl "hairPasses";
+
+ editorTemplate -callCustom "AEshaveCutTexParmNew"
+ "AEshaveCutTexParmReplace" "shaveTex[29]";
+
+ editorTemplate -callCustom "AEshaveDensityTexParmNew"
+ "AEshaveDensityTexParmReplace" "shaveTex[28]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "scale";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[41]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "randScale";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[36]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "rootThickness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[20]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipThickness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[37]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "displacement";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[43]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "active";
+ editorTemplate -label "Use Instance Obj"
+ -addControl "instancingStatus" "AEshaveNodeDimControls";
+ editorTemplate -addControl "interpolateGuides";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Material Properties" -collapse false;
+
+ editorTemplate -addControl "selfShadow";
+ editorTemplate -addControl "geomShadow";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "specular";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[4]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "gloss";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[5]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "amb/diff";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[6]";
+
+ AEshaveSpacer();
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "hairColor";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "hairColorTexture";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "hueVariation";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[12]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "valueVariation";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[39]";
+
+ AEshaveSpacer();
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "rootColor";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "rootHairColorTexture";
+
+ AEshaveSpacer();
+
+ editorTemplate -callCustom "AEshaveColourNew"
+ "AEshaveColourReplace" "mutantHairColor";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "mutantHairColorTexture";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "percentMutantHairs";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[16]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "overrideGeomShader";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Flyaway Hairs" -collapse false;
+
+ editorTemplate -addControl "flyawayPerc";
+
+ editorTemplate -addControl "flyawayStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "flyawayStrenTexture";
+
+ editorTemplate -addControl "messStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "messStrenTexture";
+
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Clumping" -collapse false;
+
+ editorTemplate -addControl "clumps";
+
+ editorTemplate -addControl "clumpsStrength";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[49]";
+
+ editorTemplate -addControl "clumpsScruffle";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[55]";
+
+ editorTemplate -addControl "clumpsRotStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[50]";
+
+ editorTemplate -addControl "clumpsColStren";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[51]";
+
+ editorTemplate -addControl "clumpsRotOffset";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[52]";
+
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Frizz Properties" -collapse true;
+ editorTemplate -addControl "rootFrizz";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[0]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipFrizz";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[24]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzXFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[1]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzYFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[30]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzZFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[31]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzAnim";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[32]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "animSpeed";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[33]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "frizzAnimDir";
+ editorTemplate -endLayout;
+
+
+ editorTemplate -beginLayout "Kink Properties" -collapse true;
+ editorTemplate -addControl "rootKink";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[38]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipKink";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[2]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "kinkXFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[3]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "kinkYFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[34]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "kinkZFrequency";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[35]";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Multi Strand Properties" -collapse true;
+ editorTemplate -addControl "multiStrandCount";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[25]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "rootSplay";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[26]";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "tipSplay";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[27]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Twist" -addControl "multAsp";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[44]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Randomize" -addControl "randomizeMulti";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[42]";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Dynamics Properties" -collapse true;
+ editorTemplate -addControl "enableCollision";
+ editorTemplate -addControl "disableDynamics";
+
+ AEshaveSpacer();
+
+ editorTemplate -addControl "stiffness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[8]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Root Hold" -addControl "rootStiffness";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[21]";
+
+ AEshaveSpacer();
+
+ editorTemplate -label "Dampen" -addControl "dampening";
+ editorTemplate -callCustom "AEshaveTexParmNew"
+ "AEshaveTexParmReplace" "shaveTex[40]";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Rib Properties" -collapse true;
+ editorTemplate -addControl "ribStuff";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Nurbs Tesselation Properties" -collapse true;
+ editorTemplate -addControl "uSubdivisions";
+ editorTemplate -addControl "vSubdivisions";
+ editorTemplate -endLayout;
+
+ editorTemplate -beginLayout "Hair Display" -collapse true;
+ editorTemplate -addControl "displayAs";
+ // editorTemplate -l "Display/Actual Ratio" -addControl "displayHairRatio";
+ editorTemplate -l "Max To Display" -addControl "displayHairMax";
+ editorTemplate -endLayout;
+
+ //
+ // Handle the standard node attributes.
+ //
+ AEdependNodeTemplate $nodeName;
+
+ //
+ // Display any dynamically added attributes.
+ //
+ editorTemplate -addExtraControls;
+
+ //
+ // We don't want to see these.
+ //
+ editorTemplate -suppress "hairColor";
+ editorTemplate -suppress "hairColorTexture";
+ editorTemplate -suppress "hairColorTextureR";
+ editorTemplate -suppress "hairColorTextureG";
+ editorTemplate -suppress "hairColorTextureB";
+ editorTemplate -suppress "instanceSource";
+ editorTemplate -suppress "mutantHairColor";
+ editorTemplate -suppress "mutantHairColorTexture";
+ editorTemplate -suppress "rootHairColorTexture";
+ editorTemplate -suppress "rootColor";
+ editorTemplate -suppress "rootColor";
+ editorTemplate -suppress "shaveIsShadows";
+ editorTemplate -suppress "shaveTex";
+
+ editorTemplate -endScrollLayout;
+
+ AEshaveNodeDimControls($nodeName);
+}
+
+
+global proc AEshaveSpacer()
+{
+ editorTemplate -callCustom "AEshaveSeparator" "AEshaveDummy";
+}
+
+
+global proc AEshaveSeparator()
+{
+ separator -style "none";
+}
+
+global proc AEshaveDummy()
+{
+}
+
+
+global proc AEshaveColourNew(string $attrName)
+{
+ string $tokens[];
+ int $numTokens = tokenize($attrName, ".", $tokens);
+ string $sliderName = $tokens[$numTokens - 1] + "_slider";
+
+ attrColorSliderGrp -showButton no $sliderName;
+
+ AEshaveColourReplace($attrName);
+}
+
+
+global proc AEshaveColourReplace(string $attrName)
+{
+ string $tokens[];
+ int $numTokens = tokenize($attrName, ".", $tokens);
+ string $attrPortion = $tokens[$numTokens-1];
+ string $sliderName = $attrPortion + "_slider";
+ string $label = $attrPortion;
+
+ if ($attrPortion == "hairColor")
+ $label = "Tip Color";
+ else
+ $label = interToUI($attrPortion);
+
+ attrColorSliderGrp -e -l $label -at $attrName $sliderName;
+}
+
+
+global proc AEshaveTexParmNew (string $attrName)
+{
+ string $buffer[];
+ int $numTokens = `tokenize $attrName "[" $buffer`;
+ string $grpnam;
+ if($numTokens > 1)
+ {
+ int $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ int $numTokens = `tokenize $attrName "[" $buffer`;
+ int $numTokens = `tokenize $buffer[1] "]" $buffer`;
+ $grpnam = $grpnam + $buffer[0];
+ }
+ else
+ {
+ int $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ $grpnam = $grpnam + "Grp";
+ }
+ attrNavigationControlGrp
+ -l ""
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ $grpnam;
+}
+
+
+//
+// Procedure Name:
+// AEshaderBumpReplace
+//
+
+global proc AEshaveTexParmReplace (string $attrName)
+{
+ string $buffer[];
+ int $numTokens = `tokenize $attrName "[" $buffer`;
+ string $grpnam;
+ if($numTokens > 1)
+ {
+ int $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ int $numTokens = `tokenize $attrName "[" $buffer`;
+ int $numTokens = `tokenize $buffer[1] "]" $buffer`;
+ $grpnam = $grpnam + $buffer[0];
+ }
+ else
+ {
+ int $numTokens = `tokenize $buffer[0] "." $buffer`;
+ $grpnam = $buffer[1];
+ $grpnam = $grpnam + "Grp";
+ }
+ attrNavigationControlGrp -edit
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ $grpnam;
+}
+
+global proc AEshaveDensityTexParmNew (string $attrName)
+{
+ attrNavigationControlGrp
+ -l "Density Map"
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "densityGrp";
+}
+
+
+//
+// Procedure Name:
+// AEshaderBumpReplace
+//
+
+global proc AEshaveDensityTexParmReplace (string $attrName)
+{
+ attrNavigationControlGrp -edit
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "densityGrp";
+}
+
+global proc AEshaveCutTexParmNew (string $attrName)
+{
+ attrNavigationControlGrp
+ -l "Cut Map"
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "cutGrp";
+ ;
+}
+
+
+//
+// Procedure Name:
+// AEshaderBumpReplace
+//
+
+global proc AEshaveCutTexParmReplace (string $attrName)
+{
+ attrNavigationControlGrp -edit
+ -at $attrName
+ -ce ("shavePadCmd AEshaveParmConnect 3 " + $attrName)
+ "cutGrp";
+}
+
+
+global proc AEshaveParmConnect(string $dest, string $dummy, string $src)
+{
+ if ($src == "")
+ {
+ string $conns[] = `listConnections -p yes -s yes -d no $dest`;
+
+ if (size($conns) > 0) disconnectAttr $conns[0] $dest;
+ }
+ else
+ {
+ defaultNavigation -ce -d $dest -s $src;
+ }
+}
diff --git a/scripts/shaveAEOverrides.mel b/scripts/shaveAEOverrides.mel
new file mode 100644
index 0000000..d0e653e
--- /dev/null
+++ b/scripts/shaveAEOverrides.mel
@@ -0,0 +1,52 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveAEOverrides_fileVersion = "$Revision$";
+
+//
+// This file contains overrides for some of Maya's standard attribute
+// editor scripts so that Shave can add functionality of its own.
+//
+
+//
+// Dummy proc used to locate the file via 'whatIs'.
+//
+global proc shaveAEOverrides()
+{
+}
+
+global proc AElightCommonShadow2 ( string $nodeName )
+{
+ editorTemplate -addControl "shadowRays";
+ editorTemplate -addControl "rayDepthLimit";
+ editorTemplate -endLayout;
+
+
+ //suppressed light parameters
+ editorTemplate -suppress "centerOfIllumination";
+ editorTemplate -suppress "pointCamera";
+ editorTemplate -suppress "matrixWorldToEye";
+ editorTemplate -suppress "matrixEyeToWorld";
+ editorTemplate -suppress "objectId";
+ editorTemplate -suppress "primitiveId";
+ editorTemplate -suppress "raySampler";
+ editorTemplate -suppress "rayDepth";
+ editorTemplate -suppress "lightData";
+ editorTemplate -suppress "opticalFXvisibility";
+ editorTemplate -suppress "renderState";
+
+ // if Maya Fur is loaded (ie. if the Fur shading layout procedure
+ // exists), then end the current layout (ie. Shadows) and add the
+ // Fur shading layout
+ //
+ if ( `exists AEFurShading` ) {
+ editorTemplate -endLayout;
+ eval( "AEFurShading " + $nodeName + " true false" );
+ }
+
+ //
+ // Add the Shave layout, if needed.
+ //
+ eval("shave_AElight \"" + $nodeName + "\"");
+}
diff --git a/scripts/shaveBrush.mel b/scripts/shaveBrush.mel
new file mode 100644
index 0000000..9993931
--- /dev/null
+++ b/scripts/shaveBrush.mel
@@ -0,0 +1,422 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveBrush_fileVersion = "$Revision$";
+
+//
+// Initialize brush handling.
+//
+global proc shaveBrush()
+{
+ source shaveCursorCtxCommonProperties;
+ source shaveCursorCtxCommonValues;
+
+ //
+ // Create any properties which don't yet exist.
+ //
+ if (!`optionVar -exists shaveBrushSelectMode`)
+ optionVar -stringValue shaveBrushSelectMode "guide";
+
+ if (!`optionVar -exists shaveBrushMode`)
+ optionVar -intValue shaveBrushMode 0;
+
+ //
+ // Have the 'Other' component popup menu display hair components as
+ // well.
+ //
+ popupMenu -e -pmc shaveBrush_createComponentMenu compOtherPopup;
+
+ //
+ // Whenever the selection mode changes, convert selections.
+ //
+ global int $gShave_selectModeChangedJob;
+
+ $gShave_selectModeChangedJob = `scriptJob -e SelectModeChanged
+ "shaveUtil -convertComponentSelections"`;
+}
+
+
+global proc shaveBrush_cleanup()
+{
+ if (`popupMenu -exists compOtherPopup`)
+ popupMenu -e -pmc "createMaskPopup \"compOther\"" compOtherPopup;
+
+ //
+ // Kill off the scriptJob we started up earlier.
+ //
+ global int $gShave_selectModeChangedJob;
+
+ shave_killJob($gShave_selectModeChangedJob);
+ $gShave_selectModeChangedJob = -1;
+}
+
+
+global proc shaveBrush_convertComponentSelections(int $forceToComponentMode)
+{
+ //
+ // If we're not in component mode, and we're not forcing component
+ // mode, then there's nothing to do.
+ //
+ int $isComponentMode = `selectMode -q -co`;
+
+ if (!$forceToComponentMode && !$isComponentMode) return;
+
+ //
+ // Convert any existing selections to the current selection type. For
+ // example, if a guide has a vert selected and we're in guide selection
+ // mode, then select the guide.
+ //
+ int $foundComponents = false;
+ string $hairNode = "";
+ string $item;
+ string $mode = `optionVar -q shaveBrushSelectMode`;
+ string $newSel = "";
+ int $nodeIsHilited = false;
+ string $sel[] = `ls -sl`;
+ int $selectionChanged = false;
+ string $tmp[];
+
+ //
+ // If we're in component mode, give priority to the first hair node on
+ // the hilite list.
+ //
+ if ($isComponentMode)
+ {
+ $sel = `ls -hilite -dag -type shaveHair`;
+
+ if (size($sel) > 0) $hairNode = $sel[0];
+ }
+
+ $sel = `ls -sl`;
+
+ for ($item in $sel)
+ {
+ //
+ // We only care about the first hair node on the selection list
+ // (plus any components which might appear for that same node later
+ // in the list).
+ //
+ $tmp = `ls -o $item`;
+ $tmp = `ls -dag -s $tmp[0]`;
+
+ if (nodeType($tmp[0]) == "shaveHair")
+ {
+ if ($hairNode == "") $hairNode = $tmp[0];
+
+ if ($tmp[0] == $hairNode)
+ {
+ //
+ // Get the name of the component, if there is one.
+ //
+ tokenize $item "." $tmp;
+
+ if (size($tmp) == 2)
+ {
+ tokenize $tmp[1] "[]" $tmp;
+
+ if (($tmp[0] == "e")
+ || ($tmp[0] == "gd")
+ || ($tmp[0] == "guide"))
+ {
+ string $guideRange = $tmp[1];
+
+ switch ($mode)
+ {
+ case "guide":
+ case "root":
+ $newSel += " " + $item;
+ break;
+
+ case "tip":
+ $newSel += " " + $hairNode
+ + ".cv[" + $guideRange + "][14]";
+ $selectionChanged = true;
+ break;
+
+ case "vert":
+ $newSel += " " + $hairNode
+ + ".cv["+$guideRange+"][0:14]";
+ $selectionChanged = true;
+ break;
+ }
+
+ $foundComponents = true;
+ }
+ else if (($tmp[0] == "cv")
+ || ($tmp[0] == "vtx")
+ || ($tmp[0] == "vertex"))
+ {
+ string $guideRange = $tmp[1];
+ string $vtxRange = $tmp[2];
+
+ switch ($mode)
+ {
+ case "guide":
+ case "root":
+ $newSel += " " + $hairNode
+ + ".e[" + $guideRange + "]";
+ $selectionChanged = true;
+ break;
+
+ case "tip":
+ $newSel += " " + $hairNode
+ + ".cv[" + $guideRange + "][14]";
+
+ if ($vtxRange != "14")
+ {
+ $selectionChanged = true;
+ }
+ break;
+
+ case "vert":
+ $newSel += " " + $item;
+ break;
+ }
+
+ $foundComponents = true;
+ }
+ else
+ $selectionChanged = true;
+ }
+ else
+ $selectionChanged = true;
+ }
+ else
+ $selectionChanged = true;
+ }
+ }
+
+ //
+ // If we didn't find a hair node (or components thereof) on the
+ // selection list, then check the hilite list.
+ //
+ // If we did find a hair node, then see if it is already hilited.
+ //
+ $sel = `ls -hilite -dag -type shaveHair`;
+
+ if ($hairNode == "")
+ {
+ if (size($sel) > 0) $hairNode = $sel[0];
+ }
+ else
+ {
+ for ($item in $sel)
+ {
+ if ($item == $hairNode)
+ {
+ $nodeIsHilited = true;
+ break;
+ }
+ }
+ }
+
+ //
+ // If no hair node was selected then do nothing.
+ //
+ if ($hairNode == "") return;
+
+ //
+ // If we didn't end up with any hair components then select all of
+ // the hair node's components for the current component type.
+ //
+ if (!$foundComponents)
+ {
+ int $numGuides = getAttr($hairNode + ".totalGuides");
+
+ if ($numGuides > 0)
+ {
+ switch ($mode)
+ {
+ case "guide":
+ case "root":
+ $newSel = $hairNode + ".e[0:" + ($numGuides-1) + "]";
+ break;
+
+ case "tip":
+ $newSel = $hairNode + ".cv[0:" + ($numGuides-1) + "][14]";
+ break;
+
+ case "vert":
+ $newSel = $hairNode + ".cv[0:" + ($numGuides-1) + "][0:14]";
+ break;
+ }
+
+ $foundComponents = true;
+ $selectionChanged = true;
+ }
+ }
+
+ //
+ // At this point we have a hairNode and will definitely end up with
+ // some components selected, so it is now safe to force component mode,
+ // if we're not already there.
+ //
+ if (!$isComponentMode) selectMode -co;
+
+ //
+ // If the node is not already hilited, then do so now.
+ //
+ if (!$nodeIsHilited) hilite $hairNode;
+
+ //
+ // And finally, if we've changed the selections, then apply them now.
+ //
+ if ($selectionChanged && ($newSel != ""))
+ eval("select -r " + $newSel);
+}
+
+
+global proc shaveBrush_createComponentMenu()
+{
+ string $selMode = `optionVar -q shaveBrushSelectMode`;
+
+ createMaskPopup "compOther";
+ setParent -m compOtherPopup;
+ menuItem -l "Shave Guides" -cb ($selMode == "guide")
+ -c "shaveSelectGuides" shaveGuideCompItem;
+ menuItem -l "Shave Roots" -cb ($selMode == "root")
+ -c "shaveSelectByRoots" shaveRootCompItem;
+ menuItem -l "Shave Tips" -cb ($selMode == "tip")
+ -c "shaveSelectByTips" shaveTipCompItem;
+ menuItem -l "Shave Verts" -cb ($selMode == "vert")
+ -c "shaveSelectVerts" shaveVertCompItem;
+}
+
+global proc shaveBrush_resize(int $enable)
+{
+ string $ctx = `currentCtx`;
+
+ if (`contextInfo -class $ctx` == "shaveBrush")
+ shaveBrushCtx -e -interactiveResize $enable $ctx;
+ else if (`contextInfo -class $ctx` == "shaveCut")
+ shaveCutCtx -e -interactiveResize $enable $ctx;
+}
+
+
+global proc shaveBrush_setHotkeys(int $enable)
+{
+ global string $gShaveBrushResizeOldKeyPressCmd;
+ global string $gShaveBrushResizeOldKeyReleaseCmd;
+ global string $gShaveBrush_origHotkeySet = "";
+
+ string $curHotkeySet = `hotkeySet -q -current`;
+
+ if ($enable)
+ {
+ // We should never get nested enables.
+ //
+ if ($curHotkeySet == "Shave_Hotkeys")
+ {
+ warning("Shave: Nested hotkey settings detected. Please contact [email protected]");
+ return;
+ }
+
+ $gShaveBrush_origHotkeySet = $curHotkeySet;
+
+ if (!`hotkeySet -q -exists Shave_Hotkeys`)
+ {
+ hotkeySet -current Shave_Hotkeys;
+
+ // Create named commands for resizing the brush and assign them to
+ // the key.
+ //
+ nameCommand -ann "Shave brush resize begin"
+ -c "shaveBrush_resize on" shaveBrushResizeBegin;
+ nameCommand -ann "Shave brush resize end"
+ -c "shaveBrush_resize off" shaveBrushResizeEnd;
+
+ hotkey -k b -n shaveBrushResizeBegin -rn shaveBrushResizeEnd;
+ }
+ else
+ {
+ hotkeySet -e -current Shave_Hotkeys;
+ }
+ }
+ else
+ {
+ // No one should have the Shave hotkey set as their default. That's
+ // probably due to Maya crashing while our brush was active. Use
+ // Maya's default hotkey set as the original instead.
+ //
+ if ($gShaveBrush_origHotkeySet == "Shave_Hotkeys")
+ {
+ hotkeySet -e -current "Maya_Default";
+ }
+ // Only reset the hotkey set if the current one is ours.
+ //
+ else if ($curHotkeySet == "Shave_Hotkeys")
+ {
+ // If we never recorded an original set, use Maya's
+ // default.
+ //
+ if ($gShaveBrush_origHotkeySet == "")
+ {
+ hotkeySet -e -current "Maya_Default";
+ }
+ else
+ {
+ hotkeySet -e -current $gShaveBrush_origHotkeySet;
+ }
+ }
+
+ $gShaveBrush_origHotkeySet = "";
+ }
+}
+
+
+global proc shaveBrush_setSelectMode(string $mode)
+{
+ optionVar -sv shaveBrushSelectMode $mode;
+
+ if (`menuItem -q -exists shaveGuideCompItem`)
+ {
+ menuItem -e -cb ($mode == "guide") shaveGuideCompItem;
+ menuItem -e -cb ($mode == "root") shaveRootCompItem;
+ menuItem -e -cb ($mode == "tip") shaveTipCompItem;
+ menuItem -e -cb ($mode == "vert") shaveVertCompItem;
+ }
+
+ shaveShelf_updateSelectModeButtons($mode);
+
+ // Alas, Maya does not yet let us create own component types, so we
+ // must piggyback on top of those which already exist. That means that
+ // we must make sure that if the user sets one of our selection types,
+ // we select the corresponding Maya component type.
+ //
+ // Note that in 'root' mode we using vertices to make the selection, but it
+ // is guides which are being selected. Only in 'tip' and 'vert' modes do we
+ // actually select vertices.
+ //
+ if (($mode == "vert") || ($mode == "tip"))
+ {
+ selectType -cv on;
+ selectType -polymeshEdge off;
+
+ // For some reason VP2 won't let us do single-selection on our
+ // CVs unless the poly vert selection type is on.
+ //
+ selectType -polymeshVertex on;
+ }
+ else
+ {
+ selectType -polymeshEdge on;
+ selectType -cv off;
+ }
+
+ // If we're not currently in component selection mode, switch to it.
+ // Note that that will automatically force component selections to be
+ // updated.
+ //
+ if (!`selectMode -q -co`)
+ selectMode -co;
+ else
+ {
+ // Update component selection to match the new select mode.
+ //
+ shaveUtil -convertComponentSelections;
+ }
+
+ refresh -f;
+}
+
diff --git a/scripts/shaveBrushProperties.mel b/scripts/shaveBrushProperties.mel
new file mode 100644
index 0000000..04b383a
--- /dev/null
+++ b/scripts/shaveBrushProperties.mel
@@ -0,0 +1,21 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveBrushProperties_fileVersion = "$Revision$";
+
+
+global proc shaveBrushProperties()
+{
+ shaveCursorCtxCommonProperties("Brush", "shaveBrushCtx");
+}
+
+
+global proc shaveBrushSetMode(int $mode)
+{
+ shaveBrushSetModeButton($mode);
+
+ string $ctx = `currentCtx`;
+
+ shaveBrushCtx -e -m $mode $ctx;
+}
diff --git a/scripts/shaveBrushSetModeButton.mel b/scripts/shaveBrushSetModeButton.mel
new file mode 100644
index 0000000..957a05d
--- /dev/null
+++ b/scripts/shaveBrushSetModeButton.mel
@@ -0,0 +1,49 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveBrushSetModeButtons_fileVersion = "$Revision$";
+
+
+global proc shaveBrushSetModeButton(int $mode)
+{
+ //
+ // If one of the symbol boxes exist, assume that they all do.
+ //
+ if (`symbolCheckBox -exists shaveBrushTransBox`)
+ {
+ symbolCheckBox -e -v false shaveBrushTransBox;
+ symbolCheckBox -e -v false shaveBrushScaleBox;
+ symbolCheckBox -e -v false shaveBrushRotCursorBox;
+ symbolCheckBox -e -v false shaveBrushStandBox;
+ symbolCheckBox -e -v false shaveBrushPuffBox;
+ symbolCheckBox -e -v false shaveBrushClumpBox;
+
+ switch ($mode)
+ {
+ case 0:
+ symbolCheckBox -e -v true shaveBrushTransBox;
+ break;
+
+ case 1:
+ symbolCheckBox -e -v true shaveBrushScaleBox;
+ break;
+
+ case 2:
+ symbolCheckBox -e -v true shaveBrushRotCursorBox;
+ break;
+
+ case 3:
+ symbolCheckBox -e -v true shaveBrushStandBox;
+ break;
+
+ case 4:
+ symbolCheckBox -e -v true shaveBrushPuffBox;
+ break;
+
+ case 5:
+ symbolCheckBox -e -v true shaveBrushClumpBox;
+ break;
+ }
+ }
+}
diff --git a/scripts/shaveBrushValues.mel b/scripts/shaveBrushValues.mel
new file mode 100644
index 0000000..7c206b9
--- /dev/null
+++ b/scripts/shaveBrushValues.mel
@@ -0,0 +1,19 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveBrushValues_fileVersion = "$Revision$";
+
+
+global proc shaveBrushValues(string $toolName)
+{
+ shaveCursorCtxCommonValues($toolName);
+
+ //
+ // Update the brush-specific controls.
+ //
+ int $mode = `shaveBrushCtx -q -mode $toolName`;
+
+ shaveBrushSetModeButton($mode);
+}
+
diff --git a/scripts/shaveCheckVersion.mel b/scripts/shaveCheckVersion.mel
new file mode 100644
index 0000000..3c5117b
--- /dev/null
+++ b/scripts/shaveCheckVersion.mel
@@ -0,0 +1,173 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+// Check the version of a third-party product against a database of
+// supported versions.
+//
+// Returns:
+//
+// 1 - The specified version of the product is supported.
+// 0 - The specified version of the product is not supported.
+// -1 - An error occurred.
+//
+// $product - user-friendly name of third-party product
+//
+// $version - version of the product to be checked
+//
+// $db - path to a text file containing a database of supported versions
+// of the product. Each line of the file is of the form:
+//
+// mayaVersion: start end build
+//
+// where 'start' is the start of a range of supported versions,
+// 'end' is the end of the range, and 'build' is the version we
+// use when building the plugin which handles that range of
+// versions.
+//
+// Each of 'start', 'end' and 'build' may contain a '/' followed by
+// arbitrary text. The text is ignored by this procedure but will
+// be passed to $cmpProc if present. This text is often used to
+// indicate the corresponding versions of related SDKs. For
+// example Shave's support for the Arnold renderer uses the mtoa
+// plugin version as the the 'start', 'end' and 'build' values,
+// then includes their corresponding Arnold SDK versions after the
+// '/'. E.g:
+//
+// 2018: 3.1.0.0/5.2.0.0 3.1.9.9/5.2.9.9 3.1.0.0/5.2.0.0
+//
+// The text after the '/' must not contain any whitespace.
+//
+// $cmpProc - name of a global MEL procedure which takes two version strings
+// and compares them, returning -1 if the first version is earlier
+// than the second, 1 if the first version is later than the
+// second, or 0 if the versions are the same.
+//
+// If the versions in $db include a '/' then it and any text which
+// follows it (up to the next whitespace) will be included in the
+// strings passed to $cmpProc, so the procedure must be capable
+// of dealing with the additional text.
+//
+// $error - array to receive the error/warning message if the version is
+// not supported, or if an error occurs. In the case of the version
+// not being supported, the message will include a list of
+// supported versions. If the versions in $db contain '/' followed
+// by optional text only the portion before the '/' will be
+// included in the message.
+//
+global proc int shaveCheckVersion(string $product, string $version, string $db, string $cmpProc, string $error[])
+{
+ global string $gShaveMayaVersionStr;
+
+ clear $error;
+
+ string $result;
+ string $shaveVersion = `shaveInfo -v`;
+
+ // Let's see if we can read the database.
+ //
+ int $fid = `fopen $db "r"`;
+
+ if ($fid == 0)
+ {
+ $error[0] = "Shave and a Haircut cannot read '" + $db + "'.";
+ $error[1] = "Please reinstall Shave and a Haircut for Maya " + $gShaveMayaVersionStr + ".";
+ $error[2] = "If the problem persists, contact [email protected].";
+ return -1;
+ }
+
+ int $lineNumber = 0;
+ string $supportedVersions = "";
+ string $minVersions[];
+ string $maxVersions[];
+ string $buildVersions[];
+
+ clear $minVersions;
+ clear $maxVersions;
+ clear $buildVersions;
+
+ // Search for entries for this version of Maya.
+ //
+ string $key = $gShaveMayaVersionStr + ":";
+
+ while (!`feof $fid`)
+ {
+ string $line = strip(`fgetline $fid`);
+ $lineNumber++;
+
+ if (startsWith($line, $key))
+ {
+ string $range = strip(substring($line, size($key)+1, size($line)));
+ string $parts[];
+ tokenize($range, $parts);
+
+ if (size($parts) != 3)
+ {
+ warning("Shave: invalid syntax in line " + $lineNumber + " of " + $db);
+ }
+ else
+ {
+ $minVersions[size($minVersions)] = $parts[0];
+ $maxVersions[size($maxVersions)] = $parts[1];
+ $buildVersions[size($buildVersions)] = $parts[2];
+ }
+ }
+ }
+
+ fclose $fid;
+
+ if (size($minVersions) == 0)
+ {
+ $error[0] = "Shave " + $shaveVersion + " does not support " + $product
+ + " in Maya " + $gShaveMayaVersionStr + ".";
+ $error[1] = "Please contact [email protected] to find out if or when such support will be available.";
+ }
+ else
+ {
+ // Check whether any of the entries includes $version.
+ //
+ int $i;
+
+ for ($i = 0; $i < size($minVersions); $i++)
+ {
+ if ((eval($cmpProc + " \"" + $version + "\" \"" + $minVersions[$i] + "\"") >=0)
+ && (eval($cmpProc + " \"" + $version + "\" \"" + $maxVersions[$i] + "\"") <=0))
+ {
+ return 1;
+ }
+ }
+
+ // $version was not found. Return an error message listing the
+ // supported versions.
+ //
+ $error[0] = "Shave " + $shaveVersion + " does not support " + $product + " version " + $version + " in Maya " + $gShaveMayaVersionStr + ".";
+
+ string $versionList;
+
+ for ($i = 0; $i < size($minVersions); $i++)
+ {
+ if ($i > 0)
+ {
+ $versionList += ",";
+ }
+
+ // Strip any trailing /text from version strings.
+ //
+ string $minVersion = match("^[^/]*", $minVersions[$i]);
+ string $maxVersion = match("^[^/]*", $maxVersions[$i]);
+
+ if ($minVersion == $maxVersion)
+ {
+ $versionList += " " + $minVersion;
+ }
+ else
+ {
+ $versionList += " " + $minVersion + " to " + $maxVersion;
+ }
+ }
+
+ $error[1] = "Supported versions are:" + $versionList;
+ }
+
+ return 0;
+}
diff --git a/scripts/shaveCursorCtxCommonProperties.mel b/scripts/shaveCursorCtxCommonProperties.mel
new file mode 100644
index 0000000..1999550
--- /dev/null
+++ b/scripts/shaveCursorCtxCommonProperties.mel
@@ -0,0 +1,90 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveCursorCtxCommonProperties_fileVersion = "$Revision$";
+
+
+//
+// This is a common properties script for all shaveCursorCtx-based tools.
+//
+global proc shaveCursorCtxCommonProperties(string $toolName, string $ctxCmd)
+{
+ string $parent = `toolPropertyWindow -q -location`;
+ setParent $parent;
+
+ setUITemplate -pushTemplate OptionsTemplate;
+
+ if (`columnLayout -q -exists shaveCursor`) deleteUI shaveCursor;
+
+ columnLayout shaveCursor;
+ {
+ frameLayout -collapsable true -collapse false
+ -l $toolName shaveCursorFrame;
+ {
+ columnLayout;
+ {
+ floatSliderGrp -field true -l "Size"
+ -ann "Brush size, relative to size of current view"
+ -min 0.0 -max 1.0
+ -cc ($ctxCmd + " -e -brushSize #1 `currentCtx`")
+ shaveCursorSize;
+
+ floatSliderGrp -field true -l "Strength"
+ -ann "Brush strength"
+ -min 0.0 -max 1.0
+ -cc ($ctxCmd + " -e -brushStren #1 `currentCtx`")
+ shaveCursorStren;
+
+ checkBoxGrp -ncb 1 -l "Enable Falloff"
+ -cc ($ctxCmd + " -e -enableFalloff #1 `currentCtx`")
+ shaveCursorEnableFalloff;
+
+ //
+ // Tool-specific controls.
+ //
+ if ($toolName == "Brush")
+ {
+ separator -h 5 -style "none";
+
+ rowColumnLayout -nc 7 -cs 2 5
+ -cw 1 127 -cw 2 35 -cw 3 35 -cw 4 35
+ -cw 5 35 -cw 6 35 -cw 7 35;
+ {
+ text -al "right" -l "Mode"
+ -ann "Brush modes" shaveBrushMode;
+
+ symbolCheckBox -w 35 -h 36 -i "shaveTranslate.xpm"
+ -ann "Translate" -cc shaveBrushTranslateMode
+ shaveBrushTransBox;
+ symbolCheckBox -w 35 -h 36 -i "shaveScale.xpm"
+ -ann "Scale" -cc shaveBrushScaleMode
+ shaveBrushScaleBox;
+ symbolCheckBox -w 35 -h 36 -i "shaveRotate.xpm"
+ -ann "Rotate about cursor" -cc shaveBrushRotateMode
+ shaveBrushRotCursorBox;
+ symbolCheckBox -w 35 -h 36 -i "shaveStand.xpm"
+ -ann "Stand hairs on end" -cc shaveBrushStandMode
+ shaveBrushStandBox;
+ symbolCheckBox -w 35 -h 36 -i "shavePuffRoots.xpm"
+ -ann "Puff hairs out" -cc shaveBrushPuffMode
+ shaveBrushPuffBox;
+ symbolCheckBox -w 35 -h 36 -i "shaveClump.xpm"
+ -ann "Clump" -cc shaveBrushClumpMode
+ shaveBrushClumpBox;
+ setParent ..;
+ }
+ }
+
+ setParent ..;
+ }
+
+ setParent ..;
+ }
+
+ setParent ..;
+ }
+
+ setUITemplate -popTemplate;
+}
+
diff --git a/scripts/shaveCursorCtxCommonValues.mel b/scripts/shaveCursorCtxCommonValues.mel
new file mode 100644
index 0000000..9e3cba5
--- /dev/null
+++ b/scripts/shaveCursorCtxCommonValues.mel
@@ -0,0 +1,49 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveCursorCtxCommonValues_fileVersion = "$Revision$";
+
+
+//
+// This is a common values script for all shaveCursorCtx-based tools.
+//
+global proc shaveCursorCtxCommonValues(string $toolName)
+{
+ toolPropertySetCommon($toolName, "", "");
+ shaveCursorCtx_updateCommonPropertySheet();
+ toolPropertySelect shaveCursor;
+}
+
+
+global proc shaveCursorCtx_updateCommonPropertySheet()
+{
+ string $parent = (`toolPropertyWindow -q -location` + "|shaveCursor");
+
+ if (`layout -q -exists $parent`)
+ {
+ setParent $parent;
+
+ string $ctx = `currentCtx`;
+ string $class = `contextInfo -q -c $ctx`;
+ string $ctxCmd = $class + "Ctx";
+
+ if (`floatSliderGrp -exists shaveCursorSize`)
+ {
+ float $size = eval($ctxCmd + " -q -brushSize " + $ctx);
+ floatSliderGrp -e -v $size shaveCursorSize;
+ }
+
+ if (`floatSliderGrp -exists shaveCursorStren`)
+ {
+ float $s = eval($ctxCmd + " -q -brushStren " + $ctx);
+ floatSliderGrp -e -v $s shaveCursorStren;
+ }
+
+ if (`checkBoxGrp -exists shaveCursorEnableFalloff`)
+ {
+ int $enable = eval($ctxCmd + " -q -enableFalloff " + $ctx);
+ checkBoxGrp -e -v1 $enable shaveCursorEnableFalloff;
+ }
+ }
+}
diff --git a/scripts/shaveCutProperties.mel b/scripts/shaveCutProperties.mel
new file mode 100644
index 0000000..50cf151
--- /dev/null
+++ b/scripts/shaveCutProperties.mel
@@ -0,0 +1,12 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveCutProperties_fileVersion = "$Revision$";
+
+
+global proc shaveCutProperties()
+{
+ shaveCursorCtxCommonProperties("Cut", "shaveCutCtx");
+}
+
diff --git a/scripts/shaveCutValues.mel b/scripts/shaveCutValues.mel
new file mode 100644
index 0000000..cacf563
--- /dev/null
+++ b/scripts/shaveCutValues.mel
@@ -0,0 +1,12 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveCutValues_fileVersion = "$Revision$";
+
+
+global proc shaveCutValues(string $toolName)
+{
+ shaveCursorCtxCommonValues($toolName);
+}
+
diff --git a/scripts/shaveDiag.mel b/scripts/shaveDiag.mel
new file mode 100644
index 0000000..5d5ba4d
--- /dev/null
+++ b/scripts/shaveDiag.mel
@@ -0,0 +1,1266 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+proc string mayaNumericVersion()
+{
+ string $version = `about -v`;
+
+ if ($version == "2016 Extension 2")
+ {
+ $version = "2016.5";
+ }
+
+ return $version;
+}
+
+
+proc addErr(string $err)
+{
+ global string $shaveDiag_errors[];
+
+ $shaveDiag_errors[size($shaveDiag_errors)] = $err;
+}
+
+
+proc dumpFile(string $file, string $description, string $indent)
+{
+ if (!`filetest -f $file`)
+ {
+ print($indent + "[Not found]\n");
+ addErr(
+ "There is no " + $description + " file (" + $file + ")."
+ );
+ }
+ else if (!`filetest -r $file`)
+ {
+ print($indent + "[Not readable]\n");
+ addErr(
+ "The " + $description + " file (" + $file
+ + ") exists but is not readable."
+ );
+ }
+ else if (!`filetest -s $file`)
+ {
+ print($indent + "[Empty]\n");
+ addErr(
+ "The " + $description + " file (" + $file + ") exists but is empty."
+ );
+ }
+ else
+ {
+ int $fid = fopen($file, "r");
+
+ if ($fid == 0)
+ {
+ print($indent + "[Cannot open]\n");
+ addErr(
+ "Could not read " + $description + " file (" + $file
+ + ") even though it exists and is marked readable."
+ );
+ }
+ else
+ {
+ string $line;
+
+ print("\n");
+
+ while (($line = fgetline($fid)) != "")
+ {
+ int $len = size($line);
+
+ //
+ // Strip off trailing EOL chars.
+ //
+ while (($len > 0)
+ && ((substring($line, $len, $len) == "\n")
+ || (substring($line, $len, $len) == "\r")))
+ {
+ if ($len > 1)
+ $line = substring($line, 1, $len-1);
+ else
+ $line = "";
+
+ $len--;
+ }
+
+ print($indent + "'" + $line + "'\n");
+ }
+
+ fclose($fid);
+ }
+ }
+}
+
+
+proc string[] dumpPath(string $pathVar)
+{
+ string $path = getenv($pathVar);
+ string $paths[];
+ int $i;
+
+ print($pathVar + " (raw): '" + $path + "'\n");
+ print($pathVar + " (parsed):\n");
+
+ if (`about -nt`)
+ tokenize($path, ";", $paths);
+ else
+ tokenize($path, ":", $paths);
+
+ int $numPaths = size($paths);
+ int $foundPath = false;
+ string $validPaths[];
+ int $numValidPaths = 0;
+
+ for ($i = 0; $i < $numPaths; $i++)
+ {
+ //
+ // Make sure that the path actually contains something other than
+ // whitespace.
+ //
+ if (match("[^ \t]", $paths[$i]) != "")
+ {
+ $foundPath = true;
+
+ if (`filetest -d $paths[$i]`)
+ {
+ print(" " + $paths[$i] + "\t[Readable]\n");
+ $validPaths[$numValidPaths++] = $paths[$i];
+ }
+ else
+ {
+ print(" " + $paths[$i] + "\t[Not Readable]\n");
+ }
+ }
+ }
+
+ if (!$foundPath) print(" [Empty]\n");
+
+ return $validPaths;
+}
+
+
+proc int dumpPluginNodes(string $nodeType, string $expectedVersion)
+{
+ string $nodes[];
+ string $node;
+ int $nodeVersion;
+ int $wrongVersion = false;
+
+ if (`pluginInfo -q -loaded shaveNode`) $nodes = `ls -type $nodeType`;
+
+ if (size($nodes) == 0)
+ print(" [None]\n");
+ else
+ {
+ for ($node in $nodes)
+ {
+ $nodeVersion = getAttr($node + ".nodeVersion");
+ print(" " + $node + " -- node version is " + $nodeVersion + "\n");
+
+ if (($expectedVersion != "") && ($nodeVersion != $expectedVersion))
+ $wrongVersion = true;
+ }
+ }
+
+ if ($wrongVersion)
+ {
+ addErr(
+ "One or more " + $nodeType + " nodes did not have the"
+ + " correct version (" + $expectedVersion + ")."
+ );
+ }
+
+ return size($nodes);
+}
+
+
+// It is only valid to call this when the shaveNode plugin is loaded.
+// E.g. after shaveDiag_post() has begun execution.
+//
+proc int getVersionsFromDB(string $dbFile, string $mayaVersion, string $minVersions[], string $maxVersions[], string $buildVersions[])
+{
+ clear $minVersions;
+ clear $maxVersions;
+ clear $buildVersions;
+
+ string $dbDir;
+
+ if (`about -mac`)
+ {
+ $dbDir = "/Users/Shared/Epic Games/shaveHaircut/maya" + $mayaVersion;
+ }
+ else
+ {
+ $dbDir = dirname(dirname(`pluginInfo -q -path shaveNode`));
+ }
+
+ string $db = $dbDir + "/" + $dbFile;
+
+ $fid = `fopen $db "r"`;
+
+ if ($fid == 0)
+ {
+ addErr("Could not read Shave support file '" + $db + "'.");
+ return false;
+ }
+
+ string $key = $mayaVersion + ":";
+ int $lineNumber = 0;
+
+ while (!`feof $fid`)
+ {
+ string $line = strip(`fgetline $fid`);
+ $lineNumber++;
+
+ if (startsWith($line, $key))
+ {
+ string $range = strip(substring($line, size($key)+1, size($line)));
+ string $parts[];
+ tokenize($range, $parts);
+
+ if (size($parts) != 3)
+ {
+ addErr("Invalid syntax in line " + $lineNumber + " of " + $db + ".");
+ }
+ else
+ {
+ $minVersions[size($minVersions)] = $parts[0];
+ $maxVersions[size($maxVersions)] = $parts[1];
+ $buildVersions[size($buildVersions)] = $parts[2];
+ }
+ }
+ }
+
+ fclose $fid;
+
+ return true;
+}
+
+
+proc int getWinRegKey(string $key, string $result[])
+{
+ clear $result;
+
+ string $cmd = "reg query \"" + $key + "\"";
+ string $str = system($cmd);
+
+ // The result should consist of two lines of header output, followed by
+ // zero or more lines of results. The lines are separated by CRLFs, so
+ // split them out into an array.
+ //
+ string $lines[];
+
+ tokenize($str, "\r\n", $lines);
+
+ if (size($lines) < 1) return false;
+
+ // If the first line begins with 'Error:' then the lookup failed.
+ //
+ if ((size($lines[0]) > 5) && (substring($lines[0], 1, 6) == "Error:"))
+ return false;
+
+ int $i;
+
+ for ($i = 0; $i < size($lines); $i++)
+ $result[$i] = $lines[$i];
+
+ return true;
+}
+
+
+proc int getWinRegValue(string $key, string $value, string $result[])
+{
+ clear $result;
+
+ string $cmd = "reg query \"" + $key + "\"";
+
+ if ($value == "")
+ $cmd += " /ve";
+ else
+ $cmd += " /v \"" + $value + "\"";
+
+ string $str = system($cmd);
+
+ // The result should consist of one or more lines of output, separated by
+ // CRLFs, so split them out into an array.
+ //
+ string $lines[];
+
+ tokenize($str, "\r\n", $lines);
+
+ // If there are not exactly two lines, then the lookup failed.
+ //
+ if (size($lines) != 2) return false;
+
+ // If the first line begins with 'Error:' then the lookup failed.
+ //
+ if ((size($lines[0]) > 5) && (substring($lines[0], 1, 6) == "Error:"))
+ return false;
+
+ // The second line should contain three whitespace separated fields, so
+ // split them out.
+ //
+ string $fields[];
+
+ tokenize($lines[1], "\t ", $fields);
+
+ // If there are fewer than 3 fields, the value was empty.
+ //
+ if (size($fields) < 3)
+ {
+ $result[0] = "";
+
+ if (size($fields) == 2)
+ $result[1] = $fields[1];
+ else
+ $result[1] = "";
+
+ return true;
+ }
+
+ // We return the type of the value in the second array element.
+ //
+ $result[1] = $fields[1];
+
+ // If there are precisely 3 fields, then the third one contains the
+ // desired result and the second one the type of the result.
+ //
+ if (size($fields) == 3)
+ {
+ $result[0] = $fields[2];
+ return true;
+ }
+
+ // We have more than 3 fields which means that the result contained
+ // whitespace and was treated as multiple fields by 'tokenize'. So we have
+ // to parse the result out of the original string.
+ //
+ string $pattern = "^[\t ]*" + $fields[0] + "[\t ]*" + $fields[1] + "[\t ]*";
+ string $prefix = match($pattern, $lines[1]);
+
+ if ($prefix == "")
+ {
+ // Oh crap. The pattern didn't match. Let's bail.
+ //
+ error("Error looking key '" + $key + "', value '" + $value + "': raw result was '" + $str + "' but we couldn't parse it.\n");
+ }
+
+ if (size($prefix) >= size($lines[1]))
+ {
+ $result[0] = "";
+ return true;
+ }
+
+ $result[0] = substring($lines[1], size($prefix)+1, size($lines[1]));
+
+ return true;
+}
+
+
+proc int[] dumpMayaRegistry()
+{
+ int $foundAdsk = false;
+ int $foundInstall = false;
+ int $foundMayaVersion = false;
+ int $foundModules = false;
+ string $key = "HKLM\\Software\\Autodesk";
+ string $mayaVersion = mayaNumericVersion();
+ string $reg[];
+
+ if (!getWinRegKey($key, $reg))
+ print(" " + $key + " NOT FOUND\n");
+ else
+ {
+ $foundAdsk = true;
+ $key = "HKLM\\Software\\Autodesk\\Maya";
+
+ if (!getWinRegKey($key, $reg))
+ print(" " + $key + " NOT FOUND\n");
+ else
+ {
+ $key += "\\" + $mayaVersion;
+
+ if (!getWinRegKey($key, $reg))
+ print(" " + $key + " NOT FOUND\n");
+ else
+ {
+ $foundMayaVersion = true;
+ $key += "\\Setup";
+
+ if (!getWinRegKey($key, $reg))
+ print(" " + $key + " NOT FOUND\n");
+ else
+ {
+ $key += "\\InstallPath";
+
+ if (!getWinRegKey($key, $reg))
+ print(" " + $key + " NOT FOUND\n");
+ else if (!getWinRegValue($key, "MAYA_INSTALL_LOCATION", $reg))
+ print(" " + $key + "(MAYA_INSTALL_LOCATION) NOT FOUND\n");
+ else
+ {
+ $foundInstall = true;
+ print(" " + $key + "(MAYA_INSTALL_LOCATION) = '" + $reg[0] + "'\n");
+ }
+ }
+ }
+ }
+ }
+
+ int $result[];
+
+ $result[0] = $foundAdsk;
+ $result[1] = $foundMayaVersion;
+ $result[2] = $foundInstall;
+
+ return $result;
+}
+
+
+proc renderers()
+{
+ print("\nRenderer Support:\n");
+
+ string $dbDir = dirname(dirname(`pluginInfo -q -path shaveNode`));
+ string $mayaVersion = mayaNumericVersion();
+ string $minVersions[], $maxVersions[], $buildVersions[];
+
+ // Arnold
+ //
+ print("\n Arnold, ");
+
+ string $loadedVersion;
+
+ if (`pluginInfo -q -l mtoa`)
+ {
+ $loadedVersion = `pluginInfo -q -v mtoa`;
+
+ if ($loadedVersion == "")
+ {
+ addErr("Could not get version of loaded mtoa plug-in.");
+ print("plug-in loaded, version not known:\n");
+ }
+ else
+ {
+ string $arnoldVersion;
+
+ if (catchQuiet($arnoldVersion = python("arnold.AiGetVersionString()")))
+ {
+ addErr("Could not determine Arnold version for loaded mtoa plugin.");
+ $arnoldVersion = "unknown";
+ }
+
+ print("mtoa version " + $loadedVersion + " (Arnold version " + $arnoldVersion + ") loaded:\n");
+ }
+ }
+ else
+ {
+ print("plug-in not currently loaded:\n");
+ }
+
+ if (getVersionsFromDB("supportedMtoAVersions.txt", $mayaVersion, $minVersions, $maxVersions, $buildVersions))
+ {
+ if (size($minVersions) == 0)
+ {
+ addErr("No Arnold support found for this version of Maya.");
+ print(" No support found for this version of Maya.\n");
+ }
+ else
+ {
+ for ($i = 0; $i < size($minVersions); ++$i)
+ {
+ string $parts[];
+ tokenize($buildVersions[$i], "/", $parts);
+
+ if (size($parts) != 2)
+ {
+ addErr("Improperly formatted mtoa/arnold version entry in supportedMtoAVersions.txt.");
+ print(" Cannot parse version info.\n");
+ }
+ else
+ {
+ print(" Support found for mtoa " + $parts[0] + "/Arnold " + $parts[1] + ", ");
+
+ if ($maxVersions[$i] == $minVersions[$i])
+ {
+ print("good only for " + $minVersions[$i] + "\n");
+ }
+ else
+ {
+ print("should be good for " + $minVersions[$i] + " through " + $maxVersions[$i] + "\n");
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ print(" Could not determine version support.\n");
+ }
+
+ // RenderMan
+ //
+ print("\n RenderMan, ");
+
+ $loadedVersion = "";
+
+ if (`pluginInfo -q -l RenderMan_for_Maya`)
+ {
+ // Older versions of RenderMan provided an 'rman' command. Let's
+ // see if we have that.
+ //
+ if (exists("rman"))
+ {
+ catchQuiet($loadedVersion = `rman getversion prman`);
+
+ // Strip off any leading text.
+ //
+ $loadedVersion = match("[0-9].*$", $loadedVersion);
+ }
+ else
+ {
+ // If this is a newer version we can get the version from its
+ // Python config object.
+ //
+ if (!catchQuiet(python("import rfm2.config")))
+ {
+ catchQuiet($loadedVersion = python("rfm2.config.config.build_info.version()"));
+ }
+
+ // As a last resort we'll look at the version from the module
+ // file. This is unreliable since Pixar sometimes requires
+ // users to create the module file by hand.
+ //
+ if ($loadedVersion == "")
+ {
+ $loadedVersion = `pluginInfo -q -v RenderMan_for_Maya`;
+ }
+ }
+
+ if ($loadedVersion == "")
+ {
+ addErr("Could not get version of loaded RenderMan_for_Maya plug-in.");
+ print("plug-in loaded, version not known:\n");
+ }
+ else
+ {
+ print("version " + $loadedVersion + " loaded:\n");
+ }
+ }
+ else
+ {
+ print("plug-in not currently loaded:\n");
+ }
+
+ if (getVersionsFromDB("supportedRManVersions.txt", $mayaVersion, $minVersions, $maxVersions, $buildVersions))
+ {
+ if (size($minVersions) == 0)
+ {
+ addErr("No RenderMan support found for this version of Maya.");
+ print(" No support found for this version of Maya.\n");
+ }
+ else
+ {
+ for ($i = 0; $i < size($minVersions); ++$i)
+ {
+ print(" Support found for RenderMan " + $buildVersions[$i] + ", ");
+
+ if ($maxVersions[$i] == $minVersions[$i])
+ {
+ print("good only for " + $minVersions[$i] + "\n");
+ }
+ else
+ {
+ print("should be good for " + $minVersions[$i] + " through " + $maxVersions[$i] + "\n");
+ }
+ }
+ }
+ }
+ else
+ {
+ print(" Could not determine version support.\n");
+ }
+
+ // V-Ray
+ //
+ print("\n V-Ray, ");
+
+ $loadedVersion = "";
+
+ if (`pluginInfo -q -l vrayformaya`)
+ {
+ if (exists("vray"))
+ {
+ $loadedVersion = eval("vray version");
+ }
+
+ if ($loadedVersion == "")
+ {
+ addErr("Could not get version of loaded vrayformaya plug-in.");
+ print("plug-in loaded, version not known:\n");
+ }
+ else
+ {
+ print("version " + $loadedVersion + " loaded:\n");
+ }
+ }
+ else
+ {
+ print("plug-in not currently loaded:\n");
+ }
+
+ if (getVersionsFromDB("supportedVrayVersions.txt", $mayaVersion, $minVersions, $maxVersions, $buildVersions))
+ {
+ if (size($minVersions) == 0)
+ {
+ addErr("No V-Ray support found for this version of Maya.");
+ print(" No support found for this version of Maya.\n");
+ }
+ else
+ {
+ for ($i = 0; $i < size($minVersions); ++$i)
+ {
+ print(" Support found for V-Ray " + $buildVersions[$i] + ", ");
+
+ if ($maxVersions[$i] == $minVersions[$i])
+ {
+ print("good only for " + $minVersions[$i] + "\n");
+ }
+ else
+ {
+ print("should be good for " + $minVersions[$i] + " through " + $maxVersions[$i] + "\n");
+ }
+ }
+ }
+ }
+ else
+ {
+ print(" Could not determine version support.\n");
+ }
+}
+
+
+proc showFilePaths(string $pathVariable, string $paths[], string $file)
+{
+ int $i;
+ int $numPaths = size($paths);
+ int $numFound = 0;
+
+ print($file + ":");
+
+ for ($i = 0; $i < $numPaths; $i++)
+ {
+ string $filePath = fromNativePath($paths[$i]);
+ $filePath += "/" + $file;
+
+ if (`filetest -f $filePath`)
+ {
+ print("\n " + $filePath);
+
+ // Don't count this as a duplicate if it's just a second
+ // reference to the exact same file.
+ int $j;
+
+ for ($j = 0; $j < $i; ++$j)
+ if ($paths[$j] == $paths[$i]) break;
+
+ if ($j == $i) $numFound++;
+ }
+ }
+
+ if ($numFound == 0)
+ {
+ print("[Not found]");
+
+ if ($numPaths == 0)
+ addErr("There are no readable directories in " + $pathVariable + ".");
+ else
+ addErr("There is no '" + $file + "' file in " + $pathVariable + ".");
+ }
+ else if ($numFound > 1)
+ {
+ addErr(
+ "There are " + $numFound + " separate '" + $file + "' files"
+ + " in " + $pathVariable + "."
+ );
+ }
+
+ print("\n");
+}
+
+
+proc summary()
+{
+ global string $shaveDiag_errors[];
+
+ print("\nSummary\n");
+ print("=======\n");
+
+ if (size($shaveDiag_errors) == 0)
+ print("No obvious problems were detected.\n");
+ else
+ {
+ print(
+ "The following problems or potential problems were discovered:\n\n"
+ );
+
+ int $i;
+
+ for ($i = 0; $i < size($shaveDiag_errors); $i++)
+ print(($i+1) + ") " + $shaveDiag_errors[$i] + "\n");
+ }
+}
+
+
+global proc shaveDiag()
+{
+ global string $shaveDiag_errors[];
+ global string $shaveDiag_fileVersion;
+
+ clear $shaveDiag_errors;
+
+ int $i;
+
+ print("Shave Diagnostics\n");
+ print("====================\n");
+
+ // Maya Version
+ //
+ string $mayaVersion = mayaNumericVersion();
+
+ print("Maya version: " + $mayaVersion + " Cut: " + `about -c` + "\n\n");
+
+ int $mayaMajor = 0;
+ int $mayaMinor = 0;
+ string $temp[];
+
+ tokenize($mayaVersion, ".", $temp);
+
+ if (size($temp) == 0)
+ {
+ addErr("Maya version '" + $mayaVersion + "' is invalid");
+ }
+ else
+ {
+ $mayaMajor = $temp[0];
+
+ if (size($temp) > 1) $mayaMinor = $temp[1];
+ }
+
+ // Current Directory
+ //
+ string $cwd = pwd();
+
+ print("Current Working Directory: '" + $cwd + "'\n\n");
+
+ // Registry (Windows only)
+ //
+ if (`about -win`)
+ {
+ print("Registry:\n");
+
+ int $result[] = dumpMayaRegistry();
+ print("\n");
+
+ if (!$result[0])
+ {
+ addErr(
+ "Could not find registry entry for 'Autodesk'. Was Maya properly installed?"
+ );
+ }
+ else
+ {
+ if (!$result[1])
+ {
+ addErr(
+ "Could not find registry entry for this Maya version."
+ + " Was Maya properly installed?"
+ );
+ }
+ else if (!$result[2])
+ {
+ addErr(
+ "Could not find registry entry for 'MAYA_INSTALL_LOCATION'."
+ + " Was Maya properly installed?"
+ );
+ }
+ }
+ }
+
+ // System PATH
+ //
+ string $systemPaths[] = dumpPath("PATH");
+ int $numSystemPaths = size($systemPaths);
+
+ print("\n");
+
+ // libShave.dll (Windows only)
+ //
+ if (`about -win`)
+ {
+ showFilePaths("PATH", $systemPaths, "libShave.dll");
+ print("\n");
+ }
+
+ // LD_LIBRARY_PATH (Linux only)
+ //
+ if (`about -linux`)
+ {
+ string $libPaths[] = dumpPath("LD_LIBRARY_PATH");
+ int $numLibPaths = size($libPaths);
+
+ print("\n");
+
+ // libShave.so
+ //
+ showFilePaths("LD_LIBRARY_PATH", $libPaths, "libShave.so");
+
+ print("\n");
+ }
+
+ // MAYA_LOCATION
+ //
+ string $mayaLocationPaths[] = dumpPath("MAYA_LOCATION");
+ int $numMayaLocationPaths = size($mayaLocationPaths);
+
+ if ($numMayaLocationPaths == 0) {
+ addErr("MAYA_LOCATION is either undefined or points to an unreadable directory.");
+ } else if ($numMayaLocationPaths > 1) {
+ addErr("MAYA_LOCATION contains multiple paths.");
+ } else {
+ // libShave.dylib (OSX only)
+ //
+ string $libPath[] = { ($mayaLocationPaths[0] + "/MacOS") };
+ if (`about -mac`) {
+ showFilePaths("MAYA_LOCATION/MacOS", $libPath, "libShave.dylib");
+ print("\n");
+ }
+ }
+
+ // MAYA_MODULE_PATH
+ //
+ if (`about -mac`)
+ print("Shave module file: Not used on OSX\n");
+ else
+ {
+ string $modulePaths[] = dumpPath("MAYA_MODULE_PATH");
+ int $numModulePaths = size($modulePaths);
+ int $numFound = 0;
+
+ // Module File
+ //
+ print("\nShave module file: ");
+
+ for ($i = 0; $i < $numModulePaths; $i++)
+ {
+ string $filePath = fromNativePath($modulePaths[$i]);
+ $filePath += "/shaveHaircut.mod";
+
+ if (`filetest -f $filePath`)
+ {
+ print($filePath + " ");
+
+ dumpFile($filePath, "module", " ");
+
+ $numFound++;
+ }
+ }
+
+ if ($numFound == 0)
+ {
+ print("[Not found]\n\n");
+
+ if ($numModulePaths == 0)
+ addErr("There are no readable directories in MAYA_MODULE_PATH.");
+ else
+ {
+ addErr(
+ "There is no module file (shaveHaircut.mod) in"
+ + " MAYA_MODULE_PATH."
+ );
+ }
+ }
+ else if ($numFound > 1)
+ {
+ addErr(
+ "There are " + $numFound + " separate module files"
+ + " (shaveHaircut.mod) in MAYA_MODULE_PATH."
+ );
+ }
+
+ print("\n");
+ }
+
+ // MAYA_SCRIPT_PATH
+ //
+ string $scriptPaths[] = dumpPath("MAYA_SCRIPT_PATH");
+ int $numScriptPaths = size($scriptPaths);
+
+ print("\n");
+
+ // Script Files
+ //
+ if ($numScriptPaths > 0)
+ {
+ print("Script files:\n");
+
+ string $scriptFiles[] = {
+ "AEshaveGlobalsTemplate.mel",
+ "AEshaveHairTemplate.mel",
+ "AEshaveNodeTemplate.mel",
+ "shaveAEOverrides.mel",
+ "shaveBrush.mel",
+ "shaveBrushProperties.mel",
+ "shaveBrushSetModeButton.mel",
+ "shaveBrushValues.mel",
+ "shaveCheckVersion.mel",
+ "shaveCursorCtxCommonCtls.mel",
+ "shaveCutProperties.mel",
+ "shaveCutValues.mel",
+ "shaveDiag.mel",
+ "shavePresetWin.mel",
+ "shaveRelationshipEditor.mel",
+ "shaveRenderman.mel",
+ "shaveRunTimeCommands.mel",
+ "shaveShelf.mel",
+ "shaveUI.mel",
+ "shaveVersion.mel",
+ "shaveVrayPostRender.mel",
+ "shaveVrayPreRender.mel"
+ };
+
+ int $numScriptFiles = size($scriptFiles);
+ int $p;
+
+ for ($i = 0; $i < $numScriptFiles; $i++)
+ {
+ $numFound = 0;
+
+ for ($p = 0; $p < $numScriptPaths; $p++)
+ {
+ string $filePath = fromNativePath($scriptPaths[$p]);
+ $filePath += "/" + $scriptFiles[$i];
+
+ if (`filetest -f $filePath`)
+ {
+ // If this is Maya's version of setFilterScript.mel,
+ // then ignore it.
+ //
+ if (size(match("/scripts/startup/setFilterScript.mel", $filePath)) > 0) continue;
+
+ $numFound++;
+
+ print(" " + $filePath + "\n");
+ }
+ }
+
+ if ($numFound == 0)
+ {
+ if ($scriptFiles[$i] != "setFilterScript.mel")
+ {
+ print(" [None found]\n");
+
+ addErr(
+ "Script file '" + $scriptFiles[$i]
+ + "' was not found anywhere in MAYA_SCRIPT_PATH."
+ );
+ }
+ }
+ else if ($numFound > 1)
+ {
+ addErr(
+ $numFound + " separate copies of '"
+ + $scriptFiles[$i] + " were found in MAYA_SCRIPT_PATH."
+ );
+ }
+ }
+
+ print("\n");
+ }
+ else
+ {
+ addErr("There are no readable directories in MAYA_SCRIPT_PATH.");
+ }
+
+ // MAYA_PLUG_IN_PATH
+ //
+ string $pluginPaths[] = dumpPath("MAYA_PLUG_IN_PATH");
+ int $numPluginPaths = size($pluginPaths);
+
+ if ($numPluginPaths == 0)
+ addErr("There are no readable directories in MAYA_PLUG_IN_PATH.");
+
+ print("\n");
+
+ // Shave Plugin
+ //
+ string $plugin = "";
+
+ if (`about -linux`)
+ $plugin = "shaveNode.so";
+ else if (`about -mac`)
+ {
+ if (`about -version` == "7.0")
+ $plugin = "shaveNode.lib";
+ else
+ $plugin = "shaveNode.bundle";
+ }
+ else if (`about -win` || `about -nt`)
+ $plugin = "shaveNode.mll";
+
+ if ($plugin != "")
+ {
+ print("Main plugin file (" + $plugin + "):\n");
+
+ $numFound = 0;
+
+ for ($i = 0; $i < $numPluginPaths; $i++)
+ {
+ if (`filetest -f ($pluginPaths[$i] + "/" + $plugin)`)
+ {
+ $numFound++;
+
+ print(" " + $pluginPaths[$i] + "/" + $plugin + "\n");
+
+ if (!`filetest -r ($pluginPaths[$i] + "/" + $plugin)`)
+ {
+ addErr(
+ "The main Shave plugin file (" + $pluginPaths[$i]
+ + "/" + $plugin + ") exists but is not readable."
+ );
+ }
+ }
+ }
+
+ if ($numFound == 0)
+ {
+ addErr(
+ "The main Shave plugin file (" + $plugin
+ + ") was not found in MAYA_PLUG_IN_PATH."
+ );
+ }
+ else if ($numFound > 1)
+ {
+ addErr(
+ $numFound + " different copies of the main Shave plugin file ("
+ + $plugin + ") were found in MAYA_PLUG_IN_PATH."
+ );
+ }
+
+ // On Linux Maya cannot find the plugin if there is a copy of it in
+ // the current directory.
+ //
+ if (`about -linux` && `filetest -r ($cwd + "/shaveNode.so")`)
+ {
+ addErr(
+ "There is a copy of shaveNode.so in the current directory"
+ + " (" + $cwd + ")."
+ );
+ }
+
+ print("\n");
+ }
+
+ // Loaded Plugins
+ //
+ print("Plugins currently loaded:\n");
+
+ string $plugins[] = `pluginInfo -q -listPlugins`;
+ int $numPlugins = size($plugins);
+
+ if ($numPlugins == 0)
+ print(" [None]\n");
+ else
+ {
+ for ($i = 0; $i < $numPlugins; $i++)
+ {
+ print(" " + $plugins[$i]);
+
+ if (`pluginInfo -q -autoload $plugins[$i]`) print(" [autoload]");
+
+ print("\n");
+ }
+ }
+
+ print("\n");
+
+ // Operating System
+ //
+ if (($mayaMajor > 4) || (($mayaMajor == 4) && ($mayaMinor == 5)))
+ print("Operating System: " + `about -osv` + "\n");
+ else
+ print("Operating System: " + `about -os` + "\n");
+
+ print("\n");
+
+ // Shave Version (from scripts)
+ //
+ string $version = "[Not available]";
+
+ if (exists("shaveVersion")) $version = shaveVersion();
+
+ print("\nShave version, according to scripts: " + $version + "\n");
+
+ // Load Plugin
+ //
+ int $pluginLoaded = `pluginInfo -q -loaded shaveNode`;
+
+ if (!$pluginLoaded)
+ {
+ // We used to try loading the plugin ourselves, but if there was a
+ // problem with Shave crashing Maya then the user would never get a
+ // chance to copy the shaveDiag output. So now we simply ask
+ // the user to rerun with the plugin loaded.
+ //
+ addErr("Shave plugin is not loaded. Try loading the plugin then rerunning shaveDiag.");
+ summary();
+ }
+ else
+ {
+ // We may have just loaded the Shave plugin, in which case any
+ // plugin commands we use will not yet be valid. So we must defer
+ // the remainder of the diagnostics until the load has a chance to
+ // complete.
+ //
+ evalDeferred("shaveDiag_post");
+ }
+}
+
+
+global proc shaveDiag_post()
+{
+ // Shave Version (from plugin)
+ //
+ string $version = "[Not available]";
+
+ if (exists("shaveInfo"))
+ {
+ $version = eval("shaveInfo -v");
+
+ if (exists("shaveVersion"))
+ {
+ string $versionFromScript = shaveVersion();
+
+ if ($versionFromScript != $version)
+ {
+ addErr(
+ "Shave version from script file (" + $versionFromScript
+ + ") does not match that from plugin (" + $version + ")."
+ );
+ }
+ }
+ }
+
+ print("Shave version, according to plugin: " + $version + "\n\n");
+
+ string $sceneName = `file -q -sceneName`;
+
+ if ($sceneName == "") $sceneName = "untitled";
+
+ print("Current scene: '" + $sceneName + "'\n\n");
+
+ if (exists("shaveInfo"))
+ $version = eval("shaveInfo -shv");
+ else
+ $version = "";
+
+ print("Scene contains the following shaveHair nodes:\n");
+ int $numShaveNodes = dumpPluginNodes("shaveHair", $version);
+
+ print("shaveInfo says current shaveHair version should be ");
+
+ if ($version != "")
+ print($version + "\n");
+ else
+ print("[Not available]\n");
+
+ if (exists("shaveInfo"))
+ $version = eval("shaveInfo -gv");
+ else
+ $version = "";
+
+ print("\nScene contains the following shaveGlobals nodes:\n");
+ int $numShaveGlobals = dumpPluginNodes("shaveGlobals", $version);
+
+ print("shaveInfo says current shaveGlobals version should be ");
+
+ if ($version != "")
+ print($version + "\n");
+ else
+ print("[Not available]\n");
+
+ if ($numShaveGlobals > 1)
+ {
+ addErr(
+ "Scene contains " + $numShaveGlobals
+ + " different shaveGlobals nodes."
+ );
+ }
+ else if (($numShaveGlobals == 0) && ($numShaveNodes > 0))
+ {
+ addErr("Scene contains shaveHair nodes but no shaveGlobals node.");
+ }
+
+ print("\n");
+
+ int $missingShaveUIAlreadyNoted = false;
+
+ if (exists("shaveGetTempDir"))
+ {
+ string $dir = shaveGetTempDir();
+
+ print("Directory for Shave GUI temp files: '" + $dir + "' ");
+
+ if (($dir == "") || !`filetest -w $dir`)
+ {
+ print("[Not writable]\n");
+
+ addErr(
+ "Path to temp files for Shave GUI is not writable. " +
+ "GUI may not work."
+ );
+ }
+ else
+ print("[Writable]\n");
+ }
+ else
+ {
+ print("Cannot find shaveGetTempDir() procedure.\n");
+ addErr("shaveUI.mel is either missing or is a very old version.");
+ $missingShaveUIAlreadyNoted = true;
+ }
+
+ if (exists("shaveGetStatDir"))
+ {
+ string $dir = shaveGetStatDir();
+
+ print("Directory for Shave stat files '" + $dir + "' ");
+
+ if (($dir == "") || !`filetest -w $dir`)
+ {
+ print("[Not writable]\n");
+ addErr("Path to stat files for Shave dynamics is not writable.");
+ }
+ else if (!`filetest -r $dir`)
+ {
+ print("[Not readable]\n");
+ addErr("Path to stat files for Shave dynamics is not readable.");
+ }
+ else
+ print("[Writable]\n");
+ }
+ else
+ {
+ print("Cannot find shaveGetStatDir() procedure.\n");
+
+ if (!$missingShaveUIAlreadyNoted)
+ addErr("shaveUI.mel is either missing or is a very old version.");
+ }
+
+ // Renderer Support
+ //
+ renderers();
+
+ summary();
+}
diff --git a/scripts/shavePresetWin.mel b/scripts/shavePresetWin.mel
new file mode 100644
index 0000000..3f12e59
--- /dev/null
+++ b/scripts/shavePresetWin.mel
@@ -0,0 +1,456 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shavePresetWin_fileVersion = "$Revision$";
+
+// global float $gAEAttrPresetBlend contains blend fraction
+// global string $gAEAttrPresetCurrentTarget contains name of target node
+//
+// Then just execute the .mel file.
+//
+
+proc string getShavePresetDir(string $basePath)
+{
+ string $paths[];
+
+ // 'basePath' may be a list of paths, so split them apart.
+ if (`about -win`)
+ tokenize $basePath ";" $paths;
+ else
+ tokenize $basePath ":" $paths;
+
+ // Look for a path which contains some shaveHair preset files.
+ string $path;
+ for ($path in $paths)
+ {
+ $path += "/attrPresets/shaveHair/";
+ string $files[] = `getFileList -folder $path -fs "*.mel"`;
+
+ if (size($files) > 0) return $path;
+ }
+
+ return "";
+}
+
+
+proc string getSwatchName(string $preset, string $tabName)
+{
+ //
+ // Maya's icon caching ignores the directory portion of the icon's
+ // pathname, so we have to ensure that icons, even in different
+ // directories, have different names.
+ //
+ // We do this by inserting the first char of the tab name into the
+ // icon's file name, so make sure that your tab names are distinct in
+ // the first char.
+ //
+ return ($preset + "_" + substring($tabName, 1, 1) + "_swatch.xpm");
+}
+
+
+proc int isAProcedure(string $variable)
+{
+ int $length = size($variable);
+ if ($length < 2)
+ {
+ // Too short to have "()" in it.
+ //
+ return false;
+ }
+
+ int $secondLast = $length -1;
+ if (`substring $variable $secondLast $length` == "()")
+ {
+ return true;
+ }
+ return false;
+}
+
+
+global proc shavePreset_doCallback(string $preset)
+{
+ global string $shavePreset_callbackCmd;
+
+ if ($shavePreset_callbackCmd != "")
+ evalEcho($shavePreset_callbackCmd + " \"" + $preset + "\"");
+
+if (`window -exists shavePresetWindow`) {
+ // Maya bug: The button that the user clicked on still has some
+ // outstanding internal events. If we destroy it here then those
+ // events will be referencing a deleted button and Maya may crash.
+ // So we put the destruction of the window onto the idle queue so
+ // that it won't happen until after the button's events have
+ // finished up.
+ evalDeferred("deleteUI shavePresetWindow");
+ }
+}
+
+
+proc int createTab(string $tabName, string $dir)
+{
+ string $presets[] = `getFileList -folder $dir -fs "*.mel"`;
+ int $numPresets = size($presets);
+ string $scrollLayout = "shave" + $tabName + "Presets";
+ string $gridLayout = $scrollLayout + "Grid";
+
+ //
+ // Create a grid layout capable of holding all the presets and add it
+ // to the window's tab layout.
+ //
+ scrollLayout -p shavePresetTabs $scrollLayout;
+ tabLayout -e -tabLabel $scrollLayout $tabName shavePresetTabs;
+
+ if ($numPresets > 0)
+ {
+ gridLayout -nc 5 -cwh 100 120 -autoGrow true -p $scrollLayout $gridLayout;
+
+ //
+ // Find the default swatch, just in case we need it.
+ //
+ string $searchPath = getenv("XBMLANGPATH");
+ string $searchDirs[];
+
+ if (`about -nt`)
+ tokenize($searchPath, "%B;", $searchDirs);
+ else
+ tokenize($searchPath, "%B:", $searchDirs);
+
+ string $defaultSwatch = searchPathArray(
+ "shaveDefaultSwatch.xpm", $searchDirs
+ );
+
+ //
+ // Step through each preset and add a button for it to the row layout.
+ //
+ string $preset;
+
+ for ($preset in $presets)
+ {
+ $preset = basename($preset, ".mel");
+
+ //
+ // If we can't find a swatch specific to this preset, then use
+ // the default.
+ //
+ string $icon = $dir + getSwatchName($preset, $tabName);
+
+ if (!`filetest -r $icon`) $icon = $defaultSwatch;
+
+ //
+ // Create the button.
+ //
+ iconTextButton -p $gridLayout
+ -st "iconAndTextVertical" -l $preset
+ -width 100 -height 120 -i1 $icon
+ -c ("shavePreset_doCallback(\"" + $dir + $preset + ".mel\")");
+ }
+ }
+ else
+ {
+ gridLayout -p $scrollLayout $gridLayout;
+ }
+
+ return $numPresets;
+}
+
+
+global proc shavePresetWin(string $cmd)
+{
+ global string $shavePreset_callbackCmd;
+
+ $shavePreset_callbackCmd = $cmd;
+
+ if (`window -exists shavePresetWindow`) deleteUI shavePresetWindow;
+
+ //does not seem help
+ //if (`window -exists shavePresetWindow`) {
+ // // Maya bug: The button that the user clicked on still has some
+ // // outstanding internal events. If we destroy it here then those
+ // // events will be referencing a deleted button and Maya may crash.
+ // // So we put the destruction of the window onto the idle queue so
+ // // that it won't happen until after the button's events have
+ // // finished up.
+ // evalDeferred("deleteUI shavePresetWindow");
+ // }
+
+ window shavePresetWindow;
+
+ tabLayout -cc "shavePreset_tabChanged" shavePresetTabs;
+
+ string $dir;
+
+ //
+ // Has the user provided an override for the Shave presets?
+ //
+ $dir = getenv("SHAVE_COMMON_PRESET_PATH");
+
+ if ($dir == "")
+ {
+ //
+ // Okay, how about a generic override for the location of all
+ // Shave-related files?
+ //
+ $dir = getenv("SHAVE_LOCATION");
+
+ if ($dir != "")
+ {
+ //
+ // They might have overridden SHAVE_LOCATION for some reason
+ // other than presets, so let's make sure that the presets are
+ // actually there.
+ //
+ $dir = getShavePresetDir($dir + "/presets");
+ }
+
+ if ($dir == "")
+ {
+ // Check MAYA_PRESET_PATH
+ $dir = getShavePresetDir(getenv("MAYA_PRESET_PATH"));
+
+ if ($dir == "")
+ {
+ // No overrides, so go with Maya's default location.
+ $dir = getenv("MAYA_LOCATION") + "/presets/attrPresets/shaveHair/";
+ }
+ }
+ }
+ else
+ {
+ if (substring($dir, size($dir), size($dir)) != "/")
+ $dir += "/";
+ }
+
+ int $numCommonPresets = createTab("Common", $dir);
+
+ $dir = `internalVar -userPrefDir`;
+ $dir = substitute("prefs", $dir, "presets/attrPresets");
+ $dir += "shaveHair/";
+
+ int $numPersonalPresets = createTab("Personal", $dir);
+
+ //
+ // Return the user to whichever tab zie was last on. If this is the
+ // first time and there is no current tab, then default to the user's
+ // personal tab, if it exists, otherwise to the common tab.
+ //
+ string $curTab = "";
+
+ if (`optionVar -exists shavePreset_curTab`)
+ $curTab = `optionVar -q shavePreset_curTab`;
+
+ if (($curTab != "shaveCommonPresets")
+ && ($curTab != "shavePersonalPresets"))
+ {
+ if (($numPersonalPresets == 0) && ($numCommonPresets > 0))
+ $curTab = "shaveCommonPresets";
+ else
+ $curTab = "shavePersonalPresets";
+ }
+
+ tabLayout -e -selectTab $curTab shavePresetTabs;
+
+ optionVar -sv shavePreset_curTab $curTab;
+
+ showWindow shavePresetWindow;
+
+ //
+ // Make sure that all the button icons are up-to-date.
+ //
+ shavePreset_synchIcons();
+}
+
+
+global proc shavePreset_synchIcons()
+{
+ if (`window -exists shavePresetWindow`)
+ {
+ string $buttons[] = `gridLayout -q -ca shaveCommonPresetsGrid`;
+ int $numButtons = size($buttons);
+ int $i;
+
+ for ($i = 0; $i < $numButtons; $i++)
+ {
+ string $icon = `iconTextButton -q -i1 $buttons[$i]`;
+
+ if (`shaveIcon -needsReload $icon`)
+ reloadImage $icon $buttons[$i];
+ }
+
+ $buttons = `gridLayout -q -ca shavePersonalPresetsGrid`;
+ $numButtons = size($buttons);
+
+ for ($i = 0; $i < $numButtons; $i++)
+ {
+ string $icon = `iconTextButton -q -i1 $buttons[$i]`;
+
+ if (`shaveIcon -needsReload $icon`)
+ reloadImage $icon $buttons[$i];
+ }
+ }
+}
+
+
+global proc shavePreset_tabChanged()
+{
+ if (`tabLayout -exists shavePresetTabs`)
+ {
+ string $curTab = `tabLayout -q -selectTab shavePresetTabs`;
+
+ optionVar -sv shavePreset_curTab $curTab;
+ }
+}
+
+
+global proc shavePreset_apply(string $preset, string $shaveNode)
+{
+ global float $gAEAttrPresetBlend;
+ global string $gAEAttrPresetCurrentTarget;
+
+ $gAEAttrPresetBlend = 1.0;
+ $gAEAttrPresetCurrentTarget = $shaveNode;
+
+ eval("source \"" + $preset + "\"");
+
+ //need to set guide thinkess param
+ //float $thick = getAttr($shaveNode + ".rootThickness");
+ //setAttr($shaveNode + ".displayGuideThick") $thick;
+}
+
+
+//
+// psWinSavePreset is a procedure defined in Maya's saveAttrPresetWin.mel
+// script file. It is called whenever a new preset is saved.
+//
+// We override it here with our own version which is a duplicate of Maya's
+// (as of Maya 6.0 thru 7.0) except that if the node is a shaveNode, then we
+// also generate a swatch for it.
+//
+global proc psWinSavePreset()
+{
+ global string $gTmpAttrPresetNameField;
+ global string $gTmpAttrPresetNodeName;
+
+ // We use a nodeName ending with "()" to indicate that
+ // instead of a node, we passed a procedure for creating
+ // the node on the fly, and we want the node to be deleted
+ // after it is used.
+ //
+ int $needToDeleteNode = false;
+ if (isAProcedure($gTmpAttrPresetNodeName))
+ {
+ // suspect we don't ever use the temp node any more
+ $gTmpAttrPresetNodeName = eval($gTmpAttrPresetNodeName);
+ $needToDeleteNode = true;
+ }
+
+ if (objExists($gTmpAttrPresetNodeName)) {
+ string $presetName = `textFieldGrp -q -text $gTmpAttrPresetNameField`;
+
+ string $actualName = `saveAttrPreset $gTmpAttrPresetNodeName $presetName false`;
+
+ // if we saved something, close the window
+ // otherwise the user might want a different name
+ if (size($actualName) > 0) {
+ window -e -visible false attrPresetWin;
+
+ string $nodeType = nodeType($gTmpAttrPresetNodeName);
+
+ if ($nodeType == "shaveHair")
+ {
+ //
+ // The final name of the preset may not be the same as what we
+ // originally requested, so extract the preset name from the
+ // output file name.
+ //
+ $presetName = basename($actualName, ".mel");
+
+ string $swatch = `internalVar -userPrefDir`;
+
+ $swatch = substitute("prefs", $swatch, "presets/attrPresets");
+ $swatch = $swatch + $nodeType + "/" + getSwatchName($presetName, "Personal");
+
+ waitCursor -state on;
+ shaveRender -swatch $gTmpAttrPresetNodeName 100 $swatch;
+ waitCursor -state off;
+
+ //
+ // If the preset window is up, refresh it.
+ //
+ if (`window -exists shavePresetWindow`)
+ {
+ global string $shavePreset_callbackCmd;
+
+ shavePresetWin $shavePreset_callbackCmd;
+ }
+ }
+ }
+ } else {
+ warning "Nothing selected, can't save attribute preset.";
+ window -e -visible false attrPresetWin;
+ }
+
+ if ($needToDeleteNode && $gTmpAttrPresetNodeName != "")
+ {
+ delete $gTmpAttrPresetNodeName;
+ }
+}
+
+
+//
+// deleteSelectedAttrPresets is a procedure defined in Maya's
+// attrPresetEditWin.mel script file. It is called whenever an existing
+// preset is deleted.
+//
+// We override it here with our own version which is a duplicate of Maya's
+// (as of Maya 6.0 thru 7.0) except that if the node is a shaveNode, then we
+// also delete its swatch, if present.
+//
+global proc deleteSelectedAttrPresets(){
+ global string $gApeWinPresetList;
+ global string $gApeWinNodeType;
+
+ if( "" == $gApeWinPresetList ){
+ return;
+ }
+ if( "" == $gApeWinNodeType ){
+ return;
+ }
+
+ string $nodeType = $gApeWinNodeType;
+ string $ppath = `internalVar -userPrefDir`;
+ $ppath = substitute("prefs", $ppath, "presets/attrPresets");
+ $ppath = $ppath + $nodeType;
+
+ string $selectedPresets[] = `textScrollList -q -si $gApeWinPresetList`;
+ string $preset;
+ string $someWereShaveNodes = false;
+
+ for ( $preset in $selectedPresets ){
+ sysFile -delete ($ppath + "/" + $preset + ".mel");
+
+ if ($nodeType == "shaveHair")
+ {
+ string $swatchFile;
+
+ $someWereShaveNodes = true;
+ $swatchFile = $ppath + "/" + getSwatchName($preset, "Personal");
+
+ if (`filetest -r $swatchFile`) sysFile -delete $swatchFile;
+ }
+ }
+
+ if (size($selectedPresets) > 0){
+ updateAPEWinNodetype($nodeType);
+ }
+
+ if ($someWereShaveNodes && `window -exists shavePresetWindow`)
+ {
+ global string $shavePreset_callbackCmd;
+
+ shavePresetWin $shavePreset_callbackCmd;
+ }
+
+
+}
+
diff --git a/scripts/shaveRelationshipEditor.mel b/scripts/shaveRelationshipEditor.mel
new file mode 100644
index 0000000..4d3dce9
--- /dev/null
+++ b/scripts/shaveRelationshipEditor.mel
@@ -0,0 +1,1621 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveRelationshipEditor_fileVersion = "$Revision$";
+
+
+// Description:
+//
+// Implementation of the shaverelationship editor. The shave
+// relationship editor is a generic editor that can be reconfigured to
+// perform a variety of tasks which make relationships between
+// entities.
+//
+// Based heavily on the RelationshipEditor.mel by AliasWavefront
+
+
+if (!`scriptedPanelType -exists shaverelationshipPanel`)
+{
+ scriptedPanelType
+ -createCallback "createShaveRelationshipPanel"
+ -initCallback "initShaveRelationshipPanel"
+ -addCallback "addShaveRelationshipPanel"
+ -removeCallback "removeShaveRelationshipPanel"
+ -saveStateCallback "saveStateShaveRelationshipPanel"
+ -deleteCallback "deleteShaveRelationshipPanel"
+ shaverelationshipPanel;
+
+}
+
+/*
+if (!`scriptedPanelType -exists shaverelationshipPanel`)
+{
+ $panel = "shaverelationshipPanel";
+ $idx = 1;
+ while(`selectionConnection -exists ( $panel + $idx +"LeftMainList")`)
+ {
+ $idx = $idx+1;
+ }
+ $panel = $panel + $idx;
+ if(!`scriptedPanelType -exists ($panel)`)
+ {
+ scriptedPanelType
+ -createCallback "createShaveRelationshipPanel"
+ -initCallback "initShaveRelationshipPanel"
+ -addCallback "addShaveRelationshipPanel"
+ -removeCallback "removeShaveRelationshipPanel"
+ -saveStateCallback "saveStateShaveRelationshipPanel"
+ -deleteCallback "deleteShaveRelationshipPanel"
+ $panel;
+ }
+
+}
+*/
+//**********************************************************************
+//
+// Local utility procedures.
+//
+//**********************************************************************
+
+
+//
+// Return the index number from a string which ends with an array element
+// reference.
+//
+proc int getIndex(string $str)
+{
+ return int(substitute("].*$", substitute("^.*[[]", $str, ""), ""));
+}
+
+
+//
+// Return the index of an unused element of the given array attribute.
+//
+// Note that this will not necessarily return *first* unused element.
+//
+proc int getAvailableIndex(string $arrayAttr)
+{
+ string $parts[];
+ tokenize($arrayAttr, ".", $parts);
+
+ string $inUse[] = `listAttr -multi -string $parts[size($parts)-1]
+ $arrayAttr`;
+
+ if (size($inUse) == 0) return 0;
+
+ return (getIndex($inUse[size($inUse)-1]) + 1);
+}
+
+
+//
+// Given the name of a UV set, return an array of textures it is driving,
+// as well as the index into the shaveNode's "hairUVSetAssignments" attribute.
+//
+// If no mapping is found for the UV set, $textures will be returned empty
+// and the proc will return an index of -1.
+//
+proc int getUVSetTextures(string $uvSetPlug, string $textures[])
+{
+ string $shaveNode = shave_getCurrentNode();
+ string $elementsInUse[];
+ string $element;
+ string $tmp[];
+
+ $elementsInUse = `listAttr -multi -string "hairUVSetAssignments"
+ ($shaveNode + ".hairUVSetAssignments")`;
+
+ clear($textures);
+
+ //
+ // Step through each of the shaveNode's uv set mappings, looking for
+ // the one whose hairUVSetName attr is connected to $uvSetPlug.
+ //
+ for ($element in $elementsInUse)
+ {
+ $tmp = `listConnections -p on -s yes -d no
+ ($shaveNode + "." + $element + ".hairUVSetName")`;
+
+ if ((size($tmp) > 0) && ($tmp[0] == $uvSetPlug))
+ {
+ $textures = `listConnections -s yes -d no
+ ($shaveNode + "." + $element + ".hairUVSetTextures")`;
+
+ if (size($textures) == 0) break;
+
+ return getIndex($element);
+ }
+ }
+
+ return -1;
+}
+
+
+proc addUVSetTextures(string $uvSetPlug, string $texturesToAdd[])
+{
+ if (($uvSetPlug != "") && (size($texturesToAdd) > 0))
+ {
+ string $existingTextures[];
+ int $uvSetIndex = getUVSetTextures($uvSetPlug, $existingTextures);
+
+ $texturesToAdd = stringArrayRemove($existingTextures, $texturesToAdd);
+
+ if (size($texturesToAdd) > 0)
+ {
+ string $shaveNode = shave_getCurrentNode();
+ string $attr = $shaveNode + ".hairUVSetAssignments";
+
+ //
+ // If this UV set doesn't already have an entry, then find an
+ // empty one for it and connect the set name plug.
+ //
+ if ($uvSetIndex == -1)
+ {
+ $uvSetIndex = getAvailableIndex($attr);
+ connectAttr $uvSetPlug
+ ($attr+ "[" + $uvSetIndex + "].hairUVSetName");
+ }
+
+ string $texture;
+
+ $attr += "[" + $uvSetIndex + "]";
+
+ for ($texture in $texturesToAdd)
+ {
+ //
+ // For some reason '-nextAvailable' isn't working on the
+ // 'hairUVSetTextures' attr, even though it's marked as index
+ // not mattering. I haven't the energy to debug it
+ // tonight, so I'll just do it the brute-force way.
+ //
+ int $textureIndex = getAvailableIndex($attr + ".hairUVSetTextures");
+
+ connectAttr
+ ($texture + ".message")
+ ($attr + ".hairUVSetTextures[" + $textureIndex + "]");
+ }
+ }
+ }
+}
+
+
+proc removeUVSetTextures(string $uvSetPlug, string $texturesToRemove[])
+{
+ if (($uvSetPlug != "") && (size($texturesToRemove) > 0))
+ {
+ //
+ // Get the index of the UV set plug.
+ //
+ string $existingTextures[];
+ int $uvSetIndex = getUVSetTextures($uvSetPlug, $existingTextures);
+
+ //
+ // Get all the plugs for the textures to which this UV set is
+ // assigned.
+ //
+ string $shaveNode = shave_getCurrentNode();
+ string $uvSetEntry;
+ $uvSetEntry = $shaveNode + ".hairUVSetAssignments[" + $uvSetIndex + "]";
+
+ string $inUse[] = `listAttr -multi -string "hairUVSetTextures"
+ ($uvSetEntry + ".hairUVSetTextures")`;
+
+ if (($uvSetIndex >= 0) && (size($existingTextures) > 0))
+ {
+ string $tmp[];
+ string $texture;
+
+ //
+ // Step through each texture plug. If its texture matches one
+ // of those which we are removing, then remove that element
+ // from the array.
+ //
+ int $numRemoved = 0;
+
+ for ($texturePlug in $inUse)
+ {
+ $tmp = `listConnections -s yes -d no
+ ($shaveNode + "." + $texturePlug)`;
+
+ if (size($tmp) > 0)
+ {
+ for ($texture in $texturesToRemove)
+ {
+ if ($tmp[0] == $texture)
+ {
+ removeMultiInstance -break yes
+ ($shaveNode + "." + $texturePlug);
+ $numRemoved++;
+ break;
+ }
+ }
+ }
+ }
+
+ //
+ // If we removed all of the UV set's textures, then remove the
+ // entire entry for the UV set.
+ //
+ if ($numRemoved == size($existingTextures))
+ {
+ removeMultiInstance -break yes $uvSetEntry;
+ }
+ }
+ }
+}
+
+
+proc selectshaveRelatedItems(string $panel, string $uvSet)
+{
+ if ($uvSet != "")
+ {
+ waitCursor -state on;
+
+ //
+ // We'll be fiddling with the selections in the right-side outliner
+ // and we don't want to trigger any callbacks, so disable them.
+ //
+ string $otherSelection = ($panel + "RightSelection");
+ string $otherAddScript;
+ string $otherRemoveScript;
+
+ $otherAddScript =
+ `selectionConnection -query -addScript $otherSelection`;
+ $otherRemoveScript =
+ `selectionConnection -query -removeScript $otherSelection`;
+
+ selectionConnection
+ -edit
+ -addScript ""
+ -removeScript ""
+ $otherSelection;
+
+ //
+ // Clear the right-side selection list.
+ //
+ selectionConnection
+ -edit
+ -clear
+ $otherSelection;
+
+ //
+ // Get the textures which are driven by this uv set.
+ //
+ string $textures[];
+
+ getUVSetTextures($uvSet, $textures);
+
+ //
+ // Select all the textures.
+ //
+ string $texture;
+
+ for ($texture in $textures)
+ {
+ selectionConnection
+ -edit
+ -select $texture
+ $otherSelection;
+ }
+
+ waitCursor -state off;
+
+ //
+ // Re-enable the right-side selection list's callbacks.
+ //
+ selectionConnection
+ -edit
+ -addScript $otherAddScript
+ -removeScript $otherRemoveScript
+ $otherSelection;
+ }
+}
+
+
+//
+// Return an array containing only those elements of $before which are UV
+// sets.
+//
+proc string[] onlyUVSets(string $before[])
+{
+ string $after[];
+ string $item;
+
+ for ($item in $before)
+ {
+ //
+ // The editor contains both meshes and their uv sets, but we only
+ // allow uv sets to be selected as keys.
+ //
+ // uv sets show up in the $before list as 'hairUVSetName' attributes.
+ // So if this key ends in ".hairUVSetName" we'll keep it, otherwise
+ // not.
+ //
+ if (match("[.]uvSetName$", $item) != "")
+ $after[size($after)] = $item;
+ }
+
+ return $after;
+}
+
+
+//
+// Return an array containing only those elements of $before which are
+// textures.
+//
+proc string[] onlyTextures(string $before[])
+{
+ string $after[];
+ string $item;
+
+ for ($item in $before)
+ {
+ //
+ // It's possible for the user to manually turn on attribute display
+ // in right-side outliner, so if there's a period in the item name,
+ // toss it.
+ //
+ if (match("[.]", $item) == "") $after[size($after)] = $item;
+ }
+
+ return $after;
+}
+
+
+global proc shaverelationshipEditorSelectKey(string $panel, string $dummy[])
+{
+ string $thisSelection = ($panel + "LeftSelection");
+ string $otherSelection = ($panel + "RightSelection");
+ string $uvSets[] = onlyUVSets(
+ `selectionConnection -query -object $thisSelection`
+ );
+
+ //
+ // Clear the left and right selections.
+ //
+ selectionConnection
+ -edit
+ -addScript ""
+ -removeScript ""
+ $thisSelection;
+
+ selectionConnection
+ -edit
+ -clear
+ $thisSelection;
+
+ selectionConnection
+ -edit
+ -addScript ""
+ -removeScript ""
+ $otherSelection;
+
+ selectionConnection
+ -edit
+ -clear
+ $otherSelection;
+
+ if (size($uvSets) > 0)
+ {
+ //
+ // We don't allow multiple selection in the left-side outliner, so
+ // there should only be one uvSet.
+ //
+ selectionConnection
+ -edit
+ -select $uvSets[0]
+ $thisSelection;
+
+ selectshaveRelatedItems($panel, $uvSets[0]);
+
+ selectionConnection
+ -edit
+ -addScript
+ ("shaverelationshipEditorMakeShaveRelationship " + $panel)
+ -removeScript
+ ("shaverelationshipEditorBreakShaveRelationship " + $panel)
+ $otherSelection;
+ }
+ else
+ {
+ //
+ // There is no UV set selected, so don't allow any textures to be
+ // selected either.
+ //
+ selectionConnection
+ -edit
+ -addScript ("shaverelationshipEditorDisallowSelectItem " + $panel)
+ $otherSelection;
+ }
+
+ selectionConnection
+ -edit
+ -addScript ("shaverelationshipEditorSelectKey " + $panel)
+ -removeScript ("shaverelationshipEditorDeselectKey " + $panel)
+ $thisSelection;
+}
+
+
+global proc shaverelationshipEditorDeselectKey(string $panel, string $dummy[])
+{
+ string $thisSelection = ($panel + "LeftSelection");
+ string $otherSelection = ($panel + "RightSelection");
+ string $uvSets[] = `selectionConnection -query -object $thisSelection`;
+
+ if (size($uvSets) == 0)
+ {
+ selectionConnection
+ -edit
+ -addScript ""
+ -removeScript ""
+ $otherSelection;
+
+ selectionConnection
+ -edit
+ -clear
+ $otherSelection;
+
+ selectionConnection
+ -edit
+ -addScript ("shaverelationshipEditorDisallowSelectItem " + $panel)
+ $otherSelection;
+ }
+}
+
+
+global proc shaverelationshipEditorDisallowSelectItem(
+ string $panel,
+ string $items[])
+{
+ selectionConnection
+ -edit
+ -clear
+ ($panel + "RightSelection");
+}
+
+
+global proc shaverelationshipEditorMakeShaveRelationship(
+ string $panel,
+ string $unvalidatedItems[])
+{
+ string $leftSelection = ($panel + "LeftSelection");
+ string $uvSetPlugs[] = `selectionConnection -query -object $leftSelection`;
+ string $texturesToAdd[] = onlyTextures($unvalidatedItems);
+ int $numTexturesToAdd = size($texturesToAdd);
+
+ if ((size($uvSetPlugs) > 0) && ($numTexturesToAdd > 0))
+ {
+ waitCursor -state on;
+
+ //
+ // Note that because we don't allow multiple selections in the
+ // left outliner, $uvSetPlugs will only contain a single element.
+ //
+ string $uvSetPlug = $uvSetPlugs[0];
+
+ addUVSetTextures($uvSetPlug, $texturesToAdd);
+
+ //
+ // What is this UV set's parent object?
+ //
+ string $parts[];
+
+ tokenize($uvSetPlug, ".", $parts);
+
+ string $parent = $parts[0];
+
+ //
+ // Only one uvSet from a given parent mesh can be assigned to a
+ // texture. So we must find all of $uvSet's siblings and remove
+ // $texturesToAdd from them.
+ //
+ string $shaveNode = shave_getCurrentNode();
+ string $uvSetEntries[];
+ string $uvSetEntry;
+
+ $uvSetEntries = `listAttr -multi -string "hairUVSetName"
+ ($shaveNode + ".hairUVSetAssignments")`;
+
+ for ($uvSetEntry in $uvSetEntries)
+ {
+ //
+ // Get the UV set plug for this entry.
+ //
+ string $siblingPlug[] = `listConnections -p on -s yes -d no
+ ($shaveNode + "." + $uvSetEntry)`;
+
+ //
+ // Make sure that this isn't the UV set to which the
+ // textures were just added.
+ //
+ if ((size($siblingPlug) > 0) && ($siblingPlug[0] != $uvSetPlug))
+ {
+ tokenize($siblingPlug[0], ".", $parts);
+
+ //
+ // If this UV set comes from the same mesh, then remove all
+ // the textures which were just added to its sibling.
+ //
+ if ((size($parts) > 0) && ($parts[0] == $parent))
+ {
+ removeUVSetTextures($siblingPlug[0], $texturesToAdd);
+ }
+ }
+ }
+
+ waitCursor -state off;
+ }
+}
+
+
+global proc shaverelationshipEditorBreakShaveRelationship(
+ string $panel,
+ string $texturesToRemove[])
+{
+ int $numTexturesToRemove = size($texturesToRemove);
+
+ if ($numTexturesToRemove > 0)
+ {
+ string $leftSelection = ($panel + "LeftSelection");
+ string $uvSets[] = `selectionConnection -query -object $leftSelection`;
+
+ if (size($uvSets) > 0)
+ {
+ waitCursor -state on;
+
+ //
+ // Remove the specified textures.
+ //
+ removeUVSetTextures($uvSets[0], $texturesToRemove);
+
+ waitCursor -state off;
+ }
+ }
+}
+
+
+proc getUpstreamTextures(string $node, string $upstreamTextureArray[])
+{
+ string $history[] = `listHistory $node`;
+
+ int $i;
+
+ for ($i = 0; $i < size($history); $i++)
+ {
+ if (size(`ls ($history[$i] + ".uvCoord")`) > 0)
+ {
+ if (`nodeType $history[$i]` != "place2dTexture")
+ {
+ $upstreamTextureArray[size($upstreamTextureArray)]
+ = $history[$i];
+ }
+ }
+ }
+}
+
+
+global proc shave_RE_loadCurrentShaveNode(string $panel)
+{
+ int $i;
+
+ selectionConnection
+ -edit
+ -clear
+ ($panel + "LeftSelection");
+
+ selectionConnection
+ -edit
+ -clear
+ ($panel + "RightSelection");
+
+ selectionConnection
+ -edit
+ -clear
+ ($panel + "LeftMainList");
+
+ selectionConnection
+ -edit
+ -clear
+ ($panel + "RightMainList");
+
+ string $uvMainListConnection = ($panel + "LeftMainList");
+ string $textureMainListConnection = ($panel + "RightMainList");
+ string $shaveNode = shave_getCurrentNode();
+
+ if ($shaveNode != "")
+ {
+ string $growthSurfaces[] = `shaveNode -q -gl $shaveNode`;
+
+ if (size($growthSurfaces) > 0)
+ {
+ //
+ // $growthSurfaces may contain components, so let's use a
+ // temporary set to filter it down to just the shape nodes.
+ //
+ string $tempSet = `sets -empty`;
+
+ sets -add $tempSet $growthSurfaces;
+
+ $growthSurfaces = `sets -q -nodesOnly $tempSet`;
+
+ int $numMeshes = 0;
+ string $shape;
+
+ for ($shape in $growthSurfaces)
+ {
+ if (objectType($shape) == "mesh")
+ {
+ selectionConnection
+ -edit
+ -select $shape
+ $uvMainListConnection;
+
+ $numMeshes++;
+ }
+ }
+
+ if ($numMeshes > 0)
+ {
+ string $textures[];
+ string $dstex[];
+ string $tex1;
+ string $tex2;
+
+ string $texturableAttrs[] = {
+ "shaveTex",
+ "hairColorTexture",
+ "rootHairColorTexture",
+ "mutantHairColorTexture"
+ };
+ string $attr;
+
+ for ($attr in $texturableAttrs)
+ {
+ $textures = `listConnections -s yes -d no
+ ($shaveNode + "." + $attr)`;
+
+ for ($tex1 in $textures)
+ {
+ //
+ // Let's also include any textures upstream of this
+ // one.
+ //
+ getUpstreamTextures($tex1, $dstex);
+
+ for($tex2 in $dstex)
+ {
+ selectionConnection
+ -edit
+ -select $tex2
+ $textureMainListConnection;
+ }
+ }
+ }
+
+ //
+ // If there's only one growth mesh, and it has only one uv
+ // set, then select it.
+ //
+ if (($numMeshes == 1)
+ && (`getAttr -size ($shape + ".uvst")` == 1))
+ {
+ selectionConnection
+ -edit
+ -select $shape
+ ($panel + "LeftSelection");
+ }
+ }
+ }
+ }
+
+ string $leftSelection = ($panel + "LeftSelection");
+ string $keys[] = `selectionConnection -query -object $leftSelection`;
+
+ selectshaveRelatedItems($panel, $keys[0]);
+}
+
+
+proc configureLeftOutliner(string $panel)
+{
+ string $topLayout = `panel -query -control $panel`;
+ setParent $topLayout;
+
+ outlinerEditor
+ -edit
+ -directSelect true
+ -ignoreDagHierarchy false
+ -showShapes true
+ -showAttributes true
+ -showConnected false
+ -showDagOnly false
+ -showSetMembers true
+ -doNotSelectNewObjects true
+ -autoSelectNewObjects false
+ -setFilter 0
+ -filter 0
+ -showCompounds false
+ -autoExpand true
+ -expandConnections false
+ -showLeafs true
+ -showTextureNodesOnly false
+ -showUVAttrsOnly true
+ -highlightSecondary false
+ -showAttrValues true
+ -allowMultiSelection false
+ -mainListConnection ($panel + "LeftMainList")
+ ($panel + "LeftOutliner");
+}
+
+
+proc configureRightOutliner(string $panel)
+{
+ outlinerEditor
+ -edit
+ -directSelect true
+ -ignoreDagHierarchy false
+ -showShapes false
+ -showAttributes false
+ -showConnected false
+ -showDagOnly false
+ -showSetMembers true
+ -doNotSelectNewObjects true
+ -autoSelectNewObjects false
+ -setFilter 0
+ -filter 0
+ -autoExpand false
+ -expandConnections false
+ -showCompounds false
+ -showLeafs false
+ -showTextureNodesOnly true
+ -showUVAttrsOnly false
+ -highlightSecondary false
+ -showAttrValues false
+ -mainListConnection ($panel + "RightMainList")
+ ($panel + "RightOutliner");
+}
+
+
+proc setTextureVisibility(
+ string $texture,
+ int $isVisible)
+{
+ string $colorDstArray[];
+ string $alphaDstArray[];
+ int $i;
+ string $tokenArray[];
+ string $dstNode;
+ string $dstAttr;
+ int $success;
+
+ $colorDstArray =
+ `listConnections
+ -source false
+ -destination true
+ -plugs true
+ ($texture + ".outColor")`;
+ $alphaDstArray =
+ `listConnections
+ -source false
+ -destination true
+ -plugs true
+ ($texture + ".outAlpha")`;
+
+ for ($i = 0; $i < size($colorDstArray); $i++)
+ {
+ $success = false;
+ tokenize($colorDstArray[$i], ".", $tokenArray);
+ $dstNode = $tokenArray[0];
+ $inputsAttr = $tokenArray[1];
+ $dstAttr = $tokenArray[2];
+
+ if ((`nodeType $dstNode` == "layeredTexture") && ($dstAttr == "color"))
+ {
+ string $regExp;
+ string $index;
+
+ $regExp = "\\[.*\\]";
+ $index = match($regExp, $inputsAttr);
+
+ if ($index != "")
+ {
+ setAttr
+ ($dstNode + ".inputs" + $index + ".isVisible")
+ $isVisible;
+ $success = true;
+ }
+ }
+
+ if (!$success)
+ {
+ shadingConnection
+ -edit
+ -connectionState $isVisible
+ $colorDstArray[$i];
+ }
+ }
+
+ for ($i = 0; $i < size($alphaDstArray); $i++)
+ {
+ $success = false;
+ tokenize($alphaDstArray[$i], ".", $tokenArray);
+ $dstNode = $tokenArray[0];
+ $inputsAttr = $tokenArray[1];
+ $dstAttr = $tokenArray[2];
+
+ if ((`nodeType $dstNode` == "layeredTexture") && ($dstAttr == "alpha"))
+ {
+ string $regExp;
+ string $index;
+
+ $regExp = "\\[.*\\]";
+ $index = match($regExp, $tokenArray[1]);
+
+ if ($index != "")
+ {
+ setAttr
+ ($dstNode + ".inputs" + $index + ".isVisible")
+ $isVisible;
+ $success = true;
+ }
+ }
+
+ if (!$success)
+ {
+ shadingConnection
+ -edit
+ -connectionState $isVisible
+ $alphaDstArray[$i];
+ }
+ }
+}
+
+
+proc ignoreTexture(string $texture)
+{
+ setTextureVisibility($texture, false);
+}
+
+
+proc unignoreTexture(string $texture)
+{
+ setTextureVisibility($texture, true);
+}
+
+
+global proc shaverelationshipEditorMenuCommand(string $panel, string $command)
+{
+ string $uvMainListConnection = ($panel + "LeftMainList");
+ string $textureMainListConnection = ($panel + "RightMainList");
+ string $uvSelection = ($panel + "LeftSelection");
+ string $textureSelection = ($panel + "RightSelection");
+
+ if ($command == "leftEditRemoveUvSet")
+ {
+ string $uvSetArray[];
+ $uvSetArray = `selectionConnection -q -object $uvSelection`;
+ for ($i =0; $i < size($uvSetArray); $i++)
+ {
+ string $uvSetName = `getAttr $uvSetArray[$i]`;
+ string $buffer[];
+ string $objName = "";
+ $numTokens = `tokenize $uvSetArray[$i] "." $buffer`;
+ if ($numTokens > 0)
+ $objName = $buffer[0];
+ if (size($uvSetName) && size($objName))
+ {
+ polyUVSet -delete -uvSet $uvSetName $objName;
+ }
+ }
+ }
+ else if ($command == "leftEditRenameUvSet")
+ {
+ // Rename the UV set(s).
+ string $uvSetArray[];
+ $uvSetArray = `selectionConnection -q -object $uvSelection`;
+ int $numItems = size($uvSetArray);
+ if ($numItems)
+ {
+ string $attrName = $uvSetArray[$numItems-1];
+ string $uvSetName = `getAttr $attrName`;
+ string $buffer[];
+ int $numTokens = `tokenize $attrName "." $buffer`;
+ string $objName = "";
+ if ($numTokens > 0)
+ $objName = $buffer[0];
+ if (size($uvSetName) && size($objName))
+ {
+ performRenameUVSet 1 $uvSetName $objName;
+ }
+ }
+ }
+ else if ($command == "rightEditMenuSelectHighlighted")
+ {
+
+ string $selectedItems[] =
+ `selectionConnection -q -object ($panel + "RightSelection")`;
+ select -r -ne $selectedItems;
+ }
+ else if ($command == "rightEditIgnoreTexture")
+ {
+ // Ignore the textures
+ //
+ string $texturesToIgnoreArray[];
+ int $i;
+
+ $texturesToIgnoreArray =
+ `selectionConnection -q -object $textureSelection`;
+
+ for ($i = 0; $i < size($texturesToIgnoreArray); $i++)
+ {
+ ignoreTexture($texturesToIgnoreArray[$i]);
+ }
+ }
+ else if ($command == "rightEditUnignoreTexture")
+ {
+ // Unignore the textures
+ //
+ string $texturesToUnignoreArray[];
+ int $i;
+
+ $texturesToUnignoreArray =
+ `selectionConnection -q -object $textureSelection`;
+
+ for ($i = 0; $i < size($texturesToUnignoreArray); $i++)
+ {
+ unignoreTexture($texturesToUnignoreArray[$i]);
+ }
+ }
+ else if ($command == "rightEditIsolateTexture")
+ {
+ // Isolate the textures
+ //
+ string $texturesToIsolateArray[];
+ string $allTexturesArray[];
+ string $downstreamNodes[];
+ int $i;
+
+ $texturesToIsolateArray =
+ `selectionConnection -q -object $textureSelection`;
+ $downstreamNodes =
+ `selectionConnection -q -object $textureMainListConnection`;
+
+ for ($i = 0; $i < size($downstreamNodes); $i++)
+ {
+ getUpstreamTextures($downstreamNodes[$i], $allTexturesArray);
+ }
+
+ for ($i = 0; $i < size($allTexturesArray); $i++)
+ {
+ ignoreTexture($allTexturesArray[$i]);
+ }
+
+ for ($i = 0; $i < size($texturesToIsolateArray); $i++)
+ {
+ unignoreTexture($texturesToIsolateArray[$i]);
+ }
+ }
+ else if ($command == "rightEditUnignoreAllTextures")
+ {
+ string $allTexturesArray[];
+ string $downstreamNodes[];
+ int $i;
+
+ $downstreamNodes =
+ `selectionConnection -q -object $textureMainListConnection`;
+
+ for ($i = 0; $i < size($downstreamNodes); $i++)
+ {
+ getUpstreamTextures($downstreamNodes[$i], $allTexturesArray);
+ }
+
+ for ($i = 0; $i < size($allTexturesArray); $i++)
+ {
+ unignoreTexture($allTexturesArray[$i]);
+ }
+ }
+ else if ($command == "rightEditAttributeEditor")
+ {
+ string $highlighted[];
+
+ $highlighted = `selectionConnection -q -object $textureSelection`;
+
+ if (size($highlighted) > 1)
+ {
+ warning(
+ "Cannot open attribute editor on multiple items. "
+ + "Please choose only one.");
+ return;
+ }
+ else if (size($highlighted) == 0)
+ {
+ warning("No item is highlighted.");
+ return;
+ }
+ else
+ {
+ showEditor $highlighted[0];
+ }
+ }
+}
+
+
+proc configureLeftEditMenu(string $panel)
+{
+ string $topLayout = `panel -query -control $panel`;
+
+ setParent $topLayout;
+
+ menu
+ -edit
+ -deleteAllItems
+ leftEditMenu;
+
+ setParent -menu leftEditMenu;
+
+ string $textItem;
+
+ menuItem
+ -label "Remove UV Set"
+ -annotation
+ ("Remove UV Set: Remove the highlighted UV set from the lead "
+ + "object of the current selection")
+ -command ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "leftEditRemoveUvSet")
+ leftEditMenuRemoveUvSetItem;
+
+ menuItem
+ -label "Rename UV Set..."
+ -annotation
+ ("Rename UV Set: Rename the highlighted UV set on the "
+ + "trailing item of the current selection")
+ -command ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "leftEditRenameUvSet")
+ leftEditMenuRenameUvSetItem;
+
+ setParent -menu ..;
+}
+
+
+proc configureRightEditMenu(string $panel)
+{
+ string $topLayout = `panel -query -control $panel`;
+
+ setParent $topLayout;
+
+ menu
+ -edit
+ -deleteAllItems
+ -enable false
+ rightEditMenu;
+
+ //
+ // Windows doesn't refresh the menu to show it disabled, so let's force
+ // it.
+ //
+ if (`about -nt`)
+ {
+ menu -e -visible false rightEditMenu;
+ menu -e -visible true rightEditMenu;
+ }
+
+ setParent -menu rightEditMenu;
+
+ //
+ // Note that none of the items below currently works, which is why the
+ // menu is disabled.
+ //
+ menuItem
+ -label "Select Highlighted"
+ -command
+ ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "rightEditMenuSelectHighlighted")
+ rightEditMenuSelectHighlightedItem;
+
+ menuItem
+ -label "Ignore Texture"
+ -annotation
+ ("Ignore Texture: Ignore the highlighted textures when "
+ + "rendering")
+ -command ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "rightEditIgnoreTexture")
+ rightEditMenuIgnoreTextureItem;
+ menuItem
+ -label "Unignore Texture"
+ -annotation
+ ("Unignore Texture: Stop ignoring the highlighted textures "
+ + "when rendering")
+ -command ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "rightEditUnignoreTexture")
+ rightEditMenuUnignoreTextureItem;
+ menuItem
+ -label "Isolate Texture"
+ -annotation
+ ("Isolate Texture: Ignore all textures in the list except the "
+ + "highlighted textures when rendering")
+ -command ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "rightEditIsolateTexture")
+ rightEditMenuIsolateTextureItem;
+ menuItem
+ -label "Unignore All Textures"
+ -annotation
+ ("Unignore All Textures: Unignore all textures in the list "
+ + "when rendering")
+ -command ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "rightEditUnignoreAllTextures")
+ rightEditMenuUnignoreAllTexturesItem;
+ menuItem
+ -label "Attribute Editor..."
+ -annotation
+ ("Attribute Editor: Open the attribute editor for the "
+ + "highlighted texture")
+ -command ("shaverelationshipEditorMenuCommand "
+ + $panel
+ + " "
+ + "rightEditAttributeEditor")
+ rightEditMenuAttributeEditorItem;
+}
+
+
+proc configureLabels(string $panel)
+{
+ string $topLayout = `panel -query -control $panel`;
+
+ setParent $topLayout;
+
+ text
+ -edit
+ -label "UV Sets"
+ -manage true
+ "leftDescription";
+
+ text
+ -edit
+ -label "Shave Textures"
+ -manage true
+ "rightDescription";
+}
+
+
+proc configureLeftSelection( string $panel)
+{
+ string $selectionConnection = ($panel + "LeftSelection");
+
+ selectionConnection
+ -edit
+ -addScript ""
+ -removeScript ""
+ $selectionConnection;
+
+ selectionConnection
+ -edit
+ -clear
+ $selectionConnection;
+
+ selectionConnection
+ -edit
+ -addScript ("shaverelationshipEditorSelectKey " + $panel)
+ $selectionConnection;
+
+ selectionConnection
+ -edit
+ -removeScript ("shaverelationshipEditorDeselectKey " + $panel)
+ $selectionConnection;
+
+ string $outliner = ($panel + "LeftOutliner");
+
+ outlinerEditor
+ -edit
+ -allowMultiSelection false
+ -alwaysToggleSelect false
+ $outliner;
+}
+
+
+proc configureRightSelection(string $panel)
+{
+ string $selectionConnection = ($panel + "RightSelection");
+
+
+ selectionConnection
+ -edit
+ -addScript ""
+ -removeScript ""
+ $selectionConnection;
+
+ selectionConnection
+ -edit
+ -clear
+ $selectionConnection;
+
+ selectionConnection
+ -edit
+ -addScript ("shaverelationshipEditorDisallowSelectItem " + $panel)
+ $selectionConnection;
+
+
+ string $outliner = ($panel + "RightOutliner");
+
+ outlinerEditor
+ -edit
+ -allowMultiSelection true
+ -alwaysToggleSelect true
+ $outliner;
+}
+
+
+global int $gShaveRelationshipsChangedScriptJobNumber = -1;
+
+
+global proc shave_RE_setShaveNodeChangedScript(string $panel)
+{
+ global int $gShaveRelationshipsChangedScriptJobNumber;
+
+ if ($gShaveRelationshipsChangedScriptJobNumber != -1)
+ {
+ scriptJob -force -kill $gShaveRelationshipsChangedScriptJobNumber;
+ $gShaveRelationshipsChangedScriptJobNumber = -1;
+ }
+
+ $gShaveRelationshipsChangedScriptJobNumber =
+ `scriptJob -protected
+ -parent $panel
+ -event ("SelectionChanged")
+ ("shave_RE_loadCurrentShaveNode " + $panel)`;
+}
+
+
+global proc shave_RE_disableShaveNodeChangedScript(string $panel)
+{
+ global int $gShaveRelationshipsChangedScriptJobNumber;
+
+ if ($gShaveRelationshipsChangedScriptJobNumber != -1)
+ {
+ scriptJob -force -kill $gShaveRelationshipsChangedScriptJobNumber;
+ $gShaveRelationshipsChangedScriptJobNumber = -1;
+ }
+}
+
+
+proc shave_RE_init(string $panel)
+{
+ // put the string from the popup menu into the text field to identify
+ // the current task
+ //
+ optionMenu -e -select 1 shaveTaskPopup;
+
+ configureLabels($panel);
+ configureLeftEditMenu($panel);
+ configureRightEditMenu($panel);
+ configureLeftOutliner($panel);
+ configureRightOutliner($panel);
+ configureLeftSelection($panel);
+ configureRightSelection($panel);
+
+ shave_RE_loadCurrentShaveNode($panel);
+
+ // Set the shaverelationships changed script
+ //
+ shave_RE_setShaveNodeChangedScript($panel);
+}
+
+
+proc createshaveTaskPopup(string $panel)
+{
+ //
+ // Our editor only contains a single task, but we still provide the
+ // popup control because it helps to remind the user what zie's doing.
+ //
+ optionMenu shaveTaskPopup;
+
+ menuItem
+ -label "Shave UV Linking"
+ -annotation "Specify the UV set used by a particular Shave texture"
+ taskShaveUvLinkingItem;
+
+ setParent -m ..;
+}
+
+
+global proc shave_RE_buildContextHelp(string $nameRoot, string $menuParent)
+{
+ menuItem -label "Help on ShaveRelationship Editor..."
+ -enableCommandRepeat false
+ -command "showHelp shaveRelationshipEditor";
+}
+
+
+proc createTopMenu(string $panel)
+{
+ // The Options menu
+ //
+ string $optionsMenu =
+ `menu
+ -label "Options"
+ -tearOff false
+ -enable false
+ optionsMenu`;
+
+ // Add support for the Context Sensitive Help Menu.
+ //
+ addContextHelpProc $panel "shave_RE_buildContextHelp";
+
+ // set the menu bar visibility
+ //
+ int $menusOkayInPanels = `optionVar -q allowMenusInPanels`;
+ panel -e -mbv $menusOkayInPanels $panel;
+}
+
+
+proc string shaveCreatePane(string $side, string $outlinerName)
+{
+ string $paneLayout = ($side + "PaneLayout");
+
+ formLayout $paneLayout;
+
+ string $description = ($side + "Description");
+ text
+ -label "Pane Description"
+ -height 25
+ -align "center"
+ $description;
+
+ string $menuBarLayout = ($side + "MenuBarLayout");
+ menuBarLayout $menuBarLayout;
+
+ menu
+ -label "List"
+ -enable false
+ ($side + "ListMenu");
+ setParent -menu; // from ListMenu
+
+ menu
+ -label "Edit"
+ -allowOptionBoxes true
+ ($side + "EditMenu");
+ setParent -menu; // from EditMenu
+
+ string $outlinerLayout = ($side + "OutlinerLayout");
+ formLayout $outlinerLayout;
+
+ string $outlinerLayoutName = addSharedOutlinerEditor ($outlinerLayout, $outlinerName);
+
+ // add the filter UI field to the outliner
+ //
+ string $filterField = filterUICreateField($outlinerName, $outlinerLayout);
+
+ formLayout
+ -edit
+ -attachForm $filterField "left" 0
+ -attachForm $filterField "right" 0
+ -attachForm $filterField "top" 0
+ -attachNone $filterField "bottom"
+
+ -attachControl $outlinerLayoutName "top" 0 $filterField
+ -attachForm $outlinerLayoutName "left" 0
+ -attachForm $outlinerLayoutName "right" 0
+ -attachForm $outlinerLayoutName "bottom" 0
+ $outlinerLayout;
+
+ setParent ..; // from $outlinerLayout
+
+ setParent ..; // from $menuBarLayout
+
+ formLayout
+ -edit
+
+ -attachForm $description "top" 0
+ -attachForm $description "left" 0
+ -attachForm $description "right" 0
+ -attachNone $description "bottom"
+
+ -attachControl $menuBarLayout "top" 0 $description
+ -attachForm $menuBarLayout "left" 0
+ -attachForm $menuBarLayout "right" 0
+ -attachForm $menuBarLayout "bottom" 0
+
+ $paneLayout;
+
+ setParent ..; // from $paneLayout
+
+ return $paneLayout;
+}
+
+
+global proc createShaveRelationshipPanel(string $panel)
+{
+
+ createSharedOutlinerEditor ($panel + "LeftOutliner");
+ createSharedOutlinerEditor ($panel + "RightOutliner");
+
+
+ if(!`selectionConnection -exists ($panel + "LeftMainList")`)
+ selectionConnection ($panel + "LeftMainList");
+
+ if(!`selectionConnection -exists ($panel + "RightMainList")`)
+ selectionConnection ($panel + "RightMainList");
+
+ if(!`selectionConnection -exists ($panel + "LeftSelection")`)
+ selectionConnection ($panel + "LeftSelection");
+
+ if(!`selectionConnection -exists ($panel + "RightSelection")`)
+ selectionConnection ($panel + "RightSelection");
+
+ if(!`selectionConnection -exists ($panel + "LeftMainListManual")`)
+ selectionConnection ($panel + "LeftMainListManual");
+
+ if(!`selectionConnection -exists ($panel + "RightMainListManual")`)
+ selectionConnection ($panel + "RightMainListManual");
+
+
+ outlinerEditor
+ -edit
+ -setsIgnoreFilters true
+ -selectionConnection ($panel + "LeftSelection")
+ ($panel + "LeftOutliner");
+ outlinerEditor
+ -edit
+ -setsIgnoreFilters true
+ -selectionConnection ($panel + "RightSelection")
+ ($panel + "RightOutliner");
+}
+
+
+global proc initShaveRelationshipPanel(string $panel)
+{
+ //
+ // If the window is open, we need to call shave_RE_init to initialize
+ // it properly.
+ //
+ if (`optionMenu -exists shaveTaskPopup`) shave_RE_init($panel);
+}
+
+
+global proc addShaveRelationshipPanel(string $panel)
+{
+ createTopMenu($panel);
+
+ formLayout mainForm;
+
+ createshaveTaskPopup($panel);
+
+ separator
+ -height 5
+ -style "out"
+ taskMenuSeparator;
+
+ int $height = 24;
+
+ formLayout leftRightForm;
+ paneLayout
+ -configuration "vertical2"
+ -separatorThickness 5
+ leftRightLayout;
+
+ string $leftPaneLayout = shaveCreatePane(
+ "left",
+ ($panel + "LeftOutliner"));
+ string $rightPaneLayout = shaveCreatePane(
+ "right",
+ ($panel + "RightOutliner"));
+
+ // add the filter UI menu items to each side
+ //
+ filterUICreateMenu(($panel + "LeftOutliner"), "leftMenuBarLayout");
+ filterUICreateMenu(($panel + "RightOutliner"), "rightMenuBarLayout");
+
+ // Get the popup menu attached to each outliner and add an item
+ // to access the filter menu.
+ //
+ string $popupMenuParent, $popupMenu, $popupMenuList[];
+ int $button;
+ $popupMenuParent = `editor -query -control ($panel + "LeftOutliner")`;
+ if ("" != $popupMenuParent && "NONE" != $popupMenuParent) {
+ $popupMenuList = `control -query -popupMenuArray $popupMenuParent`;
+ for ($popupMenu in $popupMenuList) {
+ $button = `popupMenu -query -button $popupMenu`;
+ if (3 == $button) {
+ $popupMenu = $popupMenuParent + "|" + $popupMenu;
+ filterUICreateMenu(($panel + "LeftOutliner"), $popupMenu);
+ break;
+ }
+ }
+ }
+ $popupMenuParent = `editor -query -control ($panel + "RightOutliner")`;
+ if ("" != $popupMenuParent && "NONE" != $popupMenuParent) {
+ $popupMenuList = `control -query -popupMenuArray $popupMenuParent`;
+ for ($popupMenu in $popupMenuList) {
+ $button = `popupMenu -query -button $popupMenu`;
+ if (3 == $button) {
+ $popupMenu = $popupMenuParent + "|" + $popupMenu;
+ filterUICreateMenu(($panel + "RightOutliner"), $popupMenu);
+ break;
+ }
+ }
+ }
+
+ setParent ..; // from $leftRightLayout
+
+ formLayout
+ -edit
+ -attachForm "leftRightLayout" "left" 0
+ -attachForm "leftRightLayout" "right" 0
+ -attachForm "leftRightLayout" "top" 0
+ -attachForm "leftRightLayout" "bottom" 0
+ leftRightForm;
+
+ setParent ..; // from leftRightForm
+
+ formLayout
+ -edit
+
+ -attachForm "taskMenuSeparator" "left" 0
+ -attachForm "taskMenuSeparator" "right" 0
+ -attachControl "taskMenuSeparator" "top" 0 "shaveTaskPopup"
+ -attachNone "taskMenuSeparator" "bottom"
+
+ -attachForm "leftRightForm" "left" 0
+ -attachForm "leftRightForm" "right" 0
+ -attachControl "leftRightForm" "top" 0 "taskMenuSeparator"
+ -attachForm "leftRightForm" "bottom" 0
+
+ mainForm;
+
+ setParent ..; // from mainForm
+
+ shave_RE_init($panel);
+}
+
+
+global proc removeShaveRelationshipPanel(string $panel)
+{
+ removeSharedOutlinerEditor ($panel+"LeftOutliner");
+ removeSharedOutlinerEditor ($panel+"RightOutliner");
+
+ filterUIRemoveView($panel+"LeftOutliner");
+ filterUIRemoveView($panel+"RightOutliner");
+}
+
+
+global proc string saveStateShaveRelationshipPanel(string $panel)
+{
+ string $stateStr = "";
+ return $stateStr;
+}
+
+
+global proc deleteShaveRelationshipPanel(string $panel)
+{
+}
+
+// ---------------------------------------------------------------------------
+// Procedures which the user uses to invoke the shaverelationship editor
+//
+
+global proc shaveLinkingEditor()
+{
+
+ global int $gShaveRelationshipsChangedScriptJobNumber;
+ $gShaveRelationshipsChangedScriptJobNumber = -1;
+
+ tearOffPanel "ShaveRelationship Editor" "shaverelationshipPanel" true;
+
+ string $shaverelationshipEditorPanels[] =
+ `getPanel -scriptType "shaverelationshipPanel"`;
+
+ shave_RE_init($shaverelationshipEditorPanels[0]);
+}
+
diff --git a/scripts/shaveRenderman.mel b/scripts/shaveRenderman.mel
new file mode 100644
index 0000000..192ea29
--- /dev/null
+++ b/scripts/shaveRenderman.mel
@@ -0,0 +1,528 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveRenderman_fileVersion = "$Revision$";
+
+
+// WARNING: The rman plugin may not be loaded when this script gets
+// sourced so all references to commands from that plugin
+// (e.g. 'rman', 'rmanGetGlobals', 'RiReadArchive') must be
+// executed within an 'eval' otherwise the results will be
+// unpredictable.
+
+global int $gShave_rmanPluginIsLoaded = false;
+global int $gShave_rmanWaitingForPlugin = false;
+global string $gShave_rmanArchiveFiles[];
+
+
+// Local Procedures
+
+// We need this because all of Maya's native string search commands take
+// some kind of pattern and we don't want any "special chars" in our
+// search.
+proc int strIndex(string $needle, string $haystack)
+{
+ int $needleLen = size($needle);
+ int $haystackLen = size($haystack);
+ int $i;
+
+ for ($i = 1; $i <= ($haystackLen - $needleLen) + 1; $i++)
+ {
+ if (substring($haystack, $i, $i + $needleLen - 1) == $needle)
+ return $i;
+ }
+
+ return 0;
+}
+
+
+proc string getRManGlobals()
+{
+ string $globals = "";
+
+ if (exists("rmanGetGlobals")) {
+ $globals = rmanGetGlobals();
+ }
+
+ return $globals;
+}
+
+
+// Returns the version of RenderMan for Maya as a float.
+//
+proc float getRfMVersion()
+{
+ float $version = 0.0;
+
+ if (`pluginInfo -q -loaded RenderMan_for_Maya`) {
+ string $versionStr = eval("rman getversion");
+
+ // The version string will be of the form "5.5 (@1457856 Mar 16 2015)".
+ // We want just the version number part.
+ //
+ $version = float(match("^[0-9.]+", $versionStr));
+ }
+
+ return $version;
+}
+
+
+// Returns the version of Renderman as a float.
+//
+// NOTE: As of RenderMan Studio 18 the RMS version is the same as the
+// Renderman version.
+//
+proc float getRManVersion()
+{
+ float $version = 0.0;
+
+ if (`pluginInfo -q -loaded RenderMan_for_Maya`) {
+ string $versionStr = eval("rman getversion prman");
+
+ // The version string will be of the form "prman 19.0 @1457856".
+ // We want just the version number part.
+ //
+ $version = float(match("[0-9.]+", $versionStr));
+ }
+
+ return $version;
+}
+
+
+proc addShaderPath()
+{
+ // Our SLIM shader sits in the 'prman/shaders' directory beneath the
+ // same directory as our plugin was loaded from. Update RenderMan's shader
+ // path to include this directory.
+ //
+ string $pluginDir = dirname(`pluginInfo -q -path shaveNode`);
+ RiOption("searchpath", "string shader", $pluginDir + "/prman/shaders:&");
+
+ // Our Bxdf lies in the 'prman/RMSnn' directory beneath the same
+ // directory as our plugin was loaded from, where 'nn' is the
+ // version of the RMS plugin currently loaded into Maya. Update
+ // RenderMan's rixplugin path to include this directory.
+ //
+ int $rmsVersion = int(getRManVersion());
+
+ if ($rmsVersion >= 19) {
+ RiOption("searchpath", "string rixplugin", $pluginDir + "/prman/RMS" + $rmsVersion + ":&:@");
+ }
+}
+
+
+proc removeCallback(string $node, string $attr, string $callback)
+{
+ if (objExists($node) && `attributeExists $attr $node`)
+ {
+ string $val = getAttr($node + "." + $attr);
+
+ // The callback might be one command buried in a string of
+ // semi-colon separate commands. So let's strip out all the
+ // possible permutations.
+ $val = substitute((";[ \t]*" + $callback + "[ \t]*"), $val, "");
+ $val = substitute(("[ \t]*" + $callback + "[ \t]*;"), $val, "");
+ $val = substitute(("[ \t]*" + $callback + "[ \t]*"), $val, "");
+
+ setAttr -type "string" ($node + "." + $attr) $val;
+ }
+}
+
+
+proc addCallback(string $node, string $attr, string $callback)
+{
+ if (objExists($node) && `attributeExists $attr $node`)
+ {
+ // Make sure that we're not creating multiple entries for the same
+ // callback by first removing any existing ones.
+ removeCallback($node, $attr, $callback);
+
+ string $val = getAttr($node + "." + $attr);
+
+ if (($val == "") || (size(`match "^[ \t]*$" $val`) > 0))
+ $val = $callback;
+ else
+ $val += "; " + $callback;
+
+ setAttr -type "string" ($node + "." + $attr) $val;
+ }
+ else
+ warning("Shave: can not add callback, attribute not found.");
+}
+
+
+proc waitForPlugin()
+{
+ global int $gShave_rmanWaitingForPlugin;
+
+ if (!$gShave_rmanWaitingForPlugin)
+ {
+ pluginInfo -cc shave_rmanCheckNewPlugin;
+ $gShave_rmanWaitingForPlugin = true;
+ }
+}
+
+
+// Global procedures
+global proc shave_rmanInit()
+{
+ // we do not want to set callbacks in defaultRenderGlobals when loading renderman plugin - dub|22Jun2012
+ // not clear why its handled diffent way then for other renderers
+ //shave_rmanSetupCallbacks();
+}
+
+global proc shave_rmanCleanup()
+{
+ //aslo we want to let these get saved with the scene - dub|22Jun2012
+ // not clear why its handled diffent way then for other renderers
+ //shave_rmanRemoveCallbacks();
+}
+
+
+global proc shave_rmanSetupCallbacks()
+{
+ //print "tryint to set renderMan callbacks\n";
+
+ // If there's no shaveGlobals node yet then there can't be any hair in
+ // the scene so there's no reason to set up callbacks yet.
+
+ //no we can not do this cut off because render will fail with this steps:
+ //set renderer to prman, create hair, render
+ //if (!objExists("shaveGlobals"))
+ //{
+ // //warning("Shave: shaveGlobals not found ");
+ // return;
+ //}
+
+ // If the rman plugin isn't loaded yet, we'll need to wait for it.
+ if (!shave_rmanPluginIsLoaded())
+ {
+ waitForPlugin();
+ //warning("Shave: renderMan is not loaded.");
+ return;
+ }
+
+ string $globals = getRManGlobals();
+
+ if ($globals == "")
+ {
+ warning("Shave: renderMan globals not found. Giving up.");
+ return;
+ }
+
+ addCallback(
+ $globals,
+ "rman__torattr___renderBeginScript",
+ "shave_rmanRenderStart"
+ );
+
+ addCallback(
+// $globals,
+ "defaultRenderGlobals",
+// "rman__torattr___preRenderScript",
+ "preRenderMel",
+ "shave_rmanFrameStart"
+ );
+
+ // RMS 19 and onward use an external prman instance which runs as a
+ // separate process. As a result all of the 'Post * MEL' scripts fire
+ // before the render has completed, making them basically useless.
+ //
+ // At the moment all that we do post render is to delete temp files
+ // generated for that render. We can instead do that using system
+ // commands injected into the rman stream.
+ //
+ if (getRManVersion() >= 19) {
+ addCallback(
+ $globals,
+ "rman__torattr___preRenderScript",
+ "shave_rmanSetOptions"
+ );
+
+ addCallback(
+ $globals,
+ "rman__torattr___postRenderScript",
+ "shave_rmanInjectCleanup"
+ );
+ } else {
+ addCallback(
+ "defaultRenderGlobals",
+ "postRenderMel",
+ "shave_rmanFrameEnd"
+ );
+ }
+
+ addCallback(
+ $globals,
+ "rman__torattr___postTransformScript",
+ "shave_rmanInsertArchive"
+ );
+
+ //print "done.\n";
+}
+
+
+global proc shave_rmanRemoveCallbacks()
+{
+ //print "renderMan callbacks removed\n";
+
+ string $globals = getRManGlobals();
+
+ removeCallback(
+ $globals,
+ "rman__torattr___renderBeginScript",
+ "shave_rmanRenderStart"
+ );
+
+ removeCallback(
+// $globals,
+ "defaultRenderGlobals",
+// "rman__torattr___preRenderScript",
+ "preRenderMel",
+ "shave_rmanFrameStart"
+ );
+
+ if (getRManVersion() >= 19) {
+ addCallback(
+ $globals,
+ "rman__torattr___preRenderScript",
+ "shave_rmanSetOptions"
+ );
+
+ removeCallback(
+ $globals,
+ "rman__torattr___postRenderScript",
+ "shave_rmanInjectCleanup"
+ );
+ } else {
+ removeCallback(
+ "defaultRenderGlobals",
+ "postRenderMel",
+ "shave_rmanFrameEnd"
+ );
+ }
+
+ removeCallback(
+ $globals,
+ "rman__torattr___postTransformScript",
+ "shave_rmanInsertArchive"
+ );
+}
+
+
+global proc shave_rmanRenderStart()
+{
+ global int $gShave_rmanArchiveInserted;
+ $gShave_rmanArchiveInserted = false;
+}
+
+
+global proc shave_rmanSetOptions()
+{
+ addShaderPath();
+}
+
+
+global proc string shave_rmanFrameStart()
+{
+ ///////////////// dumps //////////////
+ /*
+ string $globals = getRManGlobals();
+
+ $mob =`getAttr ($globals + ".rman__torattr___motionBlur")`;
+ print "prman: Motion Blur:"; print $mob; print "\n";
+
+ $cab =`getAttr ($globals + ".rman__torattr___cameraBlur")`;
+ print "prman: Motion Blur:"; print $cab; print "\n";
+
+
+ $a =`getAttr ($globals + ".rman__toropt___shutterAngle")`;
+ print "prman: Sutter Angle:"; print $a; print "\n";
+
+ $o =`getAttr ($globals + ".rman__riopt__Camera_shutteropening0")`;
+ print "prman: Sutter Open:"; print $o; print "\n";
+
+
+ $c =`getAttr ($globals + ".rman__riopt__Camera_shutteropening1")`;
+ print "prman: Sutter Close:"; print $c; print "\n";
+
+
+ $t =`getAttr ($globals + ".rman__toropt___shutterTiming")`;
+ print "prman: Sutter Timing:"; print $t; print "\n";
+
+
+ $k =`getAttr ($globals + ".rman__toropt___motionBlurType")`;
+ print "prman: Blur Type:"; print $k; print "\n";
+ */
+ //////////////////////////////////////
+
+ global string $gShave_rmanArchiveFiles[];
+
+ float $frame = `currentTime -q`;
+ string $dir = shaveGetTempDir();
+ string $ribFile = `shaveUtil -makeTempFileName $dir "shave" ("." + $frame + ".rib")`;
+
+ $gShave_rmanArchiveFiles = `shaveWriteRib -fullPaths $ribFile`;
+
+ if (size($gShave_rmanArchiveFiles) == 0) return "";
+
+ return $gShave_rmanArchiveFiles[0];
+}
+
+
+global proc shave_rmanFrameEnd()
+{
+ global string $gShave_rmanArchiveFiles[];
+
+ if (objExists("shaveGlobals") && !getAttr("shaveGlobals.ribKeepRibFiles"))
+ {
+ string $file;
+
+ for ($file in $gShave_rmanArchiveFiles)
+ sysFile -del $file;
+ }
+
+ clear $gShave_rmanArchiveFiles;
+}
+
+
+// We no longer need this but some people have it embedded in their
+// renderManGlobals now so we keep it around to prevent errors.
+global proc shave_rmanRenderEnd()
+{
+}
+
+
+global proc string shave_rmanGetReadArchiveCmd()
+{
+ global string $gShave_rmanArchiveFiles[];
+
+ if (size($gShave_rmanArchiveFiles) > 0)
+ return ("ReadArchive \"" + $gShave_rmanArchiveFiles[0] + "\"");
+
+ return "";
+}
+
+
+global proc shave_rmanInsertArchive()
+{
+ global int $gShave_rmanArchiveInserted;
+
+ if (!$gShave_rmanArchiveInserted) {
+ global string $gShave_rmanArchiveFiles[];
+
+ // We need to insert our archive under a suitable transform node.
+ // Shave's group node would be fine for that, however we cannot
+ // guarantee that it will be named 'shaveDisplayGroup': the user may
+ // have renamed it or it may be prefaced by a namespace if loaded from
+ // a reference.
+ //
+ // Instead we'll use the group of the first hairnode to get rendered.
+ //
+ string $groups[] = shave_getDisplayGroups();
+ string $group;
+ string $curObj = eval("rman ctxGetObject");
+
+ for ($group in $groups) {
+ if ($curObj == $group) {
+ // If there are multiple files the first one is the base file
+ // which includes all the others. So we always just want to
+ // emit the first one into the RIB stream.
+ //
+ if (size($gShave_rmanArchiveFiles) > 0)
+ {
+ eval("RiReadArchive(\"" + $gShave_rmanArchiveFiles[0] + "\")");
+ }
+
+ $gShave_rmanArchiveInserted = true;
+ break;
+ }
+ }
+ }
+}
+
+
+global proc shave_rmanInjectCleanup()
+{
+ global string $gShave_rmanArchiveFiles[];
+
+ if (objExists("shaveGlobals") && !getAttr("shaveGlobals.ribKeepRibFiles"))
+ {
+ string $file;
+
+ for ($file in $gShave_rmanArchiveFiles) {
+ if (`about -win`) {
+ // Convert the file path to a Windows-friendly form.
+ //
+ $file = toNativePath($file);
+
+ // All backslashes in the path must be escaped so that they
+ // don't get misinterpreted by rman's 'System' command.
+ //
+ $file = substituteAllString($file, "\\", "\\\\");
+
+ string $cmd = "System \"cmd.exe /x /a /c del \\\"" + $file + "\\\"\"";
+ RiArchiveRecord("verbatim", $cmd);
+ } else {
+ string $cmd = "System \"rm \\\"" + $file + "\\\"\"";
+ RiArchiveRecord("verbatim", $cmd);
+ }
+ }
+ }
+
+ clear $gShave_rmanArchiveFiles;
+}
+
+
+global proc int shave_rmanPluginIsLoaded()
+{
+ if (`pluginInfo -q -loaded RenderMan_for_Maya`)
+ {
+ // Only the 'Pro' version supports RIB insertion so make sure that
+ // this is Pro.
+// string $version = `pluginInfo -q -version RenderMan_for_Maya`;
+
+// return endsWith($version, "Pro");
+ string $isProVersion = eval("exists(\"RiAttribute\")");
+
+ return $isProVersion;
+ }
+
+ return false;
+}
+
+
+global proc shave_rmanCheckNewPlugin()
+{
+ print "shave_rmanCheckNewPlugin\n";
+
+ // There's no way to remove a pluginInfo callback if Shave is unloaded
+ // so let's first make sure that Shave is still loaded.
+ if (`pluginInfo -q -loaded shaveNode`)
+ {
+ //print "plugInfo - OK\n";
+
+ global int $gShave_rmanPluginIsLoaded;
+
+ // Is the rman plugin loaded?
+ if (shave_rmanPluginIsLoaded())
+ {
+ //print "rmanPluginIsLoaded - OK\n";
+
+ // If it wasn't loaded before then do our rman initialization.
+ if (!$gShave_rmanPluginIsLoaded)
+ {
+ //print "gShave_rmanPluginIsLoaded - OK\n";
+
+ $gShave_rmanPluginIsLoaded = true;
+ shave_rmanInit();
+ }
+ }
+ else
+ {
+ $gShave_rmanPluginIsLoaded = false;
+ }
+ }
+}
+
diff --git a/scripts/shaveRunTimeCommands.mel b/scripts/shaveRunTimeCommands.mel
new file mode 100644
index 0000000..b35699c
--- /dev/null
+++ b/scripts/shaveRunTimeCommands.mel
@@ -0,0 +1,501 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveRunTimeCommands_fileVersion = "$Revision$";
+
+
+//
+// The commands in this script form the external MEL interface for Shave.
+// Users can use these commands in their own scripts and we will strive to
+// ensure that they don't change from version to version. We won't be
+// able to guarantee that all the time -- for example if a feature is
+// removed from the product or completely changes in nature, then any
+// corresponding commands may no longer be valid -- but we will try to
+// keep such changes to a minimum.
+//
+
+proc registerCommand(string $commandName, string $ann, string $cmd)
+{
+ if (`runTimeCommand -exists $commandName`)
+ runTimeCommand -e -cat "Shave" -c $cmd -ann $ann $commandName;
+ else
+ runTimeCommand -cat "Shave" -c $cmd -ann $ann $commandName;
+}
+
+
+global proc shaveRunTimeCommands()
+{
+ //
+ // Menu Items
+ //
+ registerCommand(
+ "shaveSelectPresetAndCreateHair",
+ "Shave: select a preset and create a new hair node from it",
+ "shave_createHairFromPresetSelect"
+ );
+
+ registerCommand(
+ "shaveMultiClone",
+ "Shave: clone node heirarchy with hair",
+ "shave_multiClone"
+ );
+
+ registerCommand(
+ "shaveCombFromCurves",
+ "Shave: comb hair to match selected curves",
+ "shave_recomb"
+ );
+
+ registerCommand(
+ "shaveSelectAndApplyPreset",
+ "Shave: select a preset and apply it to the current hair node",
+ "shave_copyHairFromPresetSelect"
+ );
+
+ registerCommand(
+ "shaveSetInstance",
+ "Shave: set instance object",
+ "shave_setInstance"
+ );
+
+ registerCommand(
+ "shaveClearInstance",
+ "Shave: clear instance object",
+ "shave_clearInstance"
+ );
+
+ registerCommand(
+ "shaveUpdateGrowthSurfaces",
+ "Shave: change the set of surfaces from which hair grows",
+ "shave_setHairMesh"
+ );
+
+ registerCommand(
+ "shaveUpdateCollisionSurfaces",
+ "Shave: change the set of surfaces with which hair collides",
+ "shave_setCollisionMesh"
+ );
+
+ registerCommand(
+ "shaveClearCollisionSurfaces",
+ "Shave: clear the set of surfaces with which hair collides",
+ "shave_clearCollisionMesh"
+ );
+
+ registerCommand(
+ "shaveCreateSplineLocks",
+ "Shave: lock the current hair node's guides to a set of selected curves",
+ "shave_createSplineLocks"
+ );
+
+ registerCommand(
+ "shaveDeleteSplineLocks",
+ "Shave: if the current hair node is spline locked, unlock it",
+ "shave_deleteSplineLocks"
+ );
+
+ registerCommand(
+ "shaveCreateCurvesFromGuides",
+ "Shave: create NURBS curves matching the current hair node's guides",
+ "shave_guidesToCurves"
+ );
+
+ registerCommand(
+ "shaveCreateCurvesFromHairs",
+ "Shave: create NURBS curves matching the current hair node's hairs",
+ "shave_hairsToCurves"
+ );
+
+ registerCommand(
+ "shaveCreatePolysFromHairs",
+ "Shave: create poly meshes matching the current hair node's hairs",
+ "shave_hairsToPolys"
+ );
+
+ registerCommand(
+ "shaveCreateVertexShader",
+ "Shave: create a vertex shader",
+ "shave_createVertexShader \"\""
+ );
+
+ registerCommand(
+ "shaveSynchVertexShader",
+ "Shave: re-synch a vertex shader to match its corresponding hair node",
+ "shave_synchShader \"\""
+ );
+
+ registerCommand(
+ "shaveUVLinkingEditor",
+ "Shave: edit associations between Shave parameters and surface UV maps",
+ "shaveLinkingEditor"
+ );
+
+ registerCommand(
+ "shaveResetRestPose",
+ "Shave: reset the current hair node's rest pose",
+ "shave_resetRest"
+ );
+
+ registerCommand(
+ "shaveDelete",
+ "Shave: delete the current hair node",
+ "shaveDeleteCurrent"
+ );
+
+ registerCommand(
+ "shaveScaleSelectAll",
+ "Shave: set scale select for all nodes",
+ "shave_ScaleSelectAll"
+ );
+
+ registerCommand(
+ "shaveScaleSelectCurrent",
+ "Shave: set scale select for current node",
+ "shave_ScaleSelectCurrent"
+ );
+
+ registerCommand(
+ "shaveResetRestPose",
+ "Shave: reset the current hair node's rest pose",
+ "shave_resetRest"
+ );
+
+ registerCommand(
+ "shaveEditGlobalSettings",
+ "Shave: edit global settings",
+ "shaveGlobalsEditor"
+ );
+
+ registerCommand(
+ "shaveSelectGrowthSurfaces",
+ "Shave: select the current hair node's growth surfaces",
+ "shave_selectMesh growth"
+ );
+
+ registerCommand(
+ "shaveSelectCollisionSurfaces",
+ "Shave: select the current hair node's collision surfaces",
+ "shave_selectMesh collision"
+ );
+
+ registerCommand(
+ "shaveSynchToTextures",
+ "Shave: synchronize the current hair node with its textures",
+ "shaveUpdateTextures"
+ );
+
+ registerCommand(
+ "shaveToggleHideHair",
+ "Shave: hide and unhide all the hair in the scene",
+ "shave_toggleHideHair"
+ );
+
+ registerCommand(
+ "shaveToggleFallback",
+ "Shave: fallback hair count on various operations",
+ "shave_toggleFallbackHair"
+ );
+
+ registerCommand(
+ "shaveCreateShelf",
+ "Shave: create a shelf containing shave styling tools",
+ "shaveShelf_create"
+ );
+
+ //
+ // Light Shadow Parameters
+ //
+ registerCommand(
+ "shaveAddShadowParamsToLights",
+ "Shave: add shadow parameters to selected lights",
+ "shave_addShadowParamsToLight \"\""
+ );
+
+ registerCommand(
+ "shaveRemoveShadowParamsFromLights",
+ "Shave: remove shadow parameters from selected lights",
+ "shave_removeShadowParamsFromLight \"\""
+ );
+
+ //
+ // Dynamics
+ //
+ registerCommand(
+ "shaveToggleLiveMode",
+ "Shave: turn Live Mode (interactive dynamics) on and off",
+ "shave_toggleLive"
+ );
+
+ registerCommand(
+ "shaveRunDynamicsCurrent",
+ "Shave: run and cache dynamics for the current hair node",
+ "shaveDynamics current"
+ );
+
+ registerCommand(
+ "shaveRunDynamicsAll",
+ "Shave: run and cache dynamics for all hair nodes",
+ "shaveDynamics all"
+ );
+
+ registerCommand(
+ "shaveClearDynamicsCurrent",
+ "Shave: clear the dynamics cache for the current hair node",
+ "shave_clearDynamics \"\""
+ );
+
+ registerCommand(
+ "shaveClearDynamicsAll",
+ "Shave: clear the dynamics cache for all hair nodes",
+ "shave_clearDynamics all"
+ );
+
+ //
+ // Component Selection Types
+ //
+ registerCommand(
+ "shaveSelectGuides",
+ "Shave: select guides",
+ "shaveBrush_setSelectMode guide"
+ );
+
+ registerCommand(
+ "shaveSelectVerts",
+ "Shave: select vertices",
+ "shaveBrush_setSelectMode vert"
+ );
+
+ registerCommand(
+ "shaveSelectByRoots",
+ "Shave: select guides by their roots",
+ "shaveBrush_setSelectMode root"
+ );
+
+ registerCommand(
+ "shaveSelectByTips",
+ "Shave: select only the tips of guides",
+ "shaveBrush_setSelectMode tip"
+ );
+
+ //
+ // Selection Utilities
+ //
+ registerCommand(
+ "shaveSelectGrow",
+ "Shave: grow selection by adding surrounding hairs",
+ "shaveStyle -growSelection"
+ );
+
+ registerCommand(
+ "shaveSelectInverse",
+ "Shave: invert selection",
+ "shaveStyle -invertSelection"
+ );
+
+ registerCommand(
+ "shaveSelectRotateUp",
+ "Shave: move selected verts higher up hairs",
+ "shaveStyle -rotateSelectionUp"
+ );
+
+ registerCommand(
+ "shaveSelectHide",
+ "Shave: hide selected hairs",
+ "shaveStyle -hideSelection true"
+ );
+
+ registerCommand(
+ "shaveSelectUnhide",
+ "Shave: unhide selected hairs",
+ "shaveStyle -hideSelection false"
+ );
+
+ registerCommand(
+ "shaveSplitSelection",
+ "Shave: split selected hairs from interpolation groups",
+ "shaveStyle -splitSelection"
+ );
+
+ registerCommand(
+ "shaveMergeSelection",
+ "Shave: merge interpolation groups of selected hairs",
+ "shaveStyle -mergeSelection"
+ );
+
+ registerCommand(
+ "shaveUpdateTexturesN",
+ "Shave: refresh textures",
+ "shaveUpdateTextures"
+ );
+
+
+ //
+ // Brush Modes
+ //
+ registerCommand(
+ "shaveBrushTranslateMode",
+ "Shave: Brush Mode: move/translate hair",
+ "shaveBrushSetMode 0"
+ );
+
+ registerCommand(
+ "shaveBrushScaleMode",
+ "Shave: Brush Mode: resize/scale hair",
+ "shaveBrushSetMode 1"
+ );
+
+ registerCommand(
+ "shaveBrushRotateMode",
+ "Shave: Brush Mode: rotate hair about brush",
+ "shaveBrushSetMode 2"
+ );
+
+ registerCommand(
+ "shaveBrushStandMode",
+ "Shave: Brush Mode: stand hair straight out from surface",
+ "shaveBrushSetMode 3"
+ );
+
+ registerCommand(
+ "shaveBrushPuffMode",
+ "Shave: Brush Mode: puff hair out from surface",
+ "shaveBrushSetMode 4"
+ );
+
+ registerCommand(
+ "shaveBrushClumpMode",
+ "Shave: Brush Mode: clump hair together",
+ "shaveBrushSetMode 5"
+ );
+
+ //
+ // Styling Tools
+ //
+ registerCommand(
+ "shaveBrushTool",
+ "Shave: Brush Tool: brush/style hair",
+ "shave_brushTool false"
+ );
+
+ registerCommand(
+ "shaveBrushToolOptions",
+ "Shave: Brush Tool options",
+ "shave_brushTool true"
+ );
+
+ registerCommand(
+ "shaveCutTool",
+ "Shave: Cut Tool: cut hair",
+ "shave_cutTool false"
+ );
+
+ registerCommand(
+ "shaveCutToolOptions",
+ "Shave: Cut Tool options",
+ "shave_cutTool true"
+ );
+
+ //
+ // Styling Utilities
+ //
+ registerCommand(
+ "shaveAttenuate",
+ "Shave: attenuate hair",
+ "shaveStyle -attenuate"
+ );
+
+ registerCommand(
+ "shavePopSelected",
+ "Shave: pop selected hairs",
+ "shaveStyle -popSelected"
+ );
+
+ registerCommand(
+ "shavePopZeroSized",
+ "Shave: pop zero-sized hairs",
+ "shaveStyle -popZeroSized"
+ );
+
+ registerCommand(
+ "shaveRecomb",
+ "Shave: recomb hair",
+ "shaveStyle -recomb"
+ );
+
+ registerCommand(
+ "shaveReplaceRest",
+ "Shave: replace rest position on hair",
+ "shaveStyle -replaceRest"
+ );
+
+ registerCommand(
+ "shaveTglCollision",
+ "Shave: toggle collisions",
+ "shaveStyle -tglCollision"
+ );
+
+ registerCommand(
+ "shaveTglHairs",
+ "Shave: toggle hairs",
+ "shaveToggleHideHair"
+ );
+
+ registerCommand(
+ "shaveTglFallback",
+ "Shave: toggle fallback",
+ "shaveToggleFallback"
+ );
+
+ registerCommand(
+ "shaveLockHair",
+ "Shave: lock hairs",
+ "shaveStyle -lock true"
+ );
+
+
+ registerCommand(
+ "shaveUnlockHair",
+ "Shave: unlock hairs",
+ "shaveStyle -lock false"
+ );
+
+ registerCommand(
+ "shaveUndo",
+ "Shave: undo last operation",
+ "shaveStyle -undo"
+ );
+}
+
+
+global proc shaveRunTimeCommands_cleanup()
+{
+ string $cmds[] = `runTimeCommand -q -userCommandArray`;
+ string $cmd;
+
+ for ($cmd in $cmds)
+ {
+ if (`runTimeCommand -q -cat $cmd` == "Shave")
+ runTimeCommand -e -delete $cmd;
+ }
+}
+
+
+global proc shaveStyle_rotateUp()
+{
+ //
+ // Rotate Up is only meaningful in vertex selection mode. However, if
+ // the user is in tip selection mode then it can be useful to switch to
+ // vertex selection mode so that that tip selection can be rotated
+ // around to the bottom of the guide.
+ //
+ // So if we're in component mode and are selecting tips, switch to
+ // selecting vertices.
+ //
+ if (`selectMode -q -co`)
+ {
+ if (`optionVar -q shaveBrushSelectMode` == "tip") shaveSelectVerts;
+
+ shaveStyle -rotateSelectionUp;
+ }
+}
diff --git a/scripts/shaveShelf.mel b/scripts/shaveShelf.mel
new file mode 100644
index 0000000..66d1048
--- /dev/null
+++ b/scripts/shaveShelf.mel
@@ -0,0 +1,352 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveShelf_fileVersion = "$Revision$";
+
+global int $gShaveShelf_version = 7;
+
+
+proc string[] getSelectModeButtonInfo(string $button)
+{
+ string $result[];
+ string $label = `shelfButton -q -label $button`;
+
+ switch ($label)
+ {
+ case "Guides":
+ $result[0] = "guide";
+ $result[1] = "shaveSQSelectStrand.xpm";
+ $result[2] = "shaveSelectStrand.xpm";
+ break;
+
+ case "Verts":
+ $result[0] = "vert";
+ $result[1] = "shaveSQSelectVert.xpm";
+ $result[2] = "shaveSelectVert.xpm";
+ break;
+
+ case "Tips":
+ $result[0] = "tip";
+ $result[1] = "shaveSQSelectTip.xpm";
+ $result[2] = "shaveSelectTip.xpm";
+ break;
+
+ case "Roots":
+ $result[0] = "root";
+ $result[1] = "shaveSQSelectRoot.xpm";
+ $result[2] = "shaveSelectRoot.xpm";
+ break;
+ }
+
+ return $result;
+}
+
+
+global proc shaveShelf()
+{
+ //
+ // If Maya's shelf layout doesn't exist, then there's nothing for us to
+ // do.
+ //
+ global string $gShelfTopLevel;
+
+ if (($gShelfTopLevel == "") || !`shelfTabLayout -exists $gShelfTopLevel`)
+ return;
+
+ //
+ // Has this user ever had the Shave shelf before?
+ //
+ global int $gShaveShelf_version;
+ int $oldVersion = 0;
+
+ if (`optionVar -exists shaveShelf_version`)
+ $oldVersion = `optionVar -q shaveShelf_version`;
+
+ optionVar -iv shaveShelf_version $gShaveShelf_version;
+
+ //
+ // %%% Right now we just blow away the shelf and recreate it if its
+ // version is out of date. Before we release this should be
+ // changed to do a more thoughtful upgrade of the existing shelf.
+ //
+ if ($oldVersion != $gShaveShelf_version) shaveShelf_create;
+}
+
+
+global proc shaveShelf_create()
+{
+ //
+ // If Maya's shelf layout doesn't exist, then there's nothing for us to
+ // do.
+ //
+ global string $gShelfTopLevel;
+
+ if (($gShelfTopLevel == "") || !`shelfTabLayout -exists $gShelfTopLevel`)
+ return;
+
+ //
+ // Does the Shave shelf already exist?
+ //
+ int $shelfNum;
+ int $numShelves = `optionVar -q numShelves`;
+
+ for ($shelfNum = 1; $shelfNum <= $numShelves; $shelfNum++)
+ {
+ string $name = `optionVar -q ("shelfName" + $shelfNum)`;
+
+ if ($name == "Shave")
+ {
+ //
+ // If the shelf bar is not being displayed, then it's possible
+ // that while the optionVar for our shelf exists, the shelf
+ // itself has not yet been created. In that case we'll
+ // silently return without doing anything: the fact that the
+ // optionVar exists means that Maya will almost certainly
+ // create the shelf for us the next time the shelf bar is
+ // displayed.
+ //
+ if (!`shelfLayout -exists Shave`) return;
+
+ break;
+ }
+ }
+
+ //
+ // If the shelf already exists, clear it, otherwise create it.
+ //
+ if ($shelfNum <= $numShelves)
+ {
+ string $children[] = `shelfLayout -q -ca Shave`;
+ string $child;
+
+ for ($child in $children) deleteUI $child;
+ }
+ else
+ {
+ optionVar -iv numShelves $shelfNum;
+ optionVar -sv ("shelfName" + $shelfNum) "Shave";
+ optionVar -sv ("shelfFile" + $shelfNum) "shelf_Shave";
+ optionVar -iv ("shelfLoad" + $shelfNum) true;
+
+ setParent $gShelfTopLevel;
+
+ shelfLayout Shave;
+ }
+
+ //
+ // Populate the shelf.
+ //
+ // We create a named command for each shelf button. If we don't do
+ // that then when the user saves out the shelf (or Maya does it for
+ // them) the underlying command will be saved out as well, making it
+ // difficult for us to later changed the command. With named commands
+ // only the named command will be saved out and we can still change the
+ // underlying command that it represents.
+ //
+ setParent Shave;
+
+ // Component selection types.
+ shelfButton
+ -label "Guides"
+ -image "shaveSelectStrand.xpm"
+ -command shaveSelectGuides;
+
+ shelfButton
+ -label "Verts"
+ -image "shaveSelectVert.xpm"
+ -command shaveSelectVerts;
+
+ shelfButton
+ -label "Roots"
+ -image "shaveSelectRoot.xpm"
+ -command shaveSelectByRoots;
+
+ shelfButton
+ -label "Tips"
+ -image "shaveSelectTip.xpm"
+ -command shaveSelectByTips;
+
+ shelfButton
+ -annotation "Shave: Separator"
+ -enable false;
+
+ // Selection utilities
+ shelfButton
+ -label "Grow Selection"
+ -image "shaveSelectGrow.xpm"
+ -command shaveSelectGrow;
+
+ shelfButton
+ -label "Invert Selection"
+ -image "shaveSelectInverse.xpm"
+ -command shaveSelectInverse;
+
+ shelfButton
+ -label "Rotate Selection Up"
+ -image "shaveSelectRotateUp.xpm"
+ -command shaveSelectRotateUp;
+
+ shelfButton
+ -label "Select Hide"
+ -image "shaveSelectHide.xpm"
+ -command shaveSelectHide;
+
+ shelfButton
+ -label "Select Unhide"
+ -image "shaveSelectUnHide.xpm"
+ -command shaveSelectUnhide;
+
+ shelfButton
+ -annotation "separator"
+ -enable false;
+
+ // Tools
+ shelfButton
+ -label "Brush"
+ -image1 "shaveBrush.xpm"
+ -command shaveBrushTool
+ -doubleClickCommand shaveBrushToolOptions;
+
+ shelfButton
+ -label "Cut"
+ -image1 "shaveCut.xpm"
+ -command shaveCutTool
+ -doubleClickCommand shaveCutTool;
+
+ shelfButton
+ -annotation "separator"
+ -enable false;
+
+ // Utilities
+ shelfButton
+ -label "Attenuate"
+ -image1 "shaveAttenuate.xpm"
+ -command shaveAttenuate;
+
+ shelfButton
+ -label "Pop Selected"
+ -image1 "shavePopSelect.xpm"
+ -command shavePopSelected;
+
+ shelfButton
+ -label "Pop Zero-Sized"
+ -image1 "shavePopZeroSized.xpm"
+ -command shavePopZeroSized;
+
+ shelfButton
+ -label "Recomb"
+ -image1 "shaveRecomb.xpm"
+ -command shaveRecomb;
+
+ shelfButton
+ -label "Replace Rest"
+ -image1 "shaveReplaceRest.xpm"
+ -command shaveReplaceRest;
+
+ shelfButton
+ -label "Toggle Collision"
+ -image1 "shaveToggleCollision.xpm"
+ -command shaveTglCollision;
+
+ shelfButton
+ -label "Toggle Hairs"
+ -image1 "shaveToggleHairs.xpm"
+ -command shaveTglHairs;
+
+ shelfButton
+ -label "Toggle Preview"
+ -image1 "shaveToggleFallback.xpm"
+ -command shaveTglFallback;
+
+ shelfButton
+ -label "Lock"
+ -image1 "shaveLock.xpm"
+ -command shaveLockHair;
+
+ shelfButton
+ -label "Unlock"
+ -image1 "shaveUnlock.xpm"
+ -command shaveUnlockHair;
+
+ shelfButton
+ -label "Undo Style"
+ -image1 "shaveUndo.xpm"
+ -command shaveUndo;
+
+ // Additional selection utilities
+ shelfButton
+ -annotation "separator"
+ -enable false;
+
+ shelfButton
+ -label "Split Selection"
+ -image "shaveSplit.xpm"
+ -command shaveSplitSelection;
+
+ shelfButton
+ -label "Merge Selection"
+ -image "shaveMerge.xpm"
+ -command shaveMergeSelection;
+
+ shelfButton
+ -annotation "separator"
+ -enable false;
+
+ shelfButton
+ -label "Update Textures"
+ -image "textureRefresh.xpm"
+ -command shaveUpdateTexturesN;
+
+ //
+ // To avoid duplicating annotation strings and possibly getting out of
+ // synch, set each button's annotation string to be the same as its
+ // runTimeCommand.
+ //
+ string $buttons[] = `shelfLayout -q -ca Shave`;
+ string $button;
+
+ for ($button in $buttons)
+ {
+ string $cmd = `shelfButton -q -command $button`;
+
+ if (($cmd != "") && `runTimeCommand -exists $cmd`)
+ {
+ string $ann = `runTimeCommand -q -ann $cmd`;
+ shelfButton -e -annotation $ann $button;
+ }
+ }
+
+ shelfStyle("nochange", "nochange", "Shave");
+
+ shaveShelf_updateSelectModeButtons(`optionVar -q shaveBrushSelectMode`);
+}
+
+
+global proc shaveShelf_notImplemented(string $op)
+{
+ warning("The '" + $op + "' operation is not yet implemented.");
+}
+
+
+global proc shaveShelf_updateSelectModeButtons(string $mode)
+{
+ if (`shelfLayout -q -exists Shave`)
+ {
+ string $buttons[] = `shelfLayout -q -ca Shave`;
+ string $button;
+
+ for ($button in $buttons)
+ {
+ string $buttonInfo[] = getSelectModeButtonInfo($button);
+
+ if (size($buttonInfo) > 0)
+ {
+ if ($buttonInfo[0] == $mode)
+ shelfButton -e -image $buttonInfo[1] $button;
+ else
+ shelfButton -e -image $buttonInfo[2] $button;
+ }
+ }
+ }
+}
diff --git a/scripts/shaveUI.mel b/scripts/shaveUI.mel
new file mode 100644
index 0000000..7d0c459
--- /dev/null
+++ b/scripts/shaveUI.mel
@@ -0,0 +1,8328 @@
+// 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 [email protected]"
+ );
+ }
+ }
+
+ 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 [email protected]"
+ );
+ }
+ }
+
+ 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 <nodeName>_hairSet, and
+ // collision objects in one named <nodeName>_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;
+ }
+}
diff --git a/scripts/shaveVersion.mel b/scripts/shaveVersion.mel
new file mode 100644
index 0000000..5ad948e
--- /dev/null
+++ b/scripts/shaveVersion.mel
@@ -0,0 +1,10 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global string $shaveVersion_fileVersion = "$Revision$";
+
+global proc string shaveVersion()
+{
+ return "9.6v21";
+}
diff --git a/scripts/shaveVrayPostRender.mel b/scripts/shaveVrayPostRender.mel
new file mode 100644
index 0000000..a34bae0
--- /dev/null
+++ b/scripts/shaveVrayPostRender.mel
@@ -0,0 +1,24 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global proc shaveVrayPostRender()
+{
+ //global string $shaveVrayOldPostRender;
+ //execute original stuff
+ //we do not need to to this as far we chained attributes
+ //if($shaveVrayOldPostRender != "")
+ // eval $shaveVrayOldPostRender;
+
+ if(`whatIs shaveNode` != "Unknown")
+ {
+ string $shaveHairShapes[] = `ls -type shaveHair`;
+ if (size($shaveHairShapes) > 0)
+ {
+ shaveRender -renderEnd;
+
+ //all shaders we created on pre-render step
+ //shaveVrayShader -delete -all;
+ }
+ }
+}
diff --git a/scripts/shaveVrayPreRender.mel b/scripts/shaveVrayPreRender.mel
new file mode 100644
index 0000000..ef554bf
--- /dev/null
+++ b/scripts/shaveVrayPreRender.mel
@@ -0,0 +1,29 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+global proc shaveVrayPreRender()
+{
+ /*global*/ string $shaveVrayOldPreRender;
+
+ //execute original stuff
+ //no need as far we chained it
+ //if($shaveVrayOldPreRender != "")
+ // eval $shaveVrayOldPreRender;
+
+
+ if(`whatIs shaveNode` != "Unknown")
+ {
+ string $shaveHairShapes[] = `ls -type shaveHair`;
+ if (size($shaveHairShapes) > 0)
+ {
+ shaveRender -renderStart;
+
+ //attach shaders for all existing in the scene shave shapes
+ //shaveVrayShader -create -all;
+ }
+ if(`whatIs shaveClearObsoleteVrayCallbacks` != "Unknown")
+ shaveClearObsoleteVrayCallbacks;
+ }
+
+}