// =========================================================================== // Copyright 2018 Autodesk, Inc. All rights reserved. // // Use of this software is subject to the terms of the Autodesk license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // =========================================================================== // // // Creation Date: Oct 6, 2000 // // Description: // This script creates menu items to add shaders to an object. // proc string[] classificationsSuitableFor(string $item) { // // Description: // This procedure is called when building a menu of existing shaders or // shader types of a new shader to assign. In some cases, the shader is to // be assigned to a particular item, in which case $item != "". In other // cases the shader is to be assigned to the selected objects. // // This procedure determines the classifications of shaders or shader // types most appropriate to display in the menu. In the situation where // $item is specified, the classifications returned will be the // classifications of shaders which are most appropriate to assign to the // specified item. If $item is not specified, the classifications returned // will be the most commonly appropriate classifications. // // Returns: // A string array of classifications. // string $shapes[] = `ls -shapes -dag -ni -visible $item`; if( size( $shapes ) > 0 ){ $item = $shapes[0]; } int $itemRenderType = -1; int $particleRenderType = -1; if ($item != "") { // // An item has been specified to which the new shader should be // assigned. We will attempt to determine its render type and/or // particle render type. // string $lsResult[]; $lsResult = `ls ($item + ".renderType")`; if (size($lsResult) > 0) { // The item has a renderType attribute, so we will query its // value. // $itemRenderType = `getAttr ($item + ".renderType")`; } clear($lsResult); $lsResult = `ls ($item + ".particleRenderType")`; if (size($lsResult) > 0) { // The item has a particleRenderType attribute, so we will // query its value. // $particleRenderType = `getAttr ($item + ".particleRenderType")`; } } // The $classificationArray variable will be used to store the // classification strings of shaders which would be appropriate to // show in the menu. We will determine the classifications based on the // renderType and/or particleRenderType of the item. // string $classificationArray[]; if ( ($particleRenderType == 8) || ($particleRenderType == 9)) { // The object is a cloud or tube particle render type. // Only particle shaders are valid for this type. // $classificationArray[0] = "shader/volume/particle"; } else if ($itemRenderType == 0) { // The object is a "surface" render type. // $classificationArray[0] = "shader/surface"; } else if ($itemRenderType == 1) { // The object is a "volume" render type. // $classificationArray[0] = "shader/volume/fog"; } else { // The object is neither a "surface" nor "volume" render type. // Most likely we got to here because no $item was specified. // $classificationArray[0] = "shader/surface"; $classificationArray[1] = "shader/volume"; } return $classificationArray; } proc addFavouriteMenuItems(string $menuItems[], string $menuItemAnnotationFormat, string $item) { if(size($menuItems) > 1) menuItem -divider true; for ($i = 0; $i < size($menuItems)-1; $i++) { // // Add a menu item to assign the favourite material to the // specified item, or to the current selection if item is // unspecified. // string $materialUIlabel = $menuItems[$i++]; string $materialNodeID = $menuItems[$i]; string $createMaterialAnnotation = `format -stringArg $materialNodeID $menuItemAnnotationFormat `; $menu = `menuItem -label $materialUIlabel -annotation $createMaterialAnnotation -command ("createAndAssignShader " + $materialNodeID + " \""+$item+"\"")`; } } global proc buildAssignFavouriteMenu(string $parent, string $item) { // // Description: // This procedure is called to build a menu of "favourite" shaders which can // be assigned to the specified $item or to the current selection. // source "renderCreateBarUI.mel"; // for access to the favourites routines setParent -menu $parent; menu -edit -deleteAllItems $parent; string $favouriteArray[] = renderCreateBarFavorites(); string $mayaSurfaceShaders[], $mayaVolumeShaders[], $thirdPartyShaders[]; string $createMaterialAnnotationFormat = (uiRes("m_buildShaderMenus.kCreateMaterial")); int $i; for ($i = 0; $i < size($favouriteArray); $i++) { // // Loop through all the favourites so we can sort them and display the shaders // in order of the different shader types. And weed out the materials that // can't be applied to surfaces. // string $materialCategory = $favouriteArray[$i++]; string $materialNodeID = $favouriteArray[$i]; string $buffer[]; int $numTokens = tokenize($materialCategory, "/", $buffer); //Basic sanity check if($numTokens < 2) continue; string $materialUIlabel = $buffer[$numTokens-1]; if("Maya" == $buffer[0]) { if(`getClassification -satisfies "shader/volume" $materialNodeID`) { $mayaVolumeShaders[size($mayaVolumeShaders)] = $materialUIlabel; $mayaVolumeShaders[size($mayaVolumeShaders)] = $materialNodeID; } else if(`getClassification -satisfies "shader/surface" $materialNodeID`) { $mayaSurfaceShaders[size($mayaSurfaceShaders)] = $materialUIlabel; $mayaSurfaceShaders[size($mayaSurfaceShaders)] = $materialNodeID; } } else { // Find out if at least one third party claimed that the current item can be // used as a material. If so, we're going to add to the favorite menu items. int $isThirdPartyMaterialResults[] = `callbacks -executeCallbacks -hook "nodeCanBeUsedAsMaterial" $materialNodeID $buffer[0]`; for($isThirdPartyMaterial in $isThirdPartyMaterialResults) { if($isThirdPartyMaterial == 1) { $thirdPartyShaders[size($thirdPartyShaders)] = $materialUIlabel; $thirdPartyShaders[size($thirdPartyShaders)] = $materialNodeID; break; } } } // TO DO: Bug 339220 currently adding protein materials to the favourites fails. Support for protein // materials in favourite requires further development. :( } addFavouriteMenuItems($mayaSurfaceShaders, $createMaterialAnnotationFormat, $item); addFavouriteMenuItems($mayaVolumeShaders, $createMaterialAnnotationFormat, $item); addFavouriteMenuItems($thirdPartyShaders, $createMaterialAnnotationFormat, $item); menuItem -divider true; menuItem -label (uiRes("m_buildShaderMenus.kCreateAttributeEditor")) -annotation (uiRes("m_buildShaderMenus.kCreateAttributeEditorAnnoation")) -checkBox (`optionVar -q AEpopupWhenCreatingShaders`) -command "optionVar -intValue AEpopupWhenCreatingShaders #1" attrEdPopupItem; setParent -menu ..; } global proc buildAssignShaderMenu(string $parent, string $item) { // // Description: // This procedure is called to build a menu of existing shaders which can // be assigned to the specified $item or to the current selection. // setParent -menu $parent; menu -edit -deleteAllItems $parent; // Determine the classifications of shaders which would be most appropriate // to assign to the specified $item. If $item is unspecified, this will // return the most commonly appropriate classifications. // string $classificationArray[] = classificationsSuitableFor($item); int $i; for ($i = 0; $i < size($classificationArray); $i++) { // Insert a divider if we are about to add a second section of // shaders to this menu. This divider will separate, for example, all // surface shaders from all volume shaders. // if ($i != 0) menuItem -divider true; // Get a list of all node types which are classified according to the // current classification // string $types[] = `listNodeTypes $classificationArray[$i]`; for ($type in $types) { // Get a list of all material nodes which have the current node type // string $material; string $materialArray[] = `ls -exactType $type`; for ( $material in $materialArray ) { // // Add a menu item to assign the current material to the // specified item, or to the current selection if item is // unspecified. // string $assignMaterialAnnotationFormat = (uiRes("m_buildShaderMenus.kAssignMaterial")); string $assignMaterialAnnotation = `format -stringArg $material $assignMaterialAnnotationFormat `; $menu = `menuItem -label $material -annotation $assignMaterialAnnotation`; if ($item == "") { if( nodeType($material) == "oceanShader" ) { menuItem -edit -command ("assignOceanShader " + $material) $menu; } else { menuItem -edit -command ("hyperShade -assign " + $material) $menu; } } else { menuItem -edit -command ("assignSG " + $material + " " + $item) $menu; } // Add an option box to the menu item which will bring up the // material in the attribute editor // string $assignMaterialOptionBoxLabelFormat = (uiRes("m_buildShaderMenus.kAssignMaterialOptionBoxLabel")); string $assignMaterialOptionBoxLabel = `format -stringArg $material $assignMaterialOptionBoxLabelFormat `; string $assignMaterialOptionBoxAnnotationFormat = (uiRes("m_buildShaderMenus.kAssignMaterialOptionBoxAnnotation")); string $assignMaterialOptionBoxAnnotation = `format -stringArg $material $assignMaterialOptionBoxAnnotationFormat `; menuItem -optionBox true -annotation $assignMaterialOptionBoxAnnotation -label $assignMaterialOptionBoxLabel -command ( "showEditor " + $material ); } } } string $containers[] = `ls -type container -type dagContainer`; string $containerMenu; int $addedContainerParentMenu = 0; for ($container in $containers) { int $uiInfo = `getAttr ($container+".uiTreatment")`; string $customUI = `getAttr ($container+".customTreatment")`; if ($uiInfo == 1 || ($uiInfo == 1000 && (gmatch($customUI,"shader")))) { if ($addedContainerParentMenu == 0) { $containerMenu = `menuItem -label (uiRes("m_buildShaderMenus.kShaderContainers")) -subMenu true -allowOptionBoxes true`; setParent -menu $containerMenu; $addedContainerParentMenu = 1; } string $assignContainerAnnotationFormat = (uiRes("m_buildShaderMenus.kAssignContainer")); string $assignContainerAnnotation = `format -stringArg $container $assignContainerAnnotationFormat `; $menu = `menuItem -label $container -annotation $assignContainerAnnotation -c ("containerAssignMaterial \""+$container+"\" \""+$item+"\"")`; } } if ($addedContainerParentMenu) { setParent -menu ..; } } global proc updateAssignShaderMenu() { // Make sure checkbox is in correct state // menuItem -edit -checkBox (`optionVar -q AEpopupWhenCreatingShaders`) attrEdPopupItem; } global proc buildShaderMenus (string $item) // // Creates 2 menus. One for assiging shaders using existing shaders and // the other by creating a new one. // // $item = object to act on, or null string if selection list should be used // { // If there is a selection, it overrides item if ( size(`ls -sl`) > 0 ) { $item = ""; } // First menu creates the shader and then assigns it. // menuItem -label (uiRes("m_buildShaderMenus.kAssignNewMaterialMenuItem")) -command ("createAssignNewMaterialTreeLister " + "\""+$item+"\""); setParent -menu ..; string $menuAssign; // Second menu assigns from existing scene shaders. // $menuAssign = `menuItem -label (uiRes("m_buildShaderMenus.kAssignFavouriteMaterial")) -subMenu true -allowOptionBoxes true`; menuItem -edit -postMenuCommand ("buildAssignFavouriteMenu " + $menuAssign + " \""+$item+"\"") $menuAssign; setParent -menu ..; // Third menu assigns from existing scene shaders. // $menuAssign = `menuItem -label (uiRes("m_buildShaderMenus.kAssignExistingMaterial")) -subMenu true -allowOptionBoxes true`; menuItem -edit -postMenuCommand ("buildAssignShaderMenu " + $menuAssign + " \""+$item+"\"") $menuAssign; setParent -menu ..; } global proc refreshAssignNewMaterialTreeLister(string $item) // // Rebuild the treeLister when there has been a change // in selection so new materials will be appropriately // assigned to those objects. // { // We only want to refresh the treeLister if the // window is actually open and not minimized. // if (`window -exists assignNewMaterialWindow` && `window -q -visible assignNewMaterialWindow` && !`window -q -iconify assignNewMaterialWindow`) { // // Additionally, we only want to refresh the treeLister // in three cases: // 1. If the current treeLister is based off of the // selection list and not off of a given object. // 2. If the current selection list is non-empty. // 3. Given that the above conditions fail, then if // the given object the current treeLister is based // from has been deleted. // string $objs[] = `ls -selection`; if(size($item) == 0 || size($objs) > 0 || !`objExists $item`) { // The refreshed treeLister will be based from // the selection list. createAssignNewMaterialTreeLister ""; } } } global proc createAssignNewMaterialTreeLister(string $item) // // Creates the treeLister window to be used to create and // assign a new shader. // // $item = object to act on, or null string if selection list should be used // { source "createAndAssignShader.mel"; // Save the current selection list. // string $objs[]; if ($item == "") { // No object was specified in the call to this procedure, so we will // assign the shader to whatever is on the current selection list // instead. // $objs = `ls -selection`; } else { // // The $item is always an object, never a component (ie face). // If the current selection contains faces of the specified item, then // we would rather assign the new shader to the specifically selected // faces rather than the object as a whole. In particular, this allows // users to select faces of a poly object and use the RMB menu to // assign shaders to them. // string $selection[] = `ls -selection`; int $i; string $selFacesMsg = (uiRes("m_buildShaderMenus.kSelectedFaces")); for ($i = 0; $i < size($selection); $i++) { if (`match ($item + "\\.f\\[.*\\]") $selection[$i]` != "") { // One part of the currently selection is faces of the // specified item. We will add the selected faces to the list // of objects to which the shader will be assigned. // $objs[size($objs)] = $selection[$i]; } } if (size($objs) == 0) { // Try again with the shape. Face selection will be names // after the shape when other shapes are parented below the // transform. string $shapes[] = `listRelatives -s $item`; if (size($shapes) > 0 ) { string $shape = $shapes[0]; for ($i = 0; $i < size($selection); $i++) { if (`match ($shape + "\\.f\\[.*\\]") $selection[$i]` != "") { $objs[size($objs)] = $selection[$i]; } } } } if (size($objs) == 0) { // There were no faces of the specified item in the current // selection. Therefore, we will assign the shader to the entire // object specified. // $objs[0] = $item; } } // If there are no selected objects and the window is not already open in // some capacity (being minimized counts as opened), throw an error, since // we want to have selected objects to assign materials to. // if (size($objs) == 0 && (!`window -exists assignNewMaterialWindow` || !`window -q -visible assignNewMaterialWindow`)) { error((uiRes("m_buildShaderMenus.kNoObjectsSelected"))); return; } // Store selection array into a string to be passed on. string $objsString = stringArrayToString($objs, " "); // Determine and save the classifications of the shaders and // shader types most appropriate to assign to the item. // string $classificationArray[] = classificationsSuitableFor($item); // Store classification array into a string to be passed on. string $filterString = stringArrayToString($classificationArray, " "); // Give 3rd parties a chance to append to the classification string filter string $thirdPartiesClassificationStrings[] = `callbacks -executeCallbacks -hook "provideClassificationStringsForFilteredTreeLister" $filterString`; for( $thirdPartiesClassificationString in $thirdPartiesClassificationStrings ) { $filterString = $filterString + " " + $thirdPartiesClassificationString; } // Create the window and treeLister. // int $anmWindowWidth = 300; global string $gAssignNewMaterialWindowTreeLister; global string $gAssignNewMaterialWindowLockButton; string $assignNewMaterial = ((uiRes("m_buildShaderMenus.kAssignNewMaterialWinTitle")) + $objsString); string $postCommand = ("assignCreatedShader %type \"" + $item + "\" %node \"" + $objsString + "\""); // Change the window title if there are no selected objects, // since in this case you can't actually assign the material // to anything. // if(size($objs) == 0) { $assignNewMaterial = (uiRes("m_buildShaderMenus.kCreateNewMaterial")); } if (!`window -exists assignNewMaterialWindow`) { window -rtf false -title $assignNewMaterial -iconName $assignNewMaterial -menuBar true -maximizeButton false -ret -wh $anmWindowWidth 730 assignNewMaterialWindow; // // Setup up the menu bar // menu -label (uiRes("m_buildShaderMenus.kOptions")) optionsMenu; menuItem -label (uiRes("m_buildShaderMenus.kAutomaticAttributeEditor")) -annotation (uiRes("m_buildShaderMenus.kAutomaticAttributeEditorAnnoation")) -checkBox (`optionVar -q AEpopupWhenCreatingShaders`) -command "optionVar -intValue AEpopupWhenCreatingShaders #1" attrEdPopupItem; setParent -m ..; // from optionsMenu formLayout assignWindowForm; $gAssignNewMaterialWindowTreeLister = createRenderNodeTreeLister($postCommand, $filterString); $gAssignNewMaterialWindowLockButton = `iconTextCheckBox -w 26 -h 26 -image1 "lock.png" -annotation (uiRes("m_buildShaderMenus.kLockButtonAnnotation"))`; button -label (uiRes("m_buildShaderMenus.kClose")) -h 26 -c "window -e -vis false assignNewMaterialWindow" closeButton; setParent ..; formLayout -e -af $gAssignNewMaterialWindowTreeLister "left" 1 -af $gAssignNewMaterialWindowTreeLister "right" 1 -af $gAssignNewMaterialWindowTreeLister "top" 1 -ac $gAssignNewMaterialWindowTreeLister "bottom" 5 closeButton -af $gAssignNewMaterialWindowLockButton "left" 5 -an $gAssignNewMaterialWindowLockButton "right" -af $gAssignNewMaterialWindowLockButton "bottom" 5 -an $gAssignNewMaterialWindowLockButton "top" -ac closeButton "left" 0 $gAssignNewMaterialWindowLockButton -af closeButton "right" 5 -af closeButton "bottom" 5 -an closeButton "top" assignWindowForm; // Set a script job to refresh the treeLister if the selection changes. // scriptJob -event "SelectionChanged" "refreshAssignNewMaterialTreeLister \"\""; // select Favorites node to avoid MAYA-79886 nodeTreeLister -e -selectPath "Favorites" $gAssignNewMaterialWindowTreeLister; } else { window -e -title $assignNewMaterial assignNewMaterialWindow; refreshRenderNodeTreeLister($gAssignNewMaterialWindowTreeLister, $postCommand, $filterString); } // Set the window to refresh the treeLister when it is restored after // being minimized, just in case the selection has changed since then. // string $command = ("refreshAssignNewMaterialTreeLister \"" + $item + "\""); window -e -restoreCommand $command assignNewMaterialWindow; showWindow assignNewMaterialWindow; }