# ------------------------------------------------------------------------- # Description block """ FindShaderUser.pys File version: 1.1.0 Bernard Lebel October 2004 Description: This script has multiple uses, but they all serve the same purpose: finding the objects that use specific shaders. Currently the object offers 3 types of search: Material: the script finds the objects using the selected material Shader: the script finds the objects using the shader the user types in a window Clip: the scripts finds the objects using the selected clip Usage: Material: - Select material - Run script - Choose Material from drop-down menu Shader: - Run script - Choose Shader from drop-down menu - Type the shader you want in the ShaderName field. The shader name must be like it appears by default in the Render Tree when you create a node. Clip: - Select clip from the list of clips - Run script - Choose Clip from drop-down menu Result: The using objects are put in a group. Note that the Scene_root cant be added to the group. On top of putting the objects in a group, the script will print additional information. For example, if you choose a shader, the script will print the materials using that type of shader. Misc: This shader is a consolidation of 2 scripts I wrote for XSI 3.5: - FindClipUsers - FindShaderUsers And adds the functionality to find material users. """ # ------------------------------------------------------------------------- # Import block import win32com.client from win32com.client import constants as c # ------------------------------------------------------------------------- # Instantiation block xsifactory = win32com.client.Dispatch( 'XSI.Factory' ) xsi = Application # ------------------------------------------------------------------------- # Functions block def mainFunction( iType ): """ Main function. Handles the selection, depending on what the user wants. """ sGroupName = '' # Get selected material/clip oSel = xsi.selection(0) # --------------------------------- Material if iType == 0: # Check if something is selected if not oSel: xsi.logmessage( '< FindShaderUser > :: Nothing selected, script failed.', c.siError ) else: # Check if material is selected if not oSel.type == c.siMaterialType: xsi.logmessage( '< FindShaderUser > :: No material selected, script failed.', c.siError ) else: sGroupName = 'mat_' + oSel.name # Call function that collects scene items that can hold a material aSceneItems = collectSceneItems() # Call function to get mapped dictionary of materials and objects aSceneDict = mapMaterials( aSceneItems ) # Get list of users mapped to selected material aUsers = aSceneDict[ str( oSel.library.fullname + '.' + oSel.name ) ] # --------------------------------- Shader elif iType == 1: # Call function to get the shader ClassID sKey = getKey() # Call function to prompt user for shader name to search # Returns array of shaders oSel = getShaders( sKey ) if oSel == None: pass else: sGroupName = 'shdr_' + oSel.name # Call function that collects scene items that can hold a material aSceneItems = collectSceneItems() # Call function to get mapped dictionary of materials and objects aSceneDict = mapMaterials( aSceneItems ) # Call function that collects all scene materials aMats = collectSceneMaterials() # Call function that traverses all materials' Render Tree and returns users aUsers = traverseMaterials( oSel, aMats, sKey, iType, aSceneDict ) # --------------------------------- Clip else: # Check if something is selected if not oSel: xsi.logmessage( '< FindShaderUser > :: Nothing selected, script failed.', c.siError ) else: # Check if clip is selected if not oSel.type == 'ImageClip': xsi.logmessage( '< FindShaderUser > :: No clip selected, script failed.', c.siError ) else: sKey = '{22C3E8F8-CCEA-11D2-B35B-00105A1E70DE}' sGroupName = 'clip_' + oSel.name # Call function that collects scene items that can hold a material aSceneItems = collectSceneItems() # Call function to get mapped dictionary of materials and objects aSceneDict = mapMaterials( aSceneItems ) # Call function that collects all scene materials aMats = collectSceneMaterials() # Call function that traverses all materials' Render Tree and returns users aUsers = traverseMaterials( oSel, aMats, sKey, iType, aSceneDict ) # Check if sGroupName is defined # If not defined, it means an error-check has failed if sGroupName == '': pass else: # Call final function groupAndPrint( sGroupName, aUsers ) def collectSceneItems(): """ Collects all scene items that can have materials (local AND applied). """ # Create empty array to temporarily store objects aSceneItems = [] # ---------------------- Collect geometry (including hairs) # Get geometric objects oSceneGeometry = xsi.activesceneroot.findchildren( '', '', c.siGeometryFamily, True ) aSceneItems.extend( oSceneGeometry ) # ---------------------- Collect clusters # Iterate collected geometry for oSceneGeo in oSceneGeometry: # Check if geometry has clusters if not oSceneGeo.activeprimitive.geometry.clusters: pass else: # Iterate clusters for oCluster in oSceneGeo.activeprimitive.geometry.clusters: # Check if cluster is poly cluster if xsi.classname( oCluster ) == 'Cluster' and oCluster.type == 'poly': # Add cluster to scene items array aSceneItems.append( oCluster ) # ---------------------- Collect groups # Get groups under scene root, add them to collection if there are oSceneGroups = xsi.activesceneroot.groups aSceneItems.extend( oSceneGroups ) # Get models under scene root oSceneModels = xsi.activesceneroot.models if oSceneModels.count > 0: # Enumerate models for oSceneModel in oSceneModels: # Get groups under model oModelGroups = oSceneModel.groups; aSceneItems.extend( oModelGroups ) # ---------------------- Collect partitions # Get passes oPasses = xsi.enumelements( "Passes.List" ) # Enumerate passes for oPass in oPasses: # Get pass oPass = xsi.getvalue( oPass ) # Get pass elements oPassElems = xsi.enumelements( oPass ) # Enumerate through pass elements for oPassElem in oPassElems: # Check if pass element is named "Partitions" (wich is the partition collection) if oPassElem.name == "Partitions": # Get partition collection oPartitions = xsi.enumelements( oPassElem ) # Enumerate through partitions for oPartition in oPartitions: # Add partition to partition collection aSceneItems.append( oPartition ) # ---------------------- Collect scene root aSceneItems.append( xsi.activesceneroot ) return aSceneItems def collectSceneMaterials(): """ Collects all materials in the scene. """ # Create empty list to store materials aMats = [] # Collect material libraries oLibs = xsi.activeproject.activescene.materiallibraries # Iterate material libraries for oLib in oLibs: # Add materials of library to array aMats.extend( oLib.items ) return aMats def mapMaterials( aSceneItems ): """ Maps all objects with material to their respective materials. Returns dictionary whose key values are arrays. ARGUMENTS: aSceneItems: the list of all objects in the scene that can hold a material. """ # Create empty dictionary to store materials and users aSceneDict = {} # Iterate list of items for oSceneItem in aSceneItems: # Check if scene items has local properties if not oSceneItem.localproperties: pass else: # Iterate local properties for oLocalProp in oSceneItem.localproperties: # Check if local property is a material if oLocalProp.type == c.siMaterialType: aSceneDict.setdefault( oLocalProp.library.fullname + '.' + oLocalProp.name, [] ).append( oSceneItem.fullname ) # Return collection return aSceneDict def getKey(): """ Prompts the user to know what shader to get the ClassID of. Returns the ClassID of the shader. """ oProp = xsi.activesceneroot.addcustomproperty( 'ShaderName', False ) oProp.addparameter3( 'ShaderName', c.siString, '', None, None, False, False ) xsi.inspectobj( oProp, '', 'Shader_Name', c.siModal, True ) # Retrieve shader name from user input sShaderName = oProp.parameters( 'ShaderName' ).value # Get shader ClassID sKey = xsi.dictionaryShaderCLSID( sShaderName ) xsi.deleteobj( oProp ) return sKey def getShaders( sKey ): # Get shaders oShaders = xsi.findobjects( None, sKey ) # Check if shaders were found if oShaders.count < 1: xsi.logmessage( '< FindShaderUser > :: Specified shader not found in scene.' ) xsi.deleteobj( oProp ) return None else: # Get shader and return shader oShader = oShaders(0) oSel = oShader return oSel def traverseMaterials( oSel, aMats, sKey, iType, aSceneDict ): """ Traverses the Render Tree of all provided material. When it finds the desired shaders or clips, it returns the users. ARGUMENTS: oSel: the selected clip. aMats: all scene materials. sKey: the shader ClassID. iType: the type of article the user wants to find. 0 = Material 1 = Shader 2 = Clip aSceneDict: a dictionary of materials with objects mapped to them """ aUsers = [] #xsi.logmessage( 'DEBUG: Length of scene materials: ' + str( len( aMats ) ) ) # Create empty collection to store materials using the shader oMatUsers = xsifactory.CreateObject( 'XSI.Collection' ) oMatUsers.unique = True # Iterate list of materials for oMat in aMats: #xsi.logmessage( 'DEBUG: Evaluating material... ' + oMat.name ) # Create empty collection to store shaders for each material oShaders = xsifactory.CreateObject( 'XSI.Collection' ) oShaders.unique = True bLoop = True iShaderCount = 0 oDatabase = XSIUtils.DataRepository oShaders.add( oMat ) # Start loop that evaluates each shader of the material while bLoop == True: iShaderCount = oShaders.count # Iterate collection of shaders for oShader in oShaders: # Get shader ClassID sShaderID = oDatabase.getidentifier( oShader, 3 ) #xsi.logmessage( 'DEBUG: sShaderID: ' + sShaderID ) # Check if ClassID of current shader is the same as shader looked for if sShaderID == sKey: #xsi.logmessage( 'DEBUG: Adding shader... ' + oShader.name ) oShaders.add( oShader ) if iType == 1: #xsi.logmessage( 'DEBUG: Adding material...' + oMat.name ) oMatUsers.add( oMat ) else: oClip = xsi.dictionary.getobject( oShader.fullname ) aSource = oClip.fullname.split( '.' ) sSource = aSource[-1] sClip = 'Clips.' + sSource #xsi.logmessage( 'DEBUG: sClip: ' + sClip ) #xsi.logmessage( 'DEBUG: oSel: ' + oSel.fullname ) #xsi.logmessage( 'DEBUG: sSource: ' + sSource ) if sClip == oSel.fullname: #xsi.logmessage( 'DEBUG: adding material....' + oMat.fullname ) oMatUsers.add( oMat ) # Iterate parameters of shader for oParameter in oShader.parameters: # Check if shader input has source (something connected) if not oParameter.source: pass else: # Add source to shader list oShaders.add( oParameter.source ) # Check if shader were collected if oShaders.count > iShaderCount: # If shaders were collected, start loop over again bLoop = True else: # If no shader were collected, stop loop bLoop = False #xsi.logmessage( 'DEBUG: oMatUsers length: ' + str( oMatUsers.count ) ) # Iterate collected materials for oMatUser in oMatUsers: if iType == 1: sType = 'shader' else: sType = 'clip' xsi.logmessage( '< FindShaderUser > :: Material using ' + sType + ': ' + oMatUser.fullname ) aUsers.extend( aSceneDict[ oMatUser.fullname ] ) #for oUser in aUsers: # xsi.logmessage( oUser ) return aUsers def groupAndPrint( sGroupName, aUsers ): """ Puts the users in a group under the scene root. Prints the users of the material, the shaders or the clip. ARGUMENTS: sGroupName: the desired group name, depending on the type of search performed. This group name is given by the calling function: Material: 'mat_' + oSel.name Shader: 'shdr_' + sShaderName Clip: 'clip_' + oSel.name aUsers: list of users to put in a group and to print. """ if iType == 0: sArticle = 'Material' elif iType == 1: sArticle = 'Shader' else: sArticle = 'Clip' xsi.deselectall() xsi.creategroup( sGroupName, None, xsi.activesceneroot ) oGroup = xsi.activesceneroot.groups( sGroupName ) for oUser in aUsers: oUser = xsi.dictionary.getobject( oUser ) xsi.logmessage( '< FindShaderUser > :: ' + sArticle + ' user: ' + oUser.fullname ) xsi.addtogroup( oGroup, oUser ) # ------------------------------------------------------------------------- # Script block # Ask use what operation to perform aChoiceList = [ 'Material', 'Shader', 'Clip' ] oDial = xsifactory.CreateObject( 'XSIDial.XSIDialog' ) iType = oDial.Combo( 'Find what', aChoiceList ) # iType: -1 = Cancel, 0 = Material, 1 = Shader, 2 = 'Clip' if iType == -1: xsi.logmessage( '< FindShaderUser > :: Script cancelled by user.' ) else: # Call main function mainFunction( iType )