diff options
| author | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
|---|---|---|
| committer | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
| commit | bd0027e737c6512397f841c22786274ed74b927f (patch) | |
| tree | f7ffbdb8f3741bb7f24635616cc189cba5cb865c /scripts | |
| download | archived-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.mel | 924 | ||||
| -rw-r--r-- | scripts/AEshaveHairTemplate.mel | 876 | ||||
| -rw-r--r-- | scripts/AEshaveNodeTemplate.mel | 520 | ||||
| -rw-r--r-- | scripts/shaveAEOverrides.mel | 52 | ||||
| -rw-r--r-- | scripts/shaveBrush.mel | 422 | ||||
| -rw-r--r-- | scripts/shaveBrushProperties.mel | 21 | ||||
| -rw-r--r-- | scripts/shaveBrushSetModeButton.mel | 49 | ||||
| -rw-r--r-- | scripts/shaveBrushValues.mel | 19 | ||||
| -rw-r--r-- | scripts/shaveCheckVersion.mel | 173 | ||||
| -rw-r--r-- | scripts/shaveCursorCtxCommonProperties.mel | 90 | ||||
| -rw-r--r-- | scripts/shaveCursorCtxCommonValues.mel | 49 | ||||
| -rw-r--r-- | scripts/shaveCutProperties.mel | 12 | ||||
| -rw-r--r-- | scripts/shaveCutValues.mel | 12 | ||||
| -rw-r--r-- | scripts/shaveDiag.mel | 1266 | ||||
| -rw-r--r-- | scripts/shavePresetWin.mel | 456 | ||||
| -rw-r--r-- | scripts/shaveRelationshipEditor.mel | 1621 | ||||
| -rw-r--r-- | scripts/shaveRenderman.mel | 528 | ||||
| -rw-r--r-- | scripts/shaveRunTimeCommands.mel | 501 | ||||
| -rw-r--r-- | scripts/shaveShelf.mel | 352 | ||||
| -rw-r--r-- | scripts/shaveUI.mel | 8328 | ||||
| -rw-r--r-- | scripts/shaveVersion.mel | 10 | ||||
| -rw-r--r-- | scripts/shaveVrayPostRender.mel | 24 | ||||
| -rw-r--r-- | scripts/shaveVrayPreRender.mel | 29 |
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; + } + +} |