// =========================================================================== // 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 2003-2004, NVIDIA // // // These procedures are used by the cgfxShader Attribute Editor UI, // and may also be called by other tools. // // Scene graph utilities: // cgfxShader_listShapes // cgfxShader_collectUVSetNames // cgfxShader_listUVSets // cgfxShader_collectColorSetNames // cgfxShader_listColorSets // // cgfxShader node utilities: // cgfxShader_connectVector // // String utilities: // cgfxShader_appendUnique // cgfxShader_expandTags // cgfxShader_hasUnmatchedQuote // cgfxShader_split // //////////////////////////////////////////////////////////////////////// // Scene graph utilities // //////////////////////////////////////////////////////////////////////// // Return list of shape nodes that use the given shader node. global proc string[] cgfxShader_listShapes( string $shaderNode ) { string $saResult[]; string $saSearch[] = { $shaderNode }; for ( $shaderNode in $saSearch ) { string $s; string $sa[]; // Skip if not a shader node. $s = `nodeType $shaderNode`; // If shading group, append member node names to result array. if ( `getClassification -satisfies "shadingEngine" $s` ) { $sa = `sets -q -nodesOnly $shaderNode`; for ( $s in $sa ) cgfxShader_appendUnique( $saResult, $s ); } // Visit downstream nodes. else { $sa = `listConnections -d 1 -s 0 $shaderNode`; for ( $s in $sa ) cgfxShader_appendUnique( $saSearch, $s ); } } return $saResult; } // cgfxShader_listShapes // Given a shape node, find its UV set names and append them to $uvSets // (if not already present). global proc cgfxShader_collectUVSetNames( string $shapeNode, string $uvSets[] ) { string $sa[] = `polyUVSet -q -allUVSets $shapeNode`; string $sSet; for ( $sSet in $sa ) cgfxShader_appendUnique( $uvSets, $sSet ); } // cgfxShader_collectUVSetNames // Return list of UV set names existing on one or more of the given mesh nodes. global proc string[] cgfxShader_listUVSets( string $meshNames[] ) { string $uvSets[]; string $meshName; for ( $meshName in $meshNames ) cgfxShader_collectUVSetNames( $meshName, $uvSets ); return sort( $uvSets ); } // cgfxShader_listUVSets // Given a shape node, find its Color set names and append them to $uvSets // (if not already present). global proc cgfxShader_collectColorSetNames( string $shapeNode, string $uvSets[] ) { string $sa[] = `polyColorSet -q -allColorSets $shapeNode`; string $sSet; for ( $sSet in $sa ) cgfxShader_appendUnique( $uvSets, $sSet ); } // cgfxShader_collectColorSetNames // Return list of Color set names existing on one or more of the given mesh nodes. global proc string[] cgfxShader_listColorSets( string $meshNames[] ) { string $uvSets[]; string $meshName; for ( $meshName in $meshNames ) cgfxShader_collectColorSetNames( $meshName, $uvSets ); return sort( $uvSets ); } // cgfxShader_listColorSets //////////////////////////////////////////////////////////////////////// // cgfxShader node utilities // //////////////////////////////////////////////////////////////////////// // Make or break connection to a direction/position vector attribute. // $plug = detination of connection: // node.attr name referring to a direction or position // vector attribute of a cgfxShader node // $item = source of connection: // name of a dag node, or // node.attr of a float3 attribute, or // "" to break connection global proc cgfxShader_connectVector( string $plug, string $item ) { string $tmps[]; string $node = match( "^[^.]*", $plug ); string $attr = match( "[^.]*$", $plug ); // Get strings for the 4th element in the vector. // string $plugW = $plug + "W"; string $attrW = $attr + "W"; string $conns[]; string $connsW[]; int $attrWisExist = `attributeExists $attrW $node` ; $conns = `listConnections -s 1 -d 0 -p 1 $plug`; if ( $attrWisExist ){ $connsW = `listConnections -s 1 -d 0 -p 1 $plugW`; } string $connectedTo; string $connectedToW; if (size($conns) > 0) { $connectedTo = $conns[0]; } if (size($connsW) > 0) { $connectedToW = $connsW[0]; } if ($item == $connectedTo) { // No changes, we're done. // return; } // The parameter declaration in the CgFX file may contain // a semantic keyword telling whether the vector should be // treated as a direction (W = 0) or position (W = 1). string $sParamType = `cgfxShader -p $attr $node`; int $isDir = ( match( "Dir$", $sParamType ) != "" ); // First figure out if we are connecting to one of the light or // camera nodes or if we are connecting to a specific attribute // somewhere. // if (gmatch($item, "*.*")) { // We are connecting to a specific attribute. Break any // existing connection and make the new one. // connectAttr -force $item $plug; $connectedTo = $item; if ( $connectedToW != "" && $attrWisExist ) { disconnectAttr $connectedToW $plugW; setAttr $plugW ( $isDir ? 0.0 : 1.0 ); $connectedToW = ""; } return; } // Ok, we are connecting to one of the lights or cameras from the // optionMenu. First off, find our cgfxVector. If there is // none, create one. // string $cgfxVector = ""; if ($connectedTo != "") { tokenize($connectedTo, ".", $tmps); $connectedToNode = $tmps[0]; if (`nodeType $connectedToNode` != "cgfxVector") { // It was not a cgfxVector // disconnectAttr $connectedTo $plug; $connectedTo = ""; if ($connectedToW != "" && $attrWisExist ) { disconnectAttr $connectedToW $plugW; $connectedToW = ""; } } else { $cgfxVector = $connectedToNode; } } // Connect cgfxVector to cgfxShader. if ($cgfxVector == "") { string $name = $node + "_" + $attr; $cgfxVector = `createNode cgfxVector -n $name`; connectAttr -force ($cgfxVector + ".wv") $plug; if ( $attrWisExist ){ connectAttr -force ($cgfxVector + ".wvw") $plugW; } } // Now connect the specified item to the cgfxVector, blowing away // any existing connection. // string $dstM = $cgfxVector + ".matrix"; string $dstV = $cgfxVector + ".vector"; string $dstD = $cgfxVector + ".isDirection"; string $srcM = $item + ".wm[0]"; // Note: only the first instance works. // (Maya bug: wm[1] etc. don't exist) $tmps = `listConnections -s 1 -d 0 -p 1 $dstM`; if (size($tmps) > 0) { disconnectAttr $tmps[0] $dstM; } $tmps = `listConnections -s 1 -d 0 -p 1 $dstV`; if (size($tmps) > 0) { disconnectAttr $tmps[0] $dstV; } if ($item != "") { connectAttr $srcM $dstM; if ($isDir) { setAttr $dstV -type "double3" 0.0 0.0 -1.0; setAttr $dstD yes; } else { setAttr $dstV -type "double3" 0.0 0.0 0.0; setAttr $dstD no; } } } // cgfxShader_connectVector //////////////////////////////////////////////////////////////////////// // String utilities // //////////////////////////////////////////////////////////////////////// // Append item to string array if not already present. global proc cgfxShader_appendUnique( string $sa[], string $sNewItem ) { string $s; for ( $s in $sa ) if ( $s == $sNewItem ) return; $sa[ size( $sa ) ] = $sNewItem; } // cgfxShader_appendUnique // Substitute values for s in a string. // $sText is a string which may contain angle-bracketed tag // names to be replaced with values from $saDict. For example, // "123XYZ" could become "123valueXYZ". Substitution is // not recursive; result text is not re-examined. Names must be // alphanumeric and non-empty. Name lookup is case-sensitive. // $saDict is a list of tag names and values, like // {"name","value","name2","value2"} // A default value may precede the name/value pairs, like // {"defaultValue","name","value","name2","value2"} global proc string cgfxShader_expandTags( string $sText, string $saDict[] ) { string $sResult; string $s, $t; int $bQuote; while ( "" != ( $s = `match "<[a-zA-Z0-9_]+>.*" $sText` ) ) { int $i = size( $sText ) - size( $s ); if ( $i ) { $t = substring( $sText, 1, $i ); $bQuote = cgfxShader_hasUnmatchedQuote( $t, $bQuote ); $sResult += $t; } $sText = `substitute "<[a-zA-Z0-9_]+>" $s ""`; $t = substring( $s, 2, size( $s ) - size( $sText ) - 1 ); // name for ( $i = size( $saDict ); $i > 1; $i -= 2 ) if ( $t == $saDict[ $i - 2 ] ) break; if ( $i ) // name found or default given { $s = $saDict[ $i - 1 ]; // get value or default if ( $bQuote ) // if within a quoted string $s = encodeString( $s ); // insert backslashes as needed } else // name not in dict and no default $s = "<" + $t + ">"; // preserve unchanged $sResult += $s; } return $sResult + $sText; } // cgfxShader_expandTags // Return true if string contains an unmatched quote. global proc int cgfxShader_hasUnmatchedQuote( string $sText, int $bHasUnmatchedQuote ) { while ( "" != ( $sText = `match "[\"\\].*" $sText` ) ) { if ( substring( $sText, 1, 1 ) == "\\" ) // found backslash $sText = `substitute ".." $sText ""`; // ignore next char else // found quote { $sText = `substitute "." $sText ""`; $bHasUnmatchedQuote = !$bHasUnmatchedQuote; } } return $bHasUnmatchedQuote; } // cgfxShader_hasUnmatchedQuote // Split string at delimiters defined by a regular expression. // Example: // cgfxShader_split( ";x;;;y;", ";" ) returns {"","x","","","y",""} // Note that leading, consecutive, or trailing delimiters give empty // strings, unlike the MEL tokenize() function which strips them: // tokenize( ";x;;;y;", ";", $sa ) yields $sa = {"x","y"} global proc string[] cgfxShader_split( string $sText, string $sPattern ) { string $saResult[]; string $sPatTail = $sPattern + ".*"; string $s; int $l, $m, $n; while ( size( $sText ) != $l && size( $s = `match $sPatTail $sText` ) ) { $l = size( $sText ); $m = $l - size( $s ); $saResult[ $n++ ] = $m ? substring( $sText, 1, $m ) : ""; $sText = `substitute $sPattern $s ""`; } $saResult[ $n ] = $sText; return $saResult; } // cgfxShader_split