// =========================================================================== // 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. // =========================================================================== // Copyright 2002-2004, NVIDIA // // // Changes: // 10/2003 Kurt Harriman - www.octopusgraphics.com +1-415-893-1023 // Added support for multiple UV sets and user-specified // texcoord assignment. // 12/2003 Kurt Harriman - www.octopusgraphics.com +1-415-893-1023 // Added radio buttons for choosing "technique" attribute. // Direction/position vector connections are now made/broken // via a right-click popup menu, replacing the old drop-down // menu which could get out of sync with the scene contents. // "Desc" or "UIName" annotations are displayed as tooltips. // Setting the "shader" attribute no longer causes loading of // the specified CgFX file. Instead the "-fx " // option of the cgfxShader command is used. // Work around a Maya bug that caused UI elements to be overlaid. // Numerous UI touch-ups and fixes. // 1/2004 Kurt Harriman - www.octopusgraphics.com +1-415-893-1023 // Added user-definable tool buttons. // // ============================================================================ // ================ Layout Procedures for CgFX Parameters ===================== // ============================================================================ // Each of these procedures takes 2 arguments: // $plug = "node.attr" // $param = a string array containing the parameter name and description: // [0] = attribute name // [1] = cgfxShader parameter type // [2] = semantic specified in the .fx file (or " " if no semantic) // [3] = description given by "Desc" or "UIName" annotation in the .fx file // [4] = suffix for extra attribute name if Color4/Vector4, else " " // // For example, if the .fx file contains the declaration // float4 surfColor : Diffuse < string Desc = "Surface Color"; >; // then the AEcgfxColorLayout procedure is called with // $plug = "cgfxShader1.surfColor" // $param[0] = "surfColor" // $param[1] = "Color4" // $param[2] = "Diffuse" // $param[3] = "Surface Color" // $param[4] = "Alpha" // // Future versions of the cgfxShader plug-in may provide additional // items of information appended to the $param[] array. // ============================================================================ // // Layout a boolean attribute (code borrowed from AEnewBooleanGroup.mel) // global proc string AEcgfxShader_BoolLayout( string $plug, string $uiName, string $param[] ) { string $createdControl = `formLayout`; checkBoxGrp -ncb 1 -ann $param[0] valueFld; checkBoxGrp -e -l1 $uiName valueFld; formLayout -e -af valueFld top 0 -af valueFld left 0 -af valueFld right 0 -af valueFld bottom 0 $createdControl; connectControl -in 2 valueFld $plug; setParent ..; return $createdControl; } // Layout an integer attribute (code borrowed from AEnewInt.mel) // global proc string AEcgfxShader_IntLayout( string $plug, string $uiName, string $param[] ) { return `attrFieldSliderGrp -l $uiName -ann $param[0] -hideMapButton 1 -attribute $plug`; } // Layout a float attribute (code borrowed from AEnewFloat.mel) // global proc string AEcgfxShader_FloatLayout( string $plug, string $uiName, string $param[] ) { return `attrFieldSliderGrp -l $uiName -ann $param[0] -hideMapButton 1 -attribute $plug`; } // Layout a string attribute (code borrowed from AEnewString.mel) // global proc string AEcgfxShader_StringLayout( string $plug, string $uiName, string $param[] ) { string $createdControl = `textFieldGrp -l $uiName -ann $param[0]`; connectControl -index 2 $createdControl $plug; return $createdControl; } // Layout a vector2 attribute // global proc string AEcgfxShader_Vector2Layout( string $plug, string $uiName, string $param[] ) { string $cName = "Vec2"; string $createdControl; $createdControl = `columnLayout -adj true`; floatFieldGrp -l $uiName -ann $param[0] -numberOfFields 2 -cw3 132 100 100 $cName; connectControl -index 2 $cName ($plug+"X"); connectControl -index 3 $cName ($plug+"Y"); setParent ..; return $createdControl; } // Layout a vector3 attribute // global proc string AEcgfxShader_Vector3Layout( string $plug, string $uiName, string $param[] ) { string $cName = "Vec3"; string $createdControl; $createdControl = `columnLayout -adj true`; floatFieldGrp -l $uiName -ann $param[0] -numberOfFields 3 -cw4 132 67 66 67 $cName; connectControl -index 2 $cName ($plug+"X"); connectControl -index 3 $cName ($plug+"Y"); connectControl -index 4 $cName ($plug+"Z"); setParent ..; return $createdControl; } // Layout a vector4 attribute // global proc string AEcgfxShader_Vector4Layout( string $plug, string $uiName, string $param[] ) { string $cName = "Vec4"; string $createdControl; $createdControl = `columnLayout -adj true`; floatFieldGrp -l $uiName -ann $param[0] -numberOfFields 4 -cw5 132 50 50 50 50 $cName; connectControl -index 2 $cName ($plug+"X"); connectControl -index 3 $cName ($plug+"Y"); connectControl -index 4 $cName ($plug+"Z"); connectControl -index 5 $cName ($plug+"W"); setParent ..; return $createdControl; } // Layout a direction or position vector attribute // global proc string AEcgfxShader_DirectionLayout( string $plug, string $uiName, string $param[] ) { global int $gAEcgfxShader_iLayout; // IN: AE template instance id // Create the control. // We pass the attribute name to our callback procedures by // embedding it in the control name. string $sAttrName = `substitute "^[^.]*\\." $plug ""`; string $sArg = $gAEcgfxShader_iLayout + " " + $sAttrName; string $sControlRel = "cgfxDirection_" + $sAttrName; string $sControlAbs = `textFieldGrp -l $uiName -cc ( "AEcgfxShader_DirectionText " + $sArg + " \"#1\"" ) -ann ( $param[0] + " (RMB to connect/disconnect)" ) $sControlRel`; // Create popup menu. string $s = `popupMenu -postMenuCommand ( "AEcgfxShader_DirectionPopup " + $sArg ) dirPopup`; // Set the text of the control. evalDeferred ( "AEcgfxShader_DirectionUpdate " + $sArg ); return $sControlAbs; } // AEcgfxShader_DirectionLayout // Layout a color3 attribute (code borrowed from AEnewColor.mel) // global proc string AEcgfxShader_Color3Layout( string $plug, string $uiName, string $param[] ) { return `attrColorSliderGrp -l $uiName -ann $param[0] -showButton 0 -attribute $plug`; } // AEcgfxColor3Layout // Layout a color4 attribute // global proc string AEcgfxShader_Color4Layout( string $plug, string $uiName, string $param[] ) { string $createdControl = `columnLayout -adj true`; attrColorSliderGrp -l $uiName -ann ( $param[0] + "(rgb)" ) -showButton 0 -attribute $plug; // We just know that the 4th element will be in ($plug + "Alpha"); attrFieldSliderGrp -l "(alpha)" -ann ( $param[0] + "(a)" ) -hideMapButton 1 -attribute ( $plug + "Alpha" ); separator -style none; setParent ..; return $createdControl; } // AEcgfxColor4Layout // Layout a matrix attribute global proc string AEcgfxShader_MatrixLayout( string $plug, string $uiName, string $param[] ) { // TODO: Allow user to connect another node's matrix output // to our shader's matrix input. return ""; } // AEcgfxShader_MatrixLayout // Layout a texture attribute global proc string AEcgfxShader_TextureLayout( string $plug, string $uiName, string $param[] ) { // Is our shader using string filename or texture node textures? // (Based on TEXTURES_BY_NODE or TEXTURES_BY_NAME compile switch // used when compiling plugin) // if( `getAttr -type $plug` == "float3") { // Textures are specified as file texture nodes // return `attrNavigationControlGrp -l $uiName -ann $param[0] -attribute $plug`; } else { // Textures are specified as filename string attributes // string $sAttrName = `substitute "^[^.]*\\." $plug ""`; string $sControlRel = "cgfxFile_" + $sAttrName; string $sControlAbs = `rowLayout -nc 3 $sControlRel`; string $sDesc = $param[0] + " " + interToUI( $param[1] ); AEcgfxShader_fileNameControls( $sAttrName, // attribute name $uiName, // label $sDesc, // annotation "dds" , // file classification "/textures" ); // project directory connectControl -fileName tfFileName $plug; setParent ..; return $sControlAbs; } } // AEcgfxShader_TextureLayout // Layout an attribute that is not to be displayed. global proc string AEcgfxShader_SuppressLayout( string $plug, string $uiName, string $param[] ) { return ""; } // AEcgfxShader_SuppressLayout // Layout an attribute of a type for which UI support has not yet been implemented. global proc string AEcgfxShader_UnimplementedLayout( string $plug, string $uiName, string $param[] ) { return `text -l $uiName -enable 0 -ann $param[1]`; } // AEcgfxShader_UnimplementedLayout //////////////////////////////////////////////////////////////////////// // Direction/Position Vector Controls // //////////////////////////////////////////////////////////////////////// // Callback to create 1st level popup menu items for direction/position vector global proc AEcgfxShader_DirectionPopup( int $iLayout, string $sAttrName ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); string $sControlRel = "cgfxDirection_" + $sAttrName; string $sControlAbs = `setParent $sControlRel`; string $sPopupAbs = $sControlAbs + "|dirPopup"; popupMenu -e -deleteAllItems $sPopupAbs; setParent -menu $sPopupAbs; string $sCmd = "AEcgfxShader_DirectionPopup2 " + $iLayout + " " + $sAttrName + " "; string $saSubmenus[] = { "Lights", "Cameras", "Locators", "Selected" }; string $sSubmenu; int $iSubmenu; for ( $sSubmenu in $saSubmenus ) { menuItem -l $sSubmenu -subMenu 1 -postMenuCommand ( $sCmd + $iSubmenu ) -postMenuCommandOnce 1 ( "sub" + $iSubmenu ); setParent -menu ..; ++$iSubmenu; } menuItem -divider 1; $sCmd = "cgfxShader_connectVector " + // (in cgfxShader_util.mel) $sNodeName + "." + $sAttrName + " "; menuItem -l "Disconnect" -c ( $sCmd + "\"\"" ); } // AEcgfxShader_DirectionPopup // Callback to create 2nd level popup menu items global proc AEcgfxShader_DirectionPopup2( int $iLayout, string $sAttrName, int $iSubmenu ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); string $sControlRel = "cgfxDirection_" + $sAttrName; string $sControlAbs = `setParent $sControlRel`; string $sSubmenuAbs = $sControlAbs + "|dirPopup|sub" + $iSubmenu; setParent -menu $sSubmenuAbs; string $sCmd = "cgfxShader_connectVector " + $sNodeName + "." + $sAttrName + " "; string $shapes[]; string $shape; switch ( $iSubmenu ) { case 0: // Lights $shapes = `ls -type light`; break; case 1: // Cameras $shapes = `listCameras -perspective`; $shapes = sort( $shapes ); for ( $shape in $shapes ) menuItem -l $shape -c ( $sCmd + $shape ); menuItem -divider 1; $shapes = `listCameras -orthographic`; break; case 2: // Locators $shapes = `ls -type locator`; break; case 3: // Selected $shapes = `ls -sl`; break; } // Filter the node and/or attribute names. string $saMenuItems[]; for ( $shape in $shapes ) if ( !catch( $shape = AEcgfxShader_DirectionFilter( $shape ) ) && $shape != "" ) cgfxShader_appendUnique( $saMenuItems, $shape ); $saMenuItems = sort( $saMenuItems ); // Create menu items. string $sMenuItem; for ( $sMenuItem in $saMenuItems ) menuItem -l $sMenuItem -c ( $sCmd + $sMenuItem ); if ( !`popupMenu -q -numberOfItems $sSubmenuAbs` ) menuItem -l " (None) " -parent $sSubmenuAbs -enable 0; } // AEcgfxShader_DirectionPopup2 // Decide which selection items to include in direction/position vector popup menu. global proc string AEcgfxShader_DirectionFilter( string $sSelectionItem ) { string $sNode = `match "^[^.]*" $sSelectionItem`; // node if ( $sSelectionItem == $sNode ) { // For instanced objects, show only the first instance because // Maya doesn't provide worldMatrix[i] for i > 0. (Maya bug) string $sa[] = `listRelatives -allParents -path $sNode`; if ( size( `ls -shapes $sNode` ) ) $sNode = $sa[0]; else if ( size( `ls -typ dagNode $sNode` ) ) { if ( size( $sa ) > 1 ) $sNode = $sa[0] + "|" + `substitute ".*|" $sNode ""`; } else $sNode = ""; return $sNode; } // node.attr else { string $sAttr = `match "[^.]*$" $sSelectionItem`; string $saConnectable[] = `listAttr -read -scalar -connectable $sSelectionItem`; if ( size( $saConnectable ) == 1 ) // e.g. { "translateX" } { string $saParent[] = `attributeQuery -node $sNode -listParent $sAttr`; if ( !size( $saParent ) ) return ""; $sAttr = $saParent[0]; $sSelectionItem = $sNode + "." + $sAttr; $saConnectable = `listAttr -read -scalar -connectable $sSelectionItem`; } if ( size( $saConnectable ) == 3 ) // e.g. { "translateX", "translateY", "translateZ" } { string $saChildren[] = `attributeQuery -node $sNode -listChildren $sAttr`; if ( size( $saChildren ) != 3 ) return ""; int $i; for ( $i = 0; $i < 3; ++$i ) if ( $saChildren[ $i ] != $saConnectable[ $i ] ) return ""; return $sSelectionItem; } } return ""; } // AEcgfxShader_DirectionFilter // Callback when user changes the text in direction/position vector text field. global proc AEcgfxShader_DirectionText( int $iLayout, string $sAttrName, string $sText ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); string $sCmd; // If new text appears to be valid, make the requested connection. if ( $sText == "" ) $sText = "\"\""; else if ( catch( $sText = AEcgfxShader_DirectionFilter( $sText ) ) ) $sText = ""; if ( $sText != "" ) { $sCmd = "cgfxShader_connectVector " + $sNodeName + "." + $sAttrName + " " + $sText; print ( $sCmd + ";\n" ); evalDeferred $sCmd; // On undo/redo, Maya will display our cgfxShader_connectVector // command to show what was undone/redone, only if it is invoked // via "evalDeferred". We could call cgfxShader_connectVector // directly, but then what Maya displays on undo/redo would be the // invocation of the present function, AEcgfxShader_DirectionText, // which is not as informative for the user. And because this is // deferred, the call below to AEcgfxShader_DirectionUpdate must // be deferred too so it will happen afterwards. } // Update text field to show the result. $sCmd = "AEcgfxShader_DirectionUpdate " + $iLayout + " " + $sAttrName; evalDeferred $sCmd; } // AEcgfxShader_DirectionText // Callback to update UI after direction/position connection is made or broken. global proc AEcgfxShader_DirectionUpdate( int $iLayout, string $sAttrName ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); // Did node or attribute go away while our deferred // execution via "evalDeferred" was pending? if ( $sNodeName == "" || !size( `listAttr -st $sAttrName $sNodeName` ) ) return; // Call me back when a connection to this attr is made or broken. string $sControlRel = "cgfxDirection_" + $sAttrName; string $plug = $sNodeName + "." + $sAttrName; string $sArg = $iLayout + " " + $sAttrName; string $sCmd = "AEcgfxShader_DirectionUpdate " + $sArg; $sCmd = "evalDeferred \"" + $sCmd + "\""; scriptJob -parent $sControlRel -replacePrevious -killWithScene -runOnce 1 -compressUndo 1 -connectionChange $plug $sCmd; // What is the attribute connected to? string $connectedTo = ""; string $connections[] = `listConnections -s 1 -d 0 $plug`; if (size($connections) > 0) { if (`nodeType $connections[0]` == "cgfxVector") { // Call me when the cgfxVector input is connected or disconnected. string $sVectorInput = $connections[0] + ".matrix"; scriptJob -parent $sControlRel -killWithScene -runOnce 1 -compressUndo 1 -connectionChange $sVectorInput $sCmd; // See what's on the other side of the cgfxVector. $connections = `listConnections -s 1 -d 0 $sVectorInput`; if (size($connections) > 0) $connectedTo = $connections[0]; } else { // It was not a cgfxVector so it must be his own magic. // get the exact attribute. $connections = `listConnections -s 1 -d 0 -p 1 $plug`; $connectedTo = $connections[0]; } } // Show name of connected node. textFieldGrp -e -text $connectedTo $sControlRel; // Don't need popup menu items anymore. Zap 'em. string $sControlAbs = `setParent $sControlRel`; string $sPopupAbs = $sControlAbs + "|dirPopup"; popupMenu -e -deleteAllItems $sPopupAbs; } // AEcgfxShader_DirectionUpdate //////////////////////////////////////////////////////////////////////// // File browser controls // //////////////////////////////////////////////////////////////////////// // Create controls for a file name attribute. global proc AEcgfxShader_fileNameControls( string $sAttrName, string $sLabel, string $sAnnotation, string $sFileClassification, string $projectDir ) { global int $gAEcgfxShader_iLayout; // IN: AE template instance id // Label text -l $sLabel -ann $sAnnotation; // Text field textField -ann $sAnnotation tfFileName; // Button to invoke file browser dialog string $sArgs = $gAEcgfxShader_iLayout + " " + $sAttrName + " " + $sFileClassification + " \"" + $projectDir + "\" "; symbolButton -image "navButtonBrowse.png" -c ( "AEcgfxShader_fileBrowser " + $sArgs ) -ann $sAnnotation; } // AEcgfxShader_fileNameLayout // Callback on button click to invoke file browser dialog. global proc AEcgfxShader_fileBrowser( int $iLayout, string $sAttrName, string $sFileClassification, string $projectDir ) { // Retrieve the node name. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); // Determine initial directory for file browser. string $sWorkspace = `workspace -q -fn`; setWorkingDirectory $sWorkspace $sFileClassification "CgFX"; // Start in current file's directory if possible. string $sOldDir = `cgfxShader -q -fxPath $sNodeName`; $sOldDir = `match ".*/" $sOldDir`; // If there isn't a current directory, use the project dir if( $sOldDir == "") $sOldDir = $sWorkspace + $projectDir; if ( $sOldDir != "" && $sOldDir != `workspace -q -dir` && `file -q -ex $sOldDir` && !catch( `workspace -dir $sOldDir` ) ) { retainWorkingDirectory( $sOldDir ); setWorkingDirectory $sWorkspace $sFileClassification "CgFX"; } // Set up args for callback. // In addition to the args we provide, the fileBrowser will // append two more: the chosen file name, and file type. string $sArgs = $iLayout + " " + $sAttrName; // Invoke the file browser dialog. fileBrowser( "AEcgfxShader_fileChoice " + $sArgs, (getPluginResource("cgfxShader", "kOpen")), "", 0 ); } // AEcgfxShader_fileBrowser // Callback when user clicks the "Open" button in the file browser // dialog, or changes the text in the file name text field. global proc AEcgfxShader_fileChoice( int $iLayout, string $sAttrName, string $sFile, string $sFileType ) { // Save the current working directory so the file browser will // start here next time. string $currentDir = `workspace -q -dir`; retainWorkingDirectory( $currentDir ); // Retrieve the node name. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); // Load shader or set attribute. if ( $sAttrName == "shader" ) AEcgfxShader_shaderLoad( $sNodeName, $sFile ); else { string $sCmd = "setAttr -type \"string\" " + $sNodeName + "." + $sAttrName + " \"" + $sFile + "\""; print ( $sCmd + ";\n" ); evalDeferred $sCmd; // Oddly, upon undo/redo, Maya will display our setAttr // command to show what was undone/redone, only if we use // evalDeferred to execute the setAttr. Otherwise undo/redo // would display the AEcgfxShader_fileChoice invocation, // which is not as interesting for the user. } } // AEcgfxShader_fileChoice //////////////////////////////////////////////////////////////////////// // "Technique" Controls // //////////////////////////////////////////////////////////////////////// // Create "Technique" radio buttons. global proc AEcgfxShader_techniqueLayout( string $sNodeName ) { global int $gAEcgfxShader_iLayout; // IN: AE instance id for callbacks // Get list of technique names defined in the current CgFX file. string $saTechniques[] = `cgfxShader -listTechniques $sNodeName`; int $nTechnique = size( $saTechniques ); if ( !$nTechnique ) { $saTechniques[0] = (getPluginResource("cgfxShader", "kNoneDefined")) + "\t0"; $nTechnique = 1; } // Get list of existing controls. string $saControls[] = `layout -q -childArray clTechnique`; int $nControl = size( $saControls ); setParent clTechnique; setUITemplate -pst attributeEditorTemplate; // Delete any surplus controls, in LIFO order to avoid crashing Maya. while ( $nControl > $nTechnique ) deleteUI $saControls[ --$nControl ]; // Create or update a radio button per technique. string $sa[]; string $sCmd = "AEcgfxShader_techniqueChoice " + $sNodeName; string $sActiveTechnique = `getAttr ( $sNodeName + ".technique" )`; int $iTechnique; for ( $iTechnique = 0; $iTechnique < $nTechnique; ++$iTechnique ) { tokenize( $saTechniques[ $iTechnique ], "\t", $sa ); string $sName = $sa[0]; int $nPass = $sa[1]; string $rbg = "rbgTechnique" + $iTechnique; if ( $iTechnique >= $nControl ) { if ( $iTechnique == 0 ) radioButtonGrp -numberOfRadioButtons 1 -label (getPluginResource("cgfxShader", "kTechnique")) $rbg; else radioButtonGrp -numberOfRadioButtons 1 -shareCollection rbgTechnique0 $rbg; } string $sLabel = $sName; if ( $nPass > 1 ) $sLabel += " (" + $nPass + "-pass)"; radioButtonGrp -e -enable1 ( $nPass > 0 ) -label1 $sLabel -on1 ( $sCmd + " \"" + $sName + "\"" ) $rbg; if ( $sName == $sActiveTechnique ) { radioButtonGrp -e -select 1 $rbg; } } setParent ..; // When "technique" attribute is changed, select corresponding button // and rebuild our varying parameter list $sCmd = "AEcgfxShader_techniqueUpdate " + $gAEcgfxShader_iLayout; $sCmd += "; evalDeferred \"AEcgfxShader_paramLayoutRebuild " + $gAEcgfxShader_iLayout + " 1; " + "AEcgfxShader_vertexAttributeUpdate " + $gAEcgfxShader_iLayout + " 1 0\""; scriptJob -parent clTechnique -replacePrevious -killWithScene -compressUndo 1 -attributeChange ( $sNodeName + ".technique" ) $sCmd; setUITemplate -ppt; } // AEcgfxShader_techniqueLayout // Callback when user chooses a "technique" radio button. global proc AEcgfxShader_techniqueChoice( string $sNodeName, string $sChoice ) { string $sCmd = "setAttr -type \"string\" " + $sNodeName + ".technique" + " \"" + $sChoice + "\""; print ( $sCmd + ";\n" ); evalDeferred $sCmd; // use evalDeferred so Maya will display // the setAttr command on undo/redo } // AEcgfxShader_techniqueChoice // Callback when value of "technique" attribute changes. global proc AEcgfxShader_techniqueUpdate( int $iLayout ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); // Get new value and find its index in the list of techniques. string $sActiveTechnique = `getAttr ( $sNodeName + ".technique" )`; string $saTechniques[] = `cgfxShader -listTechniques $sNodeName`; int $nTechnique = size( $saTechniques ); int $i; for ( $i = 0; $i < $nTechnique; ++$i ) if ( $sActiveTechnique == `match "^[^\t]*" $saTechniques[ $i ]` ) break; // Select corresponding button. radioButtonGrp -e -select 1 ( "rbgTechnique" + $i ); } //////////////////////////////////////////////////////////////////////// // "Profile" Controls // //////////////////////////////////////////////////////////////////////// // Create "Profile" radio buttons. global proc AEcgfxShader_profileLayout( string $sNodeName ) { global int $gAEcgfxShader_iLayout; // IN: AE instance id for callbacks // Get list of profile names defined in the current CgFX file. string $saProfiles[] = `cgfxShader -listProfiles $sNodeName`; int $nProfile = size( $saProfiles ); if ( !$nProfile ) { $saProfiles[0] = (getPluginResource("cgfxShader", "kNoneDefined")); $nProfile = 1; } setParent clProfile; setUITemplate -pst attributeEditorTemplate; // Get list of existing controls. string $saControls[] = `layout -q -childArray clProfile`; int $nControl = size( $saControls ); if ($nControl == 0) { // Create the option menu for the profile. string $sCmd = "AEcgfxShader_profileChoice " + $gAEcgfxShader_iLayout; optionMenuGrp -label (getPluginResource("cgfxShader", "kProfile")) -changeCommand $sCmd "omgProfile"; menuItem -label (getPluginResource("cgfxShader", "kDefault")); int $iProfile; for ($iProfile = 0; $iProfile < $nProfile; ++$iProfile) { string $sName = $saProfiles[ $iProfile ]; menuItem -label $sName; } } string $sActiveProfile = `getAttr ( $sNodeName + ".profile" )`; if ($sActiveProfile == "") $sActiveProfile = "Default"; optionMenuGrp -e -value $sActiveProfile "omgProfile"; setParent ..; // When "profile" attribute is changed, select corresponding button // and rebuild our varying parameter list $sCmd = "AEcgfxShader_profileUpdate " + $gAEcgfxShader_iLayout; $sCmd += "; evalDeferred \"AEcgfxShader_paramLayoutRebuild " + $gAEcgfxShader_iLayout + " 1; " + "AEcgfxShader_vertexAttributeUpdate " + $gAEcgfxShader_iLayout + " 1 0\""; scriptJob -parent clProfile -replacePrevious -killWithScene -compressUndo 1 -attributeChange ( $sNodeName + ".profile" ) $sCmd; setUITemplate -ppt; } // AEcgfxShader_profileLayout // Callback when user chooses a "profile" radio button. global proc AEcgfxShader_profileChoice( int $iLayout ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); string $sChoice = `optionMenuGrp -q -value "omgProfile"`; if ($sChoice == "Default") $sChoice = ""; string $sCmd = "setAttr -type \"string\" " + $sNodeName + ".profile" + " \"" + $sChoice + "\""; print ( $sCmd + ";\n" ); evalDeferred $sCmd; // use evalDeferred so Maya will display // the setAttr command on undo/redo } // AEcgfxShader_profileChoice // Callback when value of "profile" attribute changes. global proc AEcgfxShader_profileUpdate( int $iLayout ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); // Get new value and select corresponding button. string $sActiveProfile = `getAttr ( $sNodeName + ".profile" )`; if ($sActiveProfile == "") $sActiveProfile = "Default"; optionMenuGrp -e -value $sActiveProfile "omgProfile"; } //////////////////////////////////////////////////////////////////////// // Tool buttons // //////////////////////////////////////////////////////////////////////// // Callback when user clicks a tool button. Invoke the tool. global proc AEcgfxShader_toolEval( int $iLayout, string $sCmd ) { // Substitute file name and node name into the command. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent cgfxShaderLayout; $sCmd = cgfxShader_expandTags( $sCmd, AEcgfxShader_toolTags( $sNodeName ) ); // Invoke the tool. We use evalDeferred in case the tool command // is undoable, so undo/redo can display that command instead of // the present button click callback invocation. evalDeferred $sCmd; } // AEcgfxShader_toolEval // Return dictionary of tags and values for tool command expansion. // Caller has done "setParent" so this procedure can query the // contents of UI controls from the proper "CgFX Shader" layout. global proc string[] AEcgfxShader_toolTags( string $sNodeName ) { string $saDict[] = { "file", "\"" + `cgfxShader -q -fxPath $sNodeName` + "\"", "node", $sNodeName }; return $saDict; } // AEcgfxShader_toolEval // Create or update tool buttons. global proc AEcgfxShader_toolLayout( int $iLayout, int $bNew ) { int $buttonCount = 3; int $buttonStart = 0; if ( $bNew ) { string $sCmd = "AEcgfxShader_toolPopup " + $iLayout + " "; int $iButton; rowLayout -nc 6 -columnAlign6 "center" "center" "center" "center" "center" "center" rlTools; text -l ""; for ( $iButton = $buttonStart; $iButton < $buttonCount; ++$iButton ) { button -l "" -recomputeSize 0 ( "bTool" + $iButton ); popupMenu -postMenuCommand ( $sCmd + $iButton ) -allowOptionBoxes 1 pmTool; } symbolButton -image "info.png" bUrl; setParent ..; } else { setParent rlTools; string $saTools[]; if ( `optionVar -exists AEcgfxShader_toolButtons` ) $saTools = `optionVar -q AEcgfxShader_toolButtons`; string $saArgs[]; string $saWhichTool[] = cgfxShader_split( $saTools[0], "\t" ); int $iButton; for ( $iButton = $buttonStart; $iButton < $buttonCount; ++$iButton ) { // tool button loop // Empty the popup menu. string $sControlRel = "bTool" + $iButton; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupAbs = $sControlAbs + "|pmTool"; popupMenu -e -deleteAllItems $sPopupAbs; // Get the tool definition associated with this button. string $sName = $saWhichTool[ $iButton ]; int $iTool; int $nTool = size( $saTools ); for ( $iTool = 1; $iTool < $nTool; ++$iTool ) if ( $sName == `match "^[^\t]*" $saTools[ $iTool ]` ) break; clear $saArgs; if ( $iTool < $nTool ) $saArgs = cgfxShader_split( $saTools[ $iTool ], "\t" ); // Get tool command string and description. string $sCmd = $saArgs[2]; if ( $sCmd == "" ) $saArgs[1] = "LMB: Does nothing; " + ( ( $iTool < $nTool ) ? "empty command" : "not assigned" ) + ". RMB: Choose what this button should do."; else { if ( $saArgs[1] == "" ) $saArgs[1] = $sCmd; $sCmd = "AEcgfxShader_toolEval " + $iLayout + " \"" + encodeString( $sCmd ) + "\""; } button -e -l $saArgs[0] -ann $saArgs[1] -c $sCmd $sControlRel; } // tool button loop setParent ..; // end of rlTools } } // AEcgfxShader_toolLayout // Callback to create popup menu items for a tool button. global proc AEcgfxShader_toolPopup( int $iLayout, int $iButton ) { AEcgfxShader_beginCallback( $iLayout ); setParent cgfxShaderLayout; setParent rlTools; string $sControlRel = "bTool" + $iButton; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupAbs = $sControlAbs + "|pmTool"; popupMenu -e -deleteAllItems $sPopupAbs; setParent -menu $sPopupAbs; string $sCmd = "AEcgfxShader_toolChoice " + $iLayout + " " + $iButton + " "; string $sOpt = "AEcgfxShader_toolEditor " + $iLayout + " " + $iButton + " "; menuItem -l (getPluginResource("cgfxShader", "kChooseButtonAction")) -italicized 1; menuItem -divider 1; string $saTools[]; if ( `optionVar -exists AEcgfxShader_toolButtons` ) $saTools = `optionVar -q AEcgfxShader_toolButtons`; int $i; for ( $i = 1; $i < size( $saTools ); ++$i ) { string $saArgs[] = cgfxShader_split( $saTools[ $i ], "\t" ); if ( $saArgs[0] != "" ) { menuItem -l $saArgs[0] -ann ( $saArgs[1] + ": \"" + $saArgs[2] + "\"" ) -c ( $sCmd + $i ); menuItem -optionBox 1 -ann (getPluginResource("cgfxShader", "kEditActionDefinition")) -c ( $sOpt + $i ); } } menuItem -divider 1; menuItem -l (getPluginResource("cgfxShader","kNothing")) -italicized 1 -ann (getPluginResource("cgfxShader", "kButtonNotAssigned")) -c ( $sCmd + "0" ); menuItem -divider 1; menuItem -l (getPluginResource("cgfxShader","kNewAction")) -ann (getPluginResource("cgfxShader", "kAddNewMenuAction")) -c ( $sOpt + "0" ); } // AEcgfxShader_toolPopup // Callback when user chooses an item from a tool button's popup menu. global proc AEcgfxShader_toolChoice( int $iLayout, int $iButton, int $iTool ) { AEcgfxShader_beginCallback( $iLayout ); setParent cgfxShaderLayout; // Get tool definitions and button assignments. string $saTools[] = `optionVar -q AEcgfxShader_toolButtons`; // Associate the button with the chosen tool. string $sToolArgs[]; if ( $iTool ) $sToolArgs = cgfxShader_split( $saTools[ $iTool ], "\t" ); string $saWhichTool[] = cgfxShader_split( $saTools[0], "\t" ); $saWhichTool[ $iButton ] = $sToolArgs[0]; $saTools[0] = $saWhichTool[0] + "\t" + $saWhichTool[1] + "\t" + $saWhichTool[2]; // Rebuild the optionVar. optionVar -ca AEcgfxShader_toolButtons; string $sTool; for ( $sTool in $saTools ) optionVar -sva AEcgfxShader_toolButtons $sTool; // Update the tool buttons. AEcgfxShader_toolLayout( $iLayout, false ); } // AEcgfxShader_toolChoice // Create the tool editor window. global proc AEcgfxShader_toolEditor( int $iLayout, int $iButton, int $iTool ) { // Retrieve the node name. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); string $s; int $i; // Get the tool definition. string $saArgs[]; if ( $iTool && `optionVar -exists AEcgfxShader_toolButtons` ) { string $saTools[] = `optionVar -q AEcgfxShader_toolButtons`; $saArgs = cgfxShader_split( $saTools[ $iTool ], "\t" ); } // Create the tool editor window. string $sWindow = "AEcgfxShader_toolEditorWindow"; if ( `window -exists $sWindow` ) deleteUI -window $sWindow; window -title (getPluginResource("cgfxShader", "kToolButtonEditor")) -resizeToFitChildren 1 $sWindow; setUITemplate -pst attributeEditorTemplate; columnLayout -adj 1 clMain; separator; string $sCallback = "AEcgfxShader_toolEditorUpdate " + $iLayout + " " + $iButton + " "; columnLayout -adj 1; string $tfgName = `textFieldGrp -l (getPluginResource("cgfxShader", "kName")) -tx $saArgs[0] -cc ( $sCallback + "name" ) tfgName`; string $tfgDesc = `textFieldGrp -l (getPluginResource("cgfxShader", "kDescription")) -tx $saArgs[1] -cc ( $sCallback + "desc" ) tfgDesc`; formLayout flCmd; text -l (getPluginResource("cgfxShader", "kCommands")) txCmd; string $sfCmd = `scrollField -h 65 -w 280 -tx ( $saArgs[2] + "\n" ) -cc ( $sCallback + "cmd" ) -keyPressCommand ( $sCallback + "kpc" ) -wordWrap 1 -insertionPosition 1 sfCmd`; popupMenu pmCmd; { menuItem -l (getPluginResource("cgfxShader", "kInsertVariable") + ":") -italicized 1; menuItem -divider 1; string $s1 = "scrollField -e -it \""; string $s2 = "\" " + $sfCmd + "; " + $sCallback + "kpc"; string $saTags[]; string $sa[] = AEcgfxShader_toolTags( $sNodeName ); for ( $i = size( $sa ); $i > 1; $i -= 2 ) $saTags[ size( $saTags ) ] = "<" + $sa[ $i - 2 ] + ">\t" + $sa[ $i - 1 ]; $saTags = sort( $saTags ); for ( $s in $saTags ) { $sa = cgfxShader_split( $s, "\t" ); menuItem -l $sa[0] -ann ( $sa[0] + ": " + $sa[1] ) -c ( $s1 + encodeString( $sa[0] ) + $s2 ); } } setParent ..; formLayout -e -attachNone txCmd left -attachOppositeForm txCmd right -129 -attachForm txCmd top 4 -attachForm sfCmd top 2 -attachForm sfCmd bottom 2 -attachControl sfCmd left 4 txCmd -attachForm sfCmd right 10 flCmd; setParent ..; separator; formLayout flButtons; button -l (getPluginResource("cgfxShader", "kSave")) -c ( $sCallback + "save" ) -h 26 -w 68 -enable 0 bSave; button -l (getPluginResource("cgfxShader", "kCreate")) -c ( $sCallback + "save" ) -h 26 -w 68 -enable 0 bCreate; button -l (getPluginResource("cgfxShader", "kDelete")) -c ( $sCallback + "delete" ) -enable ( $iTool > 0 ) -h 26 -w 68 bDelete; button -l (getPluginResource("cgfxShader", "kClose")) -c ( "deleteUI -window " + $sWindow ) -h 26 -w 68 bClose; setParent ..; formLayout -e -attachForm bSave top 0 -attachForm bCreate top 0 -attachForm bDelete top 0 -attachForm bClose top 0 -attachForm bSave bottom 0 -attachForm bCreate bottom 0 -attachForm bDelete bottom 0 -attachForm bClose bottom 0 -attachOppositeForm bSave right -132 -attachControl bCreate left 2 bSave -attachControl bDelete left 2 bCreate -attachControl bClose left 2 bDelete flButtons; separator; setUITemplate -ppt; setParent ..; // end of clMain showWindow $sWindow; // Set focus to first empty field if any, else to the command field. // Doesn't take effect if done here, so use evalDeferred. $s = ( $saArgs[0] == "" ) ? $tfgName : ( $saArgs[1] == "" ) ? $tfgDesc : $sfCmd; evalDeferred ( "setFocus " + $s ); } // AEcgfxShader_toolEditor // Callback for tool editor window events. global proc AEcgfxShader_toolEditorUpdate( int $iLayout, int $iButton, string $sEvent ) { int $i; string $s; // Get tool property values from the tool editor window. setParent AEcgfxShader_toolEditorWindow; string $saArgs[]; $saArgs[0] = `textFieldGrp -q -tx tfgName`; $saArgs[1] = `textFieldGrp -q -tx tfgDesc`; $saArgs[2] = `scrollField -q -tx sfCmd`; // Strip leading and trailing whitespace. Remove tabs. for ( $i = 0; $i < 3; ++$i ) { $saArgs[ $i ] = substitute( "^[ \t\n\r]*", $saArgs[ $i ], "" ); $saArgs[ $i ] = substitute( "[ \t\n\r]*$", $saArgs[ $i ], "" ); while ( `match "\t" $saArgs[ $i ]` != "" ) $saArgs[ $i ] = `substitute "\t" $saArgs[ $i ] " "`; } // Get saved tool definitions. string $saTools[]; if ( `optionVar -exists AEcgfxShader_toolButtons` ) $saTools = `optionVar -q AEcgfxShader_toolButtons`; // Look for a tool definition matching the specified name. string $sName = $saArgs[0]; int $nTool = size( $saTools ); int $iTool; for ( $iTool = 1; $iTool < $nTool; ++$iTool ) if ( $sName == `match "^[^\t]*" $saTools[ $iTool ]` ) break; int $bFound = ( $iTool < $nTool ); int $bCommit = false; string $saWhichTool[]; // Delete if ( $sEvent == "delete" && $bFound ) { $saTools[ $iTool ] = ""; // Remove from button->tool map. $saWhichTool = cgfxShader_split( $saTools[0], "\t" ); for ( $iButton = 0; $iButton < size( $saWhichTool ); ++$iButton ) if ( $saWhichTool[ $iButton ] == $sName ) $saWhichTool[ $iButton ] = ""; $saTools[0] = ""; // Set new control state. $bCommit = true; $bFound = false; } // Ok to save or create? int $bCanSave = ( $sName != "" ); if ( $bFound ) { string $saSavedArgs[] = cgfxShader_split( $saTools[ $iTool ], "\t" ); if ( $saArgs[1] == $saSavedArgs[1] && $saArgs[2] == $saSavedArgs[2] ) $bCanSave = false; } // Save or create if ( $bCanSave && $sEvent == "save" ) { $saTools[ $iTool ] = $sName + "\t" + $saArgs[1] + "\t" + $saArgs[2]; // Assign this tool to the specified button. $saWhichTool = cgfxShader_split( $saTools[0], "\t" ); $saWhichTool[ $iButton ] = $sName; // Sort the definitions $saTools[0] = ""; $saTools = sort( $saTools ); $bCommit = true; // Set new control state. $bFound = true; $bCanSave = false; } // Rebuild the optionVar and update the tool buttons. if ( $bCommit ) { optionVar -ca AEcgfxShader_toolButtons -sva AEcgfxShader_toolButtons ( $saWhichTool[0] + "\t" + $saWhichTool[1] + "\t" + $saWhichTool[2] ); for ( $s in $saTools ) if ( $s != "" ) optionVar -sva AEcgfxShader_toolButtons $s; AEcgfxShader_toolLayout( $iLayout, false ); } // Update the tool editor window. button -e -enable ( $bCanSave && $bFound ) bSave; button -e -enable ( $bCanSave && !$bFound ) bCreate; button -e -enable $bFound bDelete; if ( $sEvent != "kpc" ) { textFieldGrp -e -tx $sName tfgName; textFieldGrp -e -tx $saArgs[1] tfgDesc; scrollField -e -tx $saArgs[2] sfCmd; } } // AEcgfxShader_toolEditorUpdate //////////////////////////////////////////////////////////////////////// // "CgFX Shader" Layout // //////////////////////////////////////////////////////////////////////// // This procedure is invoked when necessary to construct the controls // for a shader. Generally, this is only when we are displaying a // cgfxShader node for the first time. // global proc AEcgfxShader_shaderNew( string $sNodeAttr ) { global int $gAEcgfxShader_iLayout; // IN: AE instance id for callbacks // Create a column layout. setUITemplate -pst attributeEditorTemplate; columnLayout -adj 1 -visible 0 cgfxShaderLayout; // CgFX File string $sCmd = "AEcgfxShader_shaderText " + $gAEcgfxShader_iLayout; rowLayout -nc 3; AEcgfxShader_fileNameControls( "shader", // attribute name (getPluginResource("cgfxShader", "kCgFXFile")), // label "", // annotation "fx", // file classification "/renderData/shaders" ); // project directory textField -e -cc $sCmd tfFileName; setParent ..; // Tool buttons to reload the effect, browse online help, etc. AEcgfxShader_toolLayout( $gAEcgfxShader_iLayout, true ); // Description textFieldGrp -l "" -ann (getPluginResource("cgfxShader", "kShaderDescription")) -editable 0 tfgDescription; separator -style none; // Technique columnLayout -adj 1 clTechnique; setParent ..; separator -style none; // Profile columnLayout -adj 1 clProfile; setParent ..; separator -style none; // Clean up. Initialize the controls. setParent ..; setUITemplate -ppt; AEcgfxShader_shaderReplace( $sNodeAttr ); // Make the layout visible. layout -e -visible 1 cgfxShaderLayout; } // AEcgfxShader_shaderNew // This procedure is invoked to connect a given cgfxShader node to the UI // controls. If the layout already exists, the Attribute Editor will // call this routine instead of AEcgfxShader_shaderNew. // global proc AEcgfxShader_shaderReplace( string $sNodeAttr ) { global int $gAEcgfxShader_iLayout; // IN: AE instance id for callbacks string $sNodeName = `match "^[^.]*" $sNodeAttr`; string $sa[]; string $s; setParent cgfxShaderLayout; // CgFX file name textField -e -fileName `getAttr $sNodeAttr` tfFileName; // Description // This attribute name can be any mixture of upper and lower // case, as given in the .fx file. Use case-insensitive lookup // to get the exact spelling. $sa = `cgfxShader -parameter "description" -caseInsensitive -description $sNodeName`; $s = ""; if ( $sa[1] == "String" ) $s = `getAttr ( $sNodeName + "." + $sa[0] )`; textFieldGrp -e -text $s tfgDescription; // Technique AEcgfxShader_techniqueLayout( $sNodeName ); // Profile // // Uncommenting the line below will enable the UI control that // allows one to manually select the Cg profile used to compile // the shader. // // AEcgfxShader_profileLayout( $sNodeName ); // URL // This attribute name can be any mixture of upper and lower // case, as given in the .fx file. Use case-insensitive lookup // to get the exact spelling. $sa = `cgfxShader -parameter "url" -caseInsensitive -description $sNodeName`; if ( $sa[1] == "String" && "" != ( $s = `getAttr ( $sNodeName + "." + $sa[0] )` ) ) symbolButton -e -enable 1 -ann ( (getPluginResource("cgfxShader", "kShaderHelp")) + ": " + $s ) -c ( "showHelp -absolute \"" + $s + "\"" ) bUrl; else symbolButton -e -enable 0 -ann (getPluginResource("cgfxShader", "kShaderHelpUnavailable")) bUrl; // Tool buttons AEcgfxShader_toolLayout( $gAEcgfxShader_iLayout, false ); setParent ..; // end of cgfxShaderLayout } // AEcgfxShader_shaderReplace // Callback when user has selected a CgFX file. global proc AEcgfxShader_shaderLoad( string $sNodeName, string $sFileName ) { string $sCmd = "cgfxShader -e -fx \"" + $sFileName + "\" " + $sNodeName; print ( $sCmd + ";\n" ); evalDeferred $sCmd; // Oddly, upon undo/redo, Maya will display our "cgfxShader" // command to show what was undone/redone, only if we use // "evalDeferred" to execute the "cgfxShader". Otherwise what // Maya displays is the command which invoked our callback, // which is not as interesting for the user. That is the // only reason we're using "evalDeferred" here. } // AEcgfxShader_shaderLoad // Callback when user makes an entry in the "CgFX File" text field. global proc AEcgfxShader_shaderText( int $iLayout ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent cgfxShaderLayout; string $sFileName = `textField -q -fileName tfFileName`; AEcgfxShader_shaderLoad( $sNodeName, $sFileName ); } // AEcgfxShader_shaderText //////////////////////////////////////////////////////////////////////// // "CgFX Parameters" Layout // //////////////////////////////////////////////////////////////////////// // Create or update "CgFX Parameters" layout, or attach the // layout to a different cgfxShader node. global proc AEcgfxShader_paramLayout( string $sNodeAttr ) { global int $gAEcgfxShader_bNew; // IN: true => build new controls global int $gAEcgfxShader_iLayout; // IN: AE template instance id // No action needed if our layout is already attached to the desired node. if ( !$gAEcgfxShader_bNew ) return; // Label the closest surrounding frameLayout with the shader name. string $sShader = `getAttr $sNodeAttr`; // CgFX file name $sShader = `substitute ".*/" $sShader ""`; // drop directory path $sShader = `substitute "\\.fx$" $sShader ""`; // drop ".fx" suffix string $sFrame = AEcgfxShader_closestAncestor( "frameLayout" ); string $s; if ( $sFrame != "" ) { $s = ( $sShader == "" ) ? "CgFX" : $sShader; frameLayout -e -l ( $s + (getPluginResource("cgfxShader", "kParameters"))) $sFrame; } // Delete old controls. string $sLayoutRel = "cgfxParameterLayout"; string $sLayoutAbs = `setParent -q` + "|" + $sLayoutRel; if ( `layout -exists $sLayoutAbs` ) deleteUI $sLayoutAbs; // Create a column layout to hold the new controls. setUITemplate -pst attributeEditorTemplate; columnLayout -adj 1 -visible 0 $sLayoutRel; // Get names and descriptions of the uniform shader variables // that are declared by the CgFX effect. Create their UI. string $sNodeName = `match "^[^.]*" $sNodeAttr`; string $paramDescriptions[] = `cgfxShader -lp -des $sNodeName`; string $paramDescription; string $param[]; for ( $paramDescription in $paramDescriptions ) { // $paramDescription has the form // "attrNametypesemanticdescriptionextraAttrSuffix" // (Future versions of the cgfxShader plug-in may provide // additional tab-separated fields after the semantic.) // Missing fields are indicated by a single space (" ") // so the string can be parsed more easily using the MEL // "tokenize" function, which skips consecutive delimiters. tokenize( $paramDescription, "\t", $param ); string $plug = $sNodeName + "." + $param[0]; if ( AEcgfxShader_paramLayoutFilter( $plug, $param ) ) { if ( catch( AEcgfxShader_paramControl( $plug, $param ) ) ) setParent $sLayoutRel; } } // Clean up and make the layout visible. setParent ..; setUITemplate -ppt; layout -e -visible 1 $sLayoutRel; // Rebuild layout when value of "shader" attribute is changed. $s = "AEcgfxShader_paramLayoutRebuild " + $gAEcgfxShader_iLayout + " 1; " + "AEcgfxShader_vertexAttributeUpdate " + $gAEcgfxShader_iLayout + " 1 0"; $s = "evalDeferred \"" + $s + "\""; scriptJob -parent $sLayoutRel -replacePrevious -killWithScene -runOnce 1 -compressUndo 1 -attributeChange $sNodeAttr $s; } // AEcgfxShader_paramLayout // Return true if parameter should be included in the "CgFx Parameters" layout. global proc int AEcgfxShader_paramLayoutFilter( string $plug, string $param[] ) { // Exclude documentation strings. Another procedure takes care of // displaying the ones that need to be visible in the Attribute Editor. if ( $param[1] == "String" ) { switch ( tolower( $param[0] ) ) { case "description": case "category": case "keywords": case "url": return false; } } return true; } // AEcgfxShader_paramLayoutFilter // Build UI for one of the dynamic parameters of the shader. // Returns the control or layout name, or "". global proc string AEcgfxShader_paramControl( string $plug, string $param[] ) { string $type = $param[1]; string $uiName = $param[3]; if( $uiName == " " || $uiName == "") $uiName = $param[0]; // Create controls depending on cgfxShader parameter type. switch ( $type ) { case "Bool": return AEcgfxShader_BoolLayout( $plug, $uiName, $param ); case "Int": return AEcgfxShader_IntLayout( $plug, $uiName, $param ); case "Float": return AEcgfxShader_FloatLayout( $plug, $uiName, $param ); case "String": return AEcgfxShader_StringLayout( $plug, $uiName, $param ); case "Vector2": return AEcgfxShader_Vector2Layout( $plug, $uiName, $param ); case "Vector3": return AEcgfxShader_Vector3Layout( $plug, $uiName, $param ); case "Vector4": return AEcgfxShader_Vector4Layout( $plug, $uiName, $param ); case "Color3": return AEcgfxShader_Color3Layout( $plug, $uiName, $param ); case "Color4": return AEcgfxShader_Color4Layout( $plug, $uiName, $param ); case "Matrix": return AEcgfxShader_MatrixLayout( $plug, $uiName, $param ); case "Time": case "HardwareFogEnabled": case "HardwareFogMode": case "HardwareFogStart": case "HardwareFogEnd": case "HardwareFogDensity": case "HardwareFogColor": case "Other": case "Unknown": return AEcgfxShader_SuppressLayout( $plug, $uiName, $param ); default: break; } // ObjectDir / WorldDir / ViewDir / ProjectionDir / ScreenDir // ObjectPos / WorldPos / ViewPos / ProjectionPos / ScreenPos if ( `match "Dir$" $type` != "" || `match "Pos$" $type` != "" ) return AEcgfxShader_DirectionLayout( $plug, $uiName, $param ); // Color1DTexture / Color2DTexture / Color3DTexture / // Color2DRectTexture / NormalTexture / BumpTexture / // CubeTexture / EnvTexture / NormalizationTexture if ( `match "Texture$" $type` != "" ) return AEcgfxShader_TextureLayout( $plug, $uiName, $param ); // WorldMatrix / ViewMatrix / ProjectionMatrix / WorldViewMatrix / // WorldViewProjectionMatrix // ... hide them because these matrices are built internally if ( `match "Matrix$" $type` != "" ) return AEcgfxShader_SuppressLayout( $plug, $uiName, $param ); // Unrecognized type return AEcgfxShader_UnimplementedLayout( $plug, $uiName, $param ); } // AEcgfxShader_paramControl // Callback to update "CgFX Shader" and "CgFX Parameters" // layouts after CgFX file has been loaded or reloaded or technique has been changed. global proc AEcgfxShader_paramLayoutRebuild( int $iLayout, int $bNew ) { global int $gAEcgfxShader_bNew; global int $gAEcgfxShader_iLayout; // Find the right AE scrollLayout and get the cgfxShader node name. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); if ( $sNodeName == "" ) return; string $sNodeAttr = $sNodeName + ".shader"; string $sOuterLayout = `setParent -q`; // Pass some info via globals. (No need to clean them up afterwards.) $gAEcgfxShader_bNew = $bNew; // true => delete controls & build new ones $gAEcgfxShader_iLayout = $iLayout; // AE instance id for callbacks // Update the "CgFX Shader" controls. setParent cgfxShaderLayout; setParent ..; AEcgfxShader_shaderReplace( $sNodeAttr ); setParent $sOuterLayout; // Build UI for parameters of new shader. setParent cgfxParameterLayout; setParent ..; AEcgfxShader_paramLayout( $sNodeAttr ); } // AEcgfxShader_paramLayoutRebuild //////////////////////////////////////////////////////////////////////// // "Vertex Attributes" Layout // //////////////////////////////////////////////////////////////////////// // Create "Vertex Data" layout, or recycle an // existing layout for use with another cgfxShader node. global proc AEcgfxShader_vertexAttributeLayout( string $sNodeAttr ) { global int $gAEcgfxShader_bNew; // IN: true => build new controls global int $gAEcgfxShader_iLayout; // IN: AE template instance id // No action needed if our layout is already associated with the given node. if ( !$gAEcgfxShader_bNew ) return; // Find or create layout. setUITemplate -pst attributeEditorTemplate; string $sLayoutRel = "cgfxVertexAttributeLayout"; string $sLayoutAbs = `setParent -q` + "|" + $sLayoutRel; int $bReuse = `layout -exists $sLayoutAbs`; if ( $bReuse ) setParent $sLayoutRel; else columnLayout -adj 1 -visible 0 $sLayoutRel; // Create or update the controls. AEcgfxShader_vertexAttributeUpdate( $gAEcgfxShader_iLayout, $bReuse, 0 ); // Clean up and make the layout visible. setParent ..; // end of cgfxVertexAttributeLayout setUITemplate -ppt; layout -e -visible 1 $sLayoutRel; // Update text fields when value of the attribute is changed. string $cmd = "AEcgfxShader_vertexAttributeUpdate " + $gAEcgfxShader_iLayout + " 1 0"; scriptJob -parent $sLayoutRel -replacePrevious -killWithScene -compressUndo 1 -attributeChange $sNodeAttr $cmd; } // AEcgfxShader_vertexAttributeLayout // Create or update the vertex attribute fields. global proc AEcgfxShader_vertexAttributeUpdate( int $iLayout, int $bReuse, int $nShow ) { string $s; // Find our layout and get the cgfxShader node name. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent cgfxVertexAttributeLayout; // Create layout for vertex attribute fields, or reuse existing layout. setUITemplate -pst attributeEditorTemplate; if ( $bReuse ) setParent clVertexAttribute; else columnLayout -adj 1 clVertexAttribute; //(01 // Get blacklisted UV set names. // A UV set name is blacklisted if the UV set is defined but empty // (has no faces mapped) on at least one mesh associated with the // cgfxShader node. For UV sets named in the blacklist, the shader // receives vertex attribute values of (0,0) instead of any actual UV values. // This protects the user against accidentally crashing Maya due to // a bug found in Maya 5.0 which causes Maya to fail if a hardware // shader accesses an existing but empty UV set. The empty UV set // blacklist logic can be deleted when the Maya bug has been fixed. string $saBlacklist[] = `cgfxShader -q -emptyUV $sNodeName`; //)01 end of workaround for empty UV set bug // Get node's current vertexAttributeSource list. string $vertexAttributeList[] = getAttr ($sNodeName + ".vertexAttributeList"); string $vertexAttributeSource[] = getAttr ($sNodeName + ".vertexAttributeSource"); // Loop over vertex attributes. int $nVertexAttribute = size( $vertexAttributeList ) / 4; $iVertexAttribute = 0; for ( ; $iVertexAttribute < $nVertexAttribute; ++$iVertexAttribute) { // Control & menu names string $sControlRel = "va" + $iVertexAttribute; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupRel = "vaPopup"; string $sPopupAbs = $sControlAbs + "|" + $sPopupRel; // Get vertex attribute specification to be displayed in the text field. string $sVertexAttributeName = $vertexAttributeList[ $iVertexAttribute * 4 ]; string $sVertexAttributeValue = $vertexAttributeSource[ $iVertexAttribute ]; //(02 // Check for UV set name in blacklist. int $bForced = false; for ( $s in $saBlacklist ) { if ( $sVertexAttributeValue == $s ) { $sVertexAttributeValue += " <-- forced to (0,0)"; $bForced = true; break; } } //)02 end of workaround for empty UV set bug if ( `control -exists $sControlRel` ) { // Reuse existing field textFieldGrp -e //(03 -editable ( !$bForced ) //)03 end of workaround for empty UV set bug -label $sVertexAttributeName -text $sVertexAttributeValue $sControlRel; popupMenu -e -deleteAllItems $sPopupAbs; } else { // Create new text field. $s = "AEcgfxShader_vertexAttributeText " + $iLayout + " " + $iVertexAttribute + " \"#1\""; textFieldGrp -label $sVertexAttributeName -ann (getPluginResource("cgfxShader", "kChooseVertexAttributeValueSource")) -cc $s //(04 -editable ( !$bForced ) //)04 end of workaround for empty UV set bug -text $sVertexAttributeValue $sControlRel; $s = "AEcgfxShader_vertexAttributePopup " + $iLayout + " " + $iVertexAttribute; popupMenu -postMenuCommand $s $sPopupRel; } } // loop over vertex attributes // Delete any superfluous controls left over from the previous shader for( ;;) { string $sControlRel = "va" + $iVertexAttribute; if ( !`control -exists $sControlRel` ) break; deleteUI $sControlRel; $iVertexAttribute++; } setParent ..; // end of clVertexAttribute // Clean up. setUITemplate -ppt; } // AEcgfxShader_vertexAttributeUpdate // Callback to create VertexAttribute popup menu items global proc AEcgfxShader_vertexAttributePopup( int $iLayout, int $iVertexAttribute ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent clVertexAttribute; // Get node's current vertexAttributelist. string $vertexAttributeList[] = getAttr ($sNodeName + ".vertexAttributeList"); string $sVertexAttributeSemantic = $vertexAttributeList[ $iVertexAttribute * 4 + 3 ]; string $sControlRel = "va" + $iVertexAttribute; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupRel = "vaPopup"; string $sPopupAbs = $sControlAbs + "|" + $sPopupRel; popupMenu -e -deleteAllItems $sPopupAbs; setParent -menu $sPopupAbs; string $cmd = "AEcgfxShader_vertexAttributeChoice " + $sNodeName + " " + $iVertexAttribute + " "; // UV sets string $shapeNodes[] = cgfxShader_listShapes( $sNodeName ); // (in cgfxShader_util.mel) string $uvSets[] = cgfxShader_listUVSets( $shapeNodes ); string $tangent; string $binormal; int $numUvSets = size( $uvSets ); if ( !$numUvSets ) { $uvSets = { "map1" }; $numUvSets = 1; } int $i; for ( $i=0; $i<$numUvSets; $i++) { string $uvSet = $uvSets[$i]; menuItem -l $uvSet -c ($cmd + "\"uv:" + $uvSet + "\""); $tangent = "tangent:"+$uvSet; menuItem -l $tangent -c ($cmd + $tangent); $binormal = "binormal:"+$uvSet; menuItem -l $binormal -c ($cmd + $binormal); if ($i != ($numUvSets-1)) menuItem -divider 1; } // Colour sets if( `exists polyColorSet` ) // Backward compatibility { string $colorSets[] = cgfxShader_listColorSets( $shapeNodes ); if ( size($colorSets)) menuItem -divider 1; string $colorSet; for ( $colorSet in $colorSets ) menuItem -l $colorSet -c ($cmd + "\"color:" + $colorSet + "\""); } // Normal menuItem -divider 1; menuItem -l (getPluginResource("cgfxShader", "kPosition")) -c ($cmd + "position"); menuItem -l (getPluginResource("cgfxShader", "kNormal")) -c ($cmd + "normal"); //bug 377916: empty position data will cause maya crash. //proper fix is to prevent user from choosing empty position data if($sVertexAttributeSemantic != "POSITION" ) { menuItem -divider 1; menuItem -l (" (" + getPluginResource("cgfxShader", "kEmpty") + ") ") -c ($cmd + "\"\""); } } // AEcgfxShader_vertexAttributePopup // Callback when user chooses an item from VertexAttribute popup menu global proc AEcgfxShader_vertexAttributeChoice( string $sNodeName, int $iVertexAttribute, string $choice ) { // Get node's current vertexAttributeSource list. string $vertexAttributeList[] = `getAttr( $sNodeName + ".vertexAttributeSource")`; // Do nothing if value is unchanged. if ( $iVertexAttribute < size( $vertexAttributeList ) && $choice == $vertexAttributeList[ $iVertexAttribute ] ) return; // Stuff new choice. $vertexAttributeList[ $iVertexAttribute ] = $choice; // Set new vertexAttributeSource list. int $i; int $numVertexAttributes = size( $vertexAttributeList ); string $sSet = $numVertexAttributes; for ( $i = 0; $i < $numVertexAttributes; ++$i ) $sSet += " \"" + $vertexAttributeList[$i] + "\""; $sSet = "setAttr " + $sNodeName + ".vertexAttributeSource -type stringArray " + $sSet; print ( $sSet + ";\n" ); evalDeferred $sSet; // use evalDeferred so Maya will display // the setAttr command on undo/redo } // AEcgfxShader_vertexAttributeChoice // Callback when user changes the text in VertexAttribute text field global proc AEcgfxShader_vertexAttributeText( int $iLayout, int $iVertexAttribute, string $sText ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); AEcgfxShader_vertexAttributeChoice( $sNodeName, $iVertexAttribute, $sText ); } // AEcgfxShader_vertexAttributeText //////////////////////////////////////////////////////////////////////// // "Texture Coordinates" Layout // //////////////////////////////////////////////////////////////////////// // Create "Texture Coordinates" layout, or recycle an // existing layout for use with another cgfxShader node. global proc AEcgfxShader_texCoordLayout( string $sNodeAttr ) { global int $gAEcgfxShader_bNew; // IN: true => build new controls global int $gAEcgfxShader_iLayout; // IN: AE template instance id // No action needed if our layout is already associated with the given node. if ( !$gAEcgfxShader_bNew ) return; // Find or create layout. setUITemplate -pst attributeEditorTemplate; string $sLayoutRel = "cgfxTexCoordLayout"; string $sLayoutAbs = `setParent -q` + "|" + $sLayoutRel; int $bReuse = `layout -exists $sLayoutAbs`; if ( $bReuse ) setParent $sLayoutRel; else columnLayout -adj 1 -visible 0 $sLayoutRel; // Create or update the controls. AEcgfxShader_texCoordUpdate( $gAEcgfxShader_iLayout, $bReuse, 0 ); // Clean up and make the layout visible. setParent ..; // end of cgfxTexCoordLayout setUITemplate -ppt; layout -e -visible 1 $sLayoutRel; // Update text fields when value of texCoordSource attribute is changed. string $cmd = "AEcgfxShader_texCoordUpdate " + $gAEcgfxShader_iLayout + " 1 0"; scriptJob -parent $sLayoutRel -replacePrevious -killWithScene -compressUndo 1 -attributeChange $sNodeAttr $cmd; } // AEcgfxShader_texCoordLayout // Create or update the TEXCOORD fields. global proc AEcgfxShader_texCoordUpdate( int $iLayout, int $bReuse, int $nShow ) { string $s; // Find our layout and get the cgfxShader node name. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent cgfxTexCoordLayout; // Create layout for TEXCOORD fields, or reuse existing layout. setUITemplate -pst attributeEditorTemplate; if ( $bReuse ) setParent clTexCoord; else columnLayout -adj 1 clTexCoord; //(01 // Get blacklisted UV set names. // A UV set name is blacklisted if the UV set is defined but empty // (has no faces mapped) on at least one mesh associated with the // cgfxShader node. For UV sets named in the blacklist, the shader // receives TEXCOORD values of (0,0) instead of any actual UV values. // This protects the user against accidentally crashing Maya due to // a bug found in Maya 5.0 which causes Maya to fail if a hardware // shader accesses an existing but empty UV set. The empty UV set // blacklist logic can be deleted when the Maya bug has been fixed. string $saBlacklist[] = `cgfxShader -q -emptyUV $sNodeName`; //)01 end of workaround for empty UV set bug // Get node's current texCoordSource list. string $texCoordList[] = `cgfxShader -q -texCoordSource $sNodeName`; // How many TEXCOORDs? int $nTexCoord = size( $texCoordList ); while ( $nTexCoord > 0 && $texCoordList[ $nTexCoord - 1 ] == "" ) $nTexCoord--; $nTexCoord = max( $nTexCoord, max( 1, $nShow ) ); // Loop over TEXCOORDs. int $mTexCoord = `cgfxShader -q -maxTexCoords`; int $iTexCoord = 0; for (;; ++$iTexCoord) { // Control & menu names string $sControlRel = "tc" + $iTexCoord; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupRel = "tcPopup"; string $sPopupAbs = $sControlAbs + "|" + $sPopupRel; // Get TEXCOORD specification to be displayed in the text field. string $sTexCoord = $texCoordList[ $iTexCoord ]; //(02 // Check for UV set name in blacklist. int $bForced = false; for ( $s in $saBlacklist ) { if ( $sTexCoord == $s ) { $sTexCoord += " <-- forced to (0,0)"; $bForced = true; break; } } //)02 end of workaround for empty UV set bug // Update existing text field. if ( `control -exists $sControlAbs` ) { if ( $nShow && $iTexCoord >= $nTexCoord ) { deleteUI $sControlRel; if ( $iTexCoord == $mTexCoord ) deleteUI sepMTU; } else { textFieldGrp -e //(03 -editable ( !$bForced ) //)03 end of workaround for empty UV set bug -text $sTexCoord $sControlRel; popupMenu -e -deleteAllItems $sPopupAbs; } } // Quit when no more fields are needed. else if ( $iTexCoord >= $nTexCoord ) break; // Create new text field. else { // Put a line after GL_MAX_TEXTURE_UNITS, as a reminder that // any TEXCOORDs beyond that limit can't be passed to shaders // given the hardware & drivers presently installed on this // computer, but still can be specified and kept in the scene // file for interchange with other more capable configurations. if ( $iTexCoord == $mTexCoord ) separator sepMTU; $s = "AEcgfxShader_texCoordText " + $iLayout + " " + $iTexCoord + " \"#1\""; textFieldGrp -l ( "TEXCOORD" + $iTexCoord ) -ann (getPluginResource("cgfxShader", "kChooseTEXCOORDAttributeValueSource")) -cc $s //(04 -editable ( !$bForced ) //)04 end of workaround for empty UV set bug -text $sTexCoord $sControlRel; $s = "AEcgfxShader_texCoordPopup " + $iLayout + " " + $iTexCoord; popupMenu -postMenuCommand $s $sPopupRel; } } // loop over TEXCOORDs setParent ..; // end of clTexCoord if ( !$nShow ) $nTexCoord = $iTexCoord; // Buttons to show or hide additional TEXCOORDs... if ( $bReuse ) setParent rlBut; else { rowLayout -numberOfColumns 2 -columnWidth2 239 100 -columnAttach 1 right 4 -columnAttach 2 left 4 rlBut; string $dir = `cgfxShader -q -pluginPath`; symbolButton -image "cgfxShaderMore.png" -height 16 -width 96 -ann (getPluginResource("cgfxShader", "kShowAnotherTEXCOORD")) sbMore; symbolButton -image "cgfxShaderLess.png" -height 16 -width 96 -ann (getPluginResource("cgfxShader", "kHideLastTEXCOORD")) sbLess; } $s = "AEcgfxShader_texCoordUpdate " + $iLayout + " 1 "; int $iShow = $nTexCoord + 1; symbolButton -e -enable ( $nTexCoord < 32 ) -c ( $s + $iShow ) sbMore; $iShow = $nTexCoord - 1; symbolButton -e -enable ( $iShow > 0 && ( $iShow >= size( $texCoordList ) || $texCoordList[ $iShow ] == "" ) ) -c ( $s + $iShow ) sbLess; setParent ..; // end of rlBut //(05 // Create layout for empty UV set info, or reuse existing layout. // (Can be deleted when the empty UV set bug has been fixed.) int $nEmpty = 0; if ( $bReuse ) scrollField -e -clear sfEUV; else { separator -style none; rowLayout -numberOfColumns 1 -columnOffset1 65; columnLayout -adj 1; text -align left txEUV; scrollField -wordWrap 0 -editable 0 -height 65 -width 295 -ann (getPluginResource("cgfxShader","kTexcoordValuesareForced")) sfEUV; setParent ..; setParent ..; } // Display empty UV set list. if ( size( $saBlacklist ) ) { // Find which combinations of ( uv set name, shape node ) // denote existing but empty UV sets. string $saEmpty[]; string $saShapes[] = `cgfxShader -q -emptyUVShapes $sNodeName`; string $sShape; string $sUV; for ( $sShape in $saShapes ) { for ( $sUV in $saBlacklist ) { int $nuv[] = `polyEvaluate -uv -uvs $sUV $sShape`; if ( size( $nuv ) == 1 && $nuv[ 0 ] == 0 ) $saEmpty[ $nEmpty++ ] = $sUV + " on " + $sShape + "\n"; } } // Display the troublesome combinations. $saEmpty = sort( $saEmpty ); $nEmpty = 0; for ( $s in $saEmpty ) scrollField -e -insertText ( ++$nEmpty + ". " + $s ) sfEUV; } // Maya positions the scroll field so that only the last // couple of lines are visible. Scroll fields can't be // scrolled via MEL; only the user can do it manually. // Show item count so user can know whether there are more // items in the scroll field in addition to the visible ones. $s = $nEmpty < 1 ? "No empty UV sets." : $nEmpty == 1 ? "1 empty UV set:" : ( $nEmpty + " empty UV sets:" ); text -e -enable ( $nEmpty > 0 ) -l $s txEUV; //)05 end of workaround for empty UV set bug // Clean up. setUITemplate -ppt; } // AEcgfxShader_texCoordUpdate // Callback to create TEXCOORD popup menu items global proc AEcgfxShader_texCoordPopup( int $iLayout, int $iTexCoord ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent clTexCoord; string $sControlRel = "tc" + $iTexCoord; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupRel = "tcPopup"; string $sPopupAbs = $sControlAbs + "|" + $sPopupRel; print( (getPluginResource("cgfxShader","kSettingUpPopup")) + $sNodeName + ", layout " + $iLayout + ", attr " + $iTexCoord + ", menu " + $sPopupAbs + "\n"); popupMenu -e -deleteAllItems $sPopupAbs; setParent -menu $sPopupAbs; string $cmd = "AEcgfxShader_texCoordChoice " + $sNodeName + " " + $iTexCoord + " "; // UV sets string $shapeNodes[] = cgfxShader_listShapes( $sNodeName ); // (in cgfxShader_util.mel) string $uvSets[] = cgfxShader_listUVSets( $shapeNodes ); string $tangent; string $binormal; int $numUvSets = size( $uvSets ); if ( !$numUvSets ) { $uvSets = { "map1" }; $numUvSets = 1; } int $i; for ( $i=0; $i<$numUvSets; $i++) { string $uvSet = $uvSets[$i]; menuItem -l $uvSet -c ($cmd + "\"" + $uvSet + "\""); $tangent = "tangent:"+$uvSet; menuItem -l $tangent -c ($cmd + $tangent); $binormal = "binormal:"+$uvSet; menuItem -l $binormal -c ($cmd + $binormal); if ($i != ($numUvSets-1)) menuItem -divider 1; } // Colour sets if( `exists polyColorSet` ) // Backward compatibility { string $colorSets[] = cgfxShader_listColorSets( $shapeNodes ); if ( size($colorSets)) menuItem -divider 1; string $colorSet; for ( $colorSet in $colorSets ) menuItem -l $colorSet -c ($cmd + "\"" + $colorSet + "\""); } // Position and Normal menuItem -divider 1; menuItem -l (getPluginResource("cgfxShader", "kNormal")) -c ($cmd + "normal"); menuItem -divider 1; menuItem -l (" (" + getPluginResource("cgfxShader", "kEmpty") + ") ") -c ($cmd + "\"\""); } // AEcgfxShader_texCoordPopup // Callback when user chooses an item from TEXCOORD popup menu global proc AEcgfxShader_texCoordChoice( string $sNodeName, int $iTexCoord, string $choice ) { // Get node's current texCoordSource list. string $texCoordList[] = `cgfxShader -q -tcs $sNodeName`; // Do nothing if value is unchanged. if ( $iTexCoord < size( $texCoordList ) && $choice == $texCoordList[ $iTexCoord ] ) return; // Stuff new choice. $texCoordList[ $iTexCoord ] = $choice; // Drop trailing empty items. int $nTexCoord = size( $texCoordList ); while ( $nTexCoord > 0 && $texCoordList[ $nTexCoord - 1 ] == "" ) $nTexCoord--; // Set new texCoordSource list. int $i; string $sSet = $nTexCoord; for ( $i = 0; $i < $nTexCoord; ++$i ) $sSet += " \"" + $texCoordList[$i] + "\""; $sSet = "setAttr " + $sNodeName + ".tcs -type stringArray " + $sSet; print ( $sSet + ";\n" ); evalDeferred $sSet; // use evalDeferred so Maya will display // the setAttr command on undo/redo } // AEcgfxShader_texCoordChoice // Callback when user changes the text in TEXCOORD text field global proc AEcgfxShader_texCoordText( int $iLayout, int $iTexCoord, string $sText ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); AEcgfxShader_texCoordChoice( $sNodeName, $iTexCoord, $sText ); } // AEcgfxShader_texCoordText //////////////////////////////////////////////////////////////////////// // "Colours" Layout // //////////////////////////////////////////////////////////////////////// // Create "Colours" layout, or recycle an // existing layout for use with another cgfxShader node. global proc AEcgfxShader_colorLayout( string $sNodeAttr ) { global int $gAEcgfxShader_bNew; // IN: true => build new controls global int $gAEcgfxShader_iLayout; // IN: AE template instance id // No action needed if our layout is already associated with the given node. if ( !$gAEcgfxShader_bNew ) return; // Find or create layout. setUITemplate -pst attributeEditorTemplate; string $sLayoutRel = "cgfxColorLayout"; string $sLayoutAbs = `setParent -q` + "|" + $sLayoutRel; int $bReuse = `layout -exists $sLayoutAbs`; if ( $bReuse ) setParent $sLayoutRel; else columnLayout -adj 1 -visible 0 $sLayoutRel; // Create or update the controls. AEcgfxShader_ColorUpdate( $gAEcgfxShader_iLayout, $bReuse, 0 ); // Clean up and make the layout visible. setParent ..; // end of cgfxColorLayout setUITemplate -ppt; layout -e -visible 1 $sLayoutRel; // Update text fields when value of colorSource attribute is changed. string $cmd = "AEcgfxShader_ColorUpdate " + $gAEcgfxShader_iLayout + " 1 0"; scriptJob -parent $sLayoutRel -replacePrevious -killWithScene -compressUndo 1 -attributeChange $sNodeAttr $cmd; } // AEcgfxShader_colorLayout // Create or update the COLOR fields. global proc AEcgfxShader_ColorUpdate( int $iLayout, int $bReuse, int $nShow ) { string $s; // Find our layout and get the cgfxShader node name. string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent cgfxColorLayout; // Create layout for COLOR fields, or reuse existing layout. setUITemplate -pst attributeEditorTemplate; if ( $bReuse ) setParent clColor; else columnLayout -adj 1 clColor; //(01 // Get blacklisted UV set names. // A UV set name is blacklisted if the UV set is defined but empty // (has no faces mapped) on at least one mesh associated with the // cgfxShader node. For UV sets named in the blacklist, the shader // receives COLOR values of (0,0) instead of any actual UV values. // This protects the user against accidentally crashing Maya due to // a bug found in Maya 5.0 which causes Maya to fail if a hardware // shader accesses an existing but empty UV set. The empty UV set // blacklist logic can be deleted when the Maya bug has been fixed. string $saBlacklist[] = `cgfxShader -q -emptyUV $sNodeName`; //)01 end of workaround for empty UV set bug // Get node's current colorSource list. string $ColorList[] = `cgfxShader -q -colorSource $sNodeName`; // How many COLORs? int $nColor = size( $ColorList ); while ( $nColor > 0 && $ColorList[ $nColor - 1 ] == "" ) $nColor--; $nColor = max( $nColor, max( 1, $nShow ) ); // Loop over COLORs. int $mColor = 1; // Implement this to allow more than 1 colour -> `cgfxShader -q -maxColors`; int $iColor = 0; for (;; ++$iColor) { // Control & menu names string $sControlRel = "tc" + $iColor; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupRel = "tcPopup"; string $sPopupAbs = $sControlAbs + "|" + $sPopupRel; // Get COLOR specification to be displayed in the text field. string $sColor = $ColorList[ $iColor ]; //(02 // Check for UV set name in blacklist. int $bForced = false; for ( $s in $saBlacklist ) { if ( $sColor == $s ) { $sColor += " <-- forced to (0,0)"; $bForced = true; break; } } //)02 end of workaround for empty UV set bug // Update existing text field. if ( `control -exists $sControlAbs` ) { if ( $nShow && $iColor >= $nColor ) { deleteUI $sControlRel; if ( $iColor == $mColor ) deleteUI sepMTU; } else { textFieldGrp -e //(03 -editable ( !$bForced ) //)03 end of workaround for empty UV set bug -text $sColor $sControlRel; popupMenu -e -deleteAllItems $sPopupAbs; } } // Quit when no more fields are needed. else if ( $iColor >= $nColor ) break; // Create new text field. else { // Put a line after GL_MAX_TEXTURE_UNITS, as a reminder that // any COLORs beyond that limit can't be passed to shaders // given the hardware & drivers presently installed on this // computer, but still can be specified and kept in the scene // file for interchange with other more capable configurations. if ( $iColor == $mColor ) separator sepMTU; $s = "AEcgfxShader_ColorText " + $iLayout + " " + $iColor + " \"#1\""; textFieldGrp -l ( "COLOR" + $iColor ) -ann (getPluginResource("cgfxShader","kRmbToChooseSource")) -cc $s //(04 -editable ( !$bForced ) //)04 end of workaround for empty UV set bug -text $sColor $sControlRel; $s = "AEcgfxShader_ColorPopup " + $iLayout + " " + $iColor; popupMenu -postMenuCommand $s $sPopupRel; } } // loop over COLORs setParent ..; // end of clColor if ( !$nShow ) $nColor = $iColor; /* // Buttons to show or hide additional COLORs... if ( $bReuse ) setParent rlBut; else { rowLayout -numberOfColumns 2 -columnWidth2 239 100 -columnAttach 1 right 4 -columnAttach 2 left 4 rlBut; string $dir = `cgfxShader -q -pluginPath`; symbolButton -image "cgfxShaderMore.png" -height 16 -width 96 -ann "Show another COLOR" sbMore; symbolButton -image "cgfxShaderLess.png" -height 16 -width 96 -ann "Hide last COLOR" sbLess; } $s = "AEcgfxShader_ColorUpdate " + $iLayout + " 1 "; int $iShow = $nColor + 1; symbolButton -e -enable ( $nColor < 32 ) -c ( $s + $iShow ) sbMore; $iShow = $nColor - 1; symbolButton -e -enable ( $iShow > 0 && ( $iShow >= size( $ColorList ) || $ColorList[ $iShow ] == "" ) ) -c ( $s + $iShow ) sbLess; setParent ..; // end of rlBut */ // Clean up. setUITemplate -ppt; } // AEcgfxShader_ColorUpdate // Callback to create COLOR popup menu items global proc AEcgfxShader_ColorPopup( int $iLayout, int $iColor ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); setParent clColor; string $sControlRel = "tc" + $iColor; string $sControlAbs = `setParent -q` + "|" + $sControlRel; string $sPopupRel = "tcPopup"; string $sPopupAbs = $sControlAbs + "|" + $sPopupRel; popupMenu -e -deleteAllItems $sPopupAbs; setParent -menu $sPopupAbs; string $cmd = "AEcgfxShader_ColorChoice " + $sNodeName + " " + $iColor + " "; // Colour sets menuItem -divider 1; string $shapeNodes[] = cgfxShader_listShapes( $sNodeName ); // (in cgfxShader_util.mel) string $colorSets[] = cgfxShader_listColorSets( $shapeNodes ); string $colorSet; for ( $colorSet in $colorSets ) menuItem -l $colorSet -c ($cmd + "\"" + $colorSet + "\""); // UV sets string $uvSets[] = cgfxShader_listUVSets( $shapeNodes ); string $tangent; string $binormal; int $numUvSets = size( $uvSets ); if ( !$numUvSets ) { $uvSets = { "map1" }; $numUvSets = 1; } int $i; for ( $i=0; $i<$numUvSets; $i++) { string $uvSet = $uvSets[$i]; menuItem -l $uvSet -c ($cmd + "\"" + $uvSet + "\""); $tangent = "tangent:"+$uvSet; menuItem -l $tangent -c ($cmd + $tangent); $binormal = "binormal:"+$uvSet; menuItem -l $binormal -c ($cmd + $binormal); if ($i != ($numUvSets-1)) menuItem -divider 1; } // normal menuItem -divider 1; menuItem -l (getPluginResource("cgfxShader", "kNormal")) -c ($cmd + "normal"); menuItem -divider 1; menuItem -l (" (" + getPluginResource("cgfxShader", "kEmpty") + ") ") -c ($cmd + "\"\""); } // AEcgfxShader_ColorPopup // Callback when user chooses an item from COLOR popup menu global proc AEcgfxShader_ColorChoice( string $sNodeName, int $iColor, string $choice ) { // Get node's current colorSource list. string $ColorList[] = `cgfxShader -q -cs $sNodeName`; // Do nothing if value is unchanged. if ( $iColor < size( $ColorList ) && $choice == $ColorList[ $iColor ] ) return; // Stuff new choice. $ColorList[ $iColor ] = $choice; // Drop trailing empty items. int $nColor = size( $ColorList ); while ( $nColor > 0 && $ColorList[ $nColor - 1 ] == "" ) $nColor--; // Set new colorSource list. int $i; string $sSet = $nColor; for ( $i = 0; $i < $nColor; ++$i ) $sSet += " \"" + $ColorList[$i] + "\""; $sSet = "setAttr " + $sNodeName + ".cs -type stringArray " + $sSet; print ( $sSet + ";\n" ); evalDeferred $sSet; // use evalDeferred so Maya will display // the setAttr command on undo/redo } // AEcgfxShader_ColorChoice // Callback when user changes the text in COLOR text field global proc AEcgfxShader_ColorText( int $iLayout, int $iColor, string $sText ) { string $sNodeName = AEcgfxShader_beginCallback( $iLayout ); AEcgfxShader_ColorChoice( $sNodeName, $iColor, $sText ); } // AEcgfxShader_ColorText //////////////////////////////////////////////////////////////////////// // "Extra Attributes" // //////////////////////////////////////////////////////////////////////// // Find the "Extra Attributes" frameLayout and hide it. global proc AEcgfxShader_suppressExtraNew() { string $sExtraParent = `setParent ..`; string $sa[] = `layout -q -ca $sExtraParent`; string $sExtra; // Bug 261197: label string may be localized, look up the localized value // Beware, the resource format or id could change some day. string $extraLabel = `uiRes("s_TPStemplateStrings.rExtraAttributes")`; for ( $sExtra in $sa ) { if ( `objectTypeUI -isType frameLayout $sExtra` && `frameLayout -q -l $sExtra` == $extraLabel ) { frameLayout -e -manage 0 $sExtra; break; } } } // AEcgfxShader_suppressExtraNew global proc AEcgfxShader_suppressExtraReplace() { } // AEcgfxShader_suppressExtraReplace // When a CgFX file is loaded, our plug-in defines dynamic // attributes for the shader parameters specified in the file. // We create our own Attribute Editor controls for those // attributes. Therefore we do not need the controls that // Maya creates automatically under "Extra Attributes". // Getting rid of the unwanted controls is not that easy. // - For dynamic attribute names known when AEcgfxShaderTemplate // is called, we use "editorTemplate -suppress". That is // good because it avoids the overhead of creating the surplus // controls. But upon (re)loading a CgFX file, generally Maya // does not call AEcgfxShaderTemplate again; so we don't get a // chance to "editorTemplate -suppress" the newly defined // attributes. We could force AEcgfxShaderTemplate to be // called again for the main AE window, but there is no // comparable solution for tear-off ("Copy Tab") windows. // - Omitting "editorTemplate -addExtraControls" was tried with // unsatisfactory results. Due to a bug in Maya 5.0, Maya // would create the controls anyway, all overlaid on the same // pixels, trashing the upper portion of the AE layout. // - It is possible to find and delete the "Extra Attributes" // layout or the controls within it, but then Maya crashes. // So here's what we do: Immediately after the "Extra Attributes" // layout is created, we find it and hide it. Although Maya still // creates unnecessary controls for any dynamic attributes that we // don't manage to suppress, those controls won't be visible. //////////////////////////////////////////////////////////////////////// // Callback initialization helpers // //////////////////////////////////////////////////////////////////////// // Callback initialization helper. // Returns cgfxShader node name, or "" if UI has been deleted. global proc string AEcgfxShader_beginCallback( int $iLayout ) { string $sNodeName; // Find the requested instance of our cgfxShader AE template. string $sLayout = "cgfxShader_AEinstance" + $iLayout; if ( `layout -exists $sLayout` ) { // Get the name of the cgfxShader node associated with this layout. setParent $sLayout; $sNodeName = `nameField -q -object nodeNameField`; // setParent to the closest ancestor scrollLayout. // Maya searches for UI layout and control names beneath // the current parent before looking elsewhere. Here we // will set the parent to the right AE template instance. // Afterwards the caller can safely refer to controls and // layouts by their simple names instead of fully qualified // names, provided the simple names are unique within our // template instance's scrollLayout. $sLayout = AEcgfxShader_closestAncestor( "scrollLayout" ); setParent $sLayout; } return $sNodeName; } // Callback before creating cgfxShader controls. global proc AEcgfxShader_beginNew( string $sNodeAttr ) { global int $gAEcgfxShader_bNew; // OUT: true because not same node global int $gAEcgfxShader_iLayout; // OUT: new template instance id global int $gAEcgfxShader_nLayout; // UPD: highest used instance id // Assign a unique serial number to identify this AE template instance. $gAEcgfxShader_iLayout = ++$gAEcgfxShader_nLayout; $gAEcgfxShader_bNew = true; // Create a uniquely named hidden layout. // Callbacks use this to help find their UI elements and node name. string $sLayout = "cgfxShader_AEinstance" + $gAEcgfxShader_iLayout; columnLayout -manage 0 $sLayout; // Stash the cgfxShader node name in a hidden textField. // - Saving the name allows us to distinguish between refreshing // an existing layout vs. switching the layout to attach to a // different cgfxShader node. // - We also are using a nameField. This is necessary because the // nameField will become null after opening a new scene. // - It seems that we need both of them to properly distinguish // all cases: // - Renaming the same node. // - Opening a node with same name but in a different (or // reopened) scene. string $sNodeName = `match "^[^.]*" $sNodeAttr`; textField -manage 0 -text $sNodeName nodeNameTextField; nameField -manage 0 -object $sNodeName nodeNameField; } // AEcgfxShader_beginNew // Callback before refreshing the layout or switching it to another node. global proc AEcgfxShader_beginReplace( string $sNodeAttr ) { global int $gAEcgfxShader_bNew; // OUT: true if different node global int $gAEcgfxShader_iLayout; // OUT: template instance id // Get our unique layout name. It is the current layout's first child. string $sParent = `setParent -q`; string $saKids[] = `layout -q -ca $sParent`; string $sKid = $saKids[0]; // The layout name contains the id of this AE template instance. // Extract it and pass it via a global variable to our // subsequent UI building functions. string $sIndex = `match "[0-9]+" $sKid`; string $sLayout = "cgfxShader_AEinstance" + $sIndex; if ( $sKid != $sLayout ) error -sl 1 (getPluginResource("cgfxShader", "kInternalError")); $gAEcgfxShader_iLayout = $sIndex; // If this template instance is already associated with the // given cgfxShader node, clear global flag to tell subsequent // UI building functions that the existing controls can be kept. string $sNodeName = `match "^[^.]*" $sNodeAttr`; if ( $sNodeName == `textField -q -text nodeNameTextField` && $sNodeName == `nameField -q -object nodeNameField` ) $gAEcgfxShader_bNew = false; else { $gAEcgfxShader_bNew = true; textField -e -text $sNodeName nodeNameTextField; nameField -e -object $sNodeName nodeNameField; } } // AEcgfxShader_beginReplace // Return full name of the closest ancestor layout of a given type, or "". global proc string AEcgfxShader_closestAncestor( string $sObjectTypeUI ) { string $sPrev; string $sLayout = `setParent -q`; while ( !`objectTypeUI -isType $sObjectTypeUI $sLayout` ) { $sPrev = $sLayout; $sLayout = `substitute "|[^|]*$" $sLayout ""`; // drop rightmost "|name" if ( $sLayout == $sPrev ) return ""; } return $sLayout; } //////////////////////////////////////////////////////////////////// // MAIN PROCEDURE FOR THE TEMPLATE //////////////////////////////// //////////////////////////////////////////////////////////////////// // Called from the Attribute Editor, by way of the AEcgfxShaderTemplate() // procedure in our "cgfxShader_initUI.mel" script. global proc AEcgfxShader_template( string $node ) { AEswatchDisplay $node; editorTemplate -beginScrollLayout; // Initialize globals used by the subsequent -callCustom procedures. editorTemplate -callCustom AEcgfxShader_beginNew AEcgfxShader_beginReplace shader; // CgFX Shader editorTemplate -beginLayout (getPluginResource("cgfxShader", "kCgFXShader")) -collapse false; editorTemplate -callCustom AEcgfxShader_shaderNew AEcgfxShader_shaderReplace shader; editorTemplate -endLayout; // CgFX Parameters // This section is generated dynamically from the shader // parameter declarations in the CgFX file. editorTemplate -beginLayout (getPluginResource("cgfxShader", "kCgFXParameters")) -collapse false; editorTemplate -callCustom AEcgfxShader_paramLayout AEcgfxShader_paramLayout shader; editorTemplate -endLayout; // Vertex Parameters editorTemplate -beginLayout (getPluginResource("cgfxShader", "kVertexData")) -collapse false; editorTemplate -callCustom AEcgfxShader_vertexAttributeLayout AEcgfxShader_vertexAttributeLayout vertexAttributeSource; editorTemplate -endLayout; // Hide "Extra Attributes" UI for dynamic attributes. editorTemplate -addExtraControls; editorTemplate -callCustom AEcgfxShader_suppressExtraNew AEcgfxShader_suppressExtraReplace; string $attr, $attrs[]; $attrs = eval("listAttr -ud "+$node); for ($attr in $attrs) editorTemplate -suppress $attr; // Those attrabutes are meaningless for CgFX shaders string $suppressed[] = { "outColor", "outTransparency", "outMatteOpacity", "outGlowColor", "technique", "texCoordSource", "enableHwShading", "miDeriveFromMaya", "miShinyness", "miAngle", "miSpreadX", "miSpreadY", "miWhiteness", "miSpecularColor", "miReflectivity", "miRefractiveIndex", "miRefractions", "miAbsorbs", "miDiffuse", "miColor", "miTransparency", "miTranslucence", "miTranslucenceFocus", "miNormalCamera" }; for ($attr in $suppressed) editorTemplate -suppress $attr; AEdependNodeTemplate $node; editorTemplate -addExtraControls; editorTemplate -endScrollLayout; } // AEcgfxShader_template