// =========================================================================== // Copyright 2018 Autodesk, Inc. All rights reserved. // // Use of this software is subject to the terms of the Autodesk license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // =========================================================================== // // // Creation Date: May 16, 2003 // global proc int isUniqueNode( string $node ) // Description: // If part of a shading network is animated then that network is considered // unique (e.g. if the node is an anim curve or an expression). Returns 1 if // the node is unique and 0 otherwise. // { if ( isAnimCurve( $node ) || "expression" == `nodeType $node` || size( `ls -dag $node` ) > 0 ) { return 1; } return 0; } global proc string[] rootNetworkNodeTypes() // // Description: // Returns a list of the types of nodes to be considered as root nodes for a // network. For example, a shadingEngine is considered the root of a shading // network. // { string $rootNetworkNodeTypes[]; if ( `optionVar -query removeDuplicateShadingNetworksOnImport` ) { // shadingEngines are the root of shading networks $rootNetworkNodeTypes[ `size $rootNetworkNodeTypes` ] = "shadingEngine"; } return $rootNetworkNodeTypes; } global proc int isStringInArray( string $str, string $array[] ) // // Description: // Return 1 if $str is an element of $array, 0 if not // { for ( $i = 0; $i < size( $array ); $i++ ) { if ( $str == $array[ $i ] ) { return 1; } } return 0; } global proc string[] findAllNodesOfType( string $type, string $exclude[] ) // // Description: // Finds the names of all objects of type $type. Returns an array of all // those names excluding the ones found in $exclude // { string $sameType[]; for ( $item in `ls -type $type` ) { if ( !isStringInArray( $item, $exclude ) ) { // Same type and not found in the exclude list. Add to $sameType $sameType[ size( $sameType ) ] = $item; } } return $sameType; } global proc int doRemoveDuplicateShadingNetworks( string $toRemove[], string $toKeep[] ) // // Description: // Compare each node in $toRemove to $toKeep. If the $toRemove node is a // duplicate of a $toKeep node delete the $toRemove node and assign all // geometry to the $toKeep node. // The elements of both $toRemove and $toKeep should be of type // "shadingEngine" // { int $numDeleted = 0; for ( $i = 0; $i < `size $toRemove`; $i++ ) { if ( !`objExists $toRemove[ $i ]` ) { // Ensure that the shadingEngine exists continue; } for ( $j = 0; $j < `size $toKeep`; $j++ ) { if ( !`objExists $toKeep[ $j ]` ) { // Ensure that the shadingEngine exists continue; } $duplicateFound = 0; string $network1[]; string $comparison = `shadingNetworkCompare $toRemove[$i] $toKeep[$j]`; if ( `shadingNetworkCompare -q -equivalent $comparison` ) { $network1 = `shadingNetworkCompare -q -network1 $comparison`; // If the networks are equivalent then assign all // the geometry connected to the imported node to // the existing duplicate. string $geometry[] = `sets -query $toRemove[ $i ]`; if ( size( $geometry ) > 0 ) { // sets can return invalid geometry, so catch it catchQuiet( `sets -edit -forceElement $toKeep[ $j ] $geometry` ); } // // Move any other dagSetMember connections (including // render layer adjustments) to new network too. // string $connections[] = `listConnections -plugs true -connections true ($toRemove[$i]+".dagSetMembers")`; for ($k = 0; $k < size($connections); $k += 2) { string $setDstPlug = $connections[$k]; string $srcPlug = $connections[$k+1]; disconnectAttr $srcPlug $setDstPlug; connectAttr -nextAvailable $srcPlug ($toKeep[$j]+".dagSetMembers"); } // Delete $network1 to remove the duplicate network // entirely. for ( $n in $network1 ) { delete $n; } $numDeleted++; $duplicateFound = 1; } shadingNetworkCompare -delete $comparison; // Clear the string arrays clear $network1; if ( $duplicateFound ) { // If a duplicate was found move on to the next imported // shadingEngine break; } } } return $numDeleted; } proc int combineHash( int $x, int $y) // // Description: // Combines two hash values to a new value. // To minimise hash collisions, this should be non-associative. // { if( $y == 0) return 0; $x = $x * 32768 + $x / 131072 + $y; if( $x == 0) return 1; return $x; } global proc int calculateStringHash( string $s ) // // Description: // Calculate a simple hash value for a string. // { string $hashChars[] = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; int $hash = size( $s ) + 1; $s = tolower( $s ); int $i = size( $s ); for( ; $i; $i--) { int $j = size( $hashChars); string $c = `substring $s $i $i`; for( ; $j--; ) { if( $hashChars[ $j ] == $c) { $hash = $hash * 32768 + $hash / 131072 + $j; break; } } } return $hash; } global proc int calculateNetworkHash( string $node, string $network[] ) // // Description: // Recursive helper function to calculate a network hash value for // an arbitrary node network. // { if ( isUniqueNode( $node ) ) { // If one of these nodes is a unique node then the network is // considered unique (e.g. the network is animated or has an // expression). Return zero (which will propagate all the way // back to give a zero hash code for the whole network) return 0; } // We do have a new node for our network int $hash = calculateStringHash( `nodeType $node` ); // Add the nodes to the $network arrays. $network[ size( $network ) ] = $node; // Find all of the connections from this node string $attrs[] = `listAttr -multi $node`; int $length = size( $attrs ); for ( $i = 0; $i < $length; $i++ ) { if ( $attrs[ $i ] == "message" ) { // Skip all messages continue; } if ( size( match( "outSize", $attrs[ $i ] ) ) > 0 ) { // Work around an assertion when calling getAttr on "outSize" continue; } string $attr = $node + "." + $attrs[ $i ]; // Determine if the attribute is the source of a connection. $isSource = `connectionInfo -isSource $attr`; if( $isSource ) { // Find all the destinations from these source. string $destinations[] = `connectionInfo -destinationFromSource $attr`; int $nDestinations = size( $destinations ); int $j; for ( $j = 0; $j < $nDestinations; $j++ ) { // Each destination is in the form "nodename.attribute[n].*" // Tokenize the destinations by ".". The first string will be the // node name, the second string will be the attribute. string $nodeTokens[]; tokenize $destinations[ $j ] "." $nodeTokens; // Tokenize the attribute on "[" to remove any multi index (indices // may be different for equivalent networks) string $attrTokens[]; tokenize $nodeTokens[ 1 ] "[" $attrTokens; // Recurse into each connected node if ( !isStringInArray( $nodeTokens[ 0 ], $network ) ) { $hash = combineHash( $hash, calculateNetworkHash( $nodeTokens[ 0 ], $network )); if( $hash == 0) return 0; } } } // Determine if the attribute is the destination of a connections $isDestination = `connectionInfo -isDestination $attr`; if( $isDestination ) { // Find the sources from these destinations. string $source = `connectionInfo -sourceFromDestination $attr`; // Each source is in the form "nodename.attribute[n].*" // Tokenize the sources by ".". The first string will be the node // name, the second string will be the attribute. string $nodeTokens[]; tokenize $source "." $nodeTokens; // Tokenize the attribute on "[" to remove any multi index (indices // may be different for equivalent networks) string $attrTokens[]; tokenize $nodeTokens[ 1 ] "[" $attrTokens; // Add the sources to the list of connected nodes if ( !isStringInArray( $nodeTokens[ 0 ], $network ) ) { $hash = combineHash( $hash, calculateNetworkHash( $nodeTokens[ 0 ], $network )); if( $hash == 0) return 0; } } if ( !$isSource && !$isDestination ) { // The attributes are not connected so compare the attribute values string $type = `getAttr -type $attr`; switch ( $type ) { case "float": float $f = `getAttr $attr`; if( abs( $f ) > 0.0001 ) { while( abs( $f ) < 100.0) $f *= 10.0; int $d = $f; $hash = combineHash( $hash, $d); } break; case "string": string $s = `getAttr $attr`; if( size( $s ) > 0 ) { $hash = combineHash( $hash, calculateStringHash( $s )); } break; case "bool": case "int": int $b = `getAttr $attr`; if( $b != 0) { $hash = combineHash( $hash, $b); } break; default: // do nothing break; } } } return $hash; } global proc int calculateShadingNetworkHash( string $node ) // // Description: // Calculate a very rough hash value for a network of nodes. // Any two node/attribute wise identical networks must always // hash to the same hash value. // { string $network[] = { $node }; string $tokenized[]; string $connection; string $connectedNodes[]; int $hash = calculateStringHash( "shadingEngine" ); // Find the connection from the surfaceShader attribute for this // shadingEngine $connection = `connectionInfo -sourceFromDestination ( $node + ".surfaceShader" )`; tokenize $connection "." $tokenized; if ( $tokenized[ 0 ] != "" ) { $hash = combineHash( $hash, calculateNetworkHash( $tokenized[ 0 ], $network )); } // Find the connection from the miMaterialShader attribute for this // shadingEngine if (`attributeExists "miMaterialShader" $node`) { $connection = `connectionInfo -sourceFromDestination ( $node + ".miMaterialShader" )`; tokenize $connection "." $tokenized; if ( $tokenized[ 0 ] != "" ) { $hash = combineHash( $hash, calculateNetworkHash( $tokenized[ 0 ], $network )); } } // Find the connection from the volumeShader attribute for this // shadingEngine $connection = `connectionInfo -sourceFromDestination ( $node + ".volumeShader" )`; tokenize $connection "." $tokenized; if ( $tokenized[ 0 ] != "" ) { $hash = combineHash( $hash, calculateNetworkHash( $tokenized[ 0 ], $network )); } // Find the connection from the displacementShader attribute for this // shadingEngine $connection = `connectionInfo -sourceFromDestination ( $node + ".displacementShader" )`; tokenize $connection "." $tokenized; if ( $tokenized[ 0 ] != "" ) { $hash = combineHash( $hash, calculateNetworkHash( $tokenized[ 0 ], $network )); } // Return our hash value return $hash; }