Sunday, August 12, 2018

Character Importer Script

Script Description:

This script is for a character importer. I wrote two versions one for Maya and one for Motion Builder. The script relies on a folder structure where the first folder holds a series of folders for different projects and in each of the project folders there are FBX files containing characters. When the user hits the import button it imports the specified character as a reference in the Maya version and merges the file into the current scene for the Motion Builder version.

UI:

For this script I wrote a fairly simple UI containing two dropdown menus, a check box with a place to input a name space and a button that says import character. The idea was to use the dropdowns to let the user select the files without making them enter a path themselves. 





One of the things I used for the first time while writing this script was a validator to limit the possible inputs into the namespace line edit. The validator limited the first input to be a letter or an underscore and further inputs to be letters, numbers or underscores.


#adds a validator to limit possible inputs into the LineEdit
v = QtCore.QRegExp('[A-Z|a-z|_]\w+')
self.validator = QtGui.QRegExpValidator(v)
self.textField.setValidator(self.validator)



Functions:

One of the other challenges with this script was make sure that the user couldn't break things by reusing namespaces or importing the same character without a namespace twice. This is especially important in Motion Builder because it will overwrite things already in the scene if you merge something with the same naming into the scene. In Motion Builder I decided to compare the name of the character or the namespace to objects already in the scene using the find objects by name function that is built into Motion Builder. For the case of looking for characters without namespaces I also limited the type of the object to constraint since in Motion Builder characters are constraints. 


#this function checks if character or namespace is already in use before importing     
def warningsCheck(self):
 warning = False
 if self.checkbox.isChecked() == True:
  #checks scene for the namespace
  namespace = str(self.textField.text())
  searchName = str(self.textField.text() + ':*')
  foundCharacters = utils.findObjects(searchName)                  
  
  #if nothing was found using that namespace import the character with the namespace
  if len(foundCharacters) > 0:
   namespaceWarning = QtGui.QMessageBox()
   namespaceWarning.setWindowTitle('Warning')
   namespaceWarning.setText('This namespace is already in use. Please change namespace to import.')
   namespaceWarning.exec_()
   warning = True 

  #if no namespace is set 
        elif self.checkbox.isChecked() == False:
            #gets the name of the character
            fileName = str(self.charDropdown.currentText())
            #removes _skel.fbx from the character name 
            charName = utils.shortenString(fileName, 9)
            #looks for uses of the character name in the scene 
            foundCharacters = utils.findObjects(charName)
            charExists = False
            
            #checks to see if there is a motionbuilder character with the given name in the list
            for char in foundCharacters:
                if str(char.FullName) == str('Constraint::' + charName):
                    charExists = True
                    break
                    
            #if no character was found imports the character
            if charExists == True:
                charExistsWarning = QtGui.QMessageBox()
                charExistsWarning.setWindowTitle('Warning')
                charExistsWarning.setText('This character is already in the scene. Please add a namespace to import')
                charExistsWarning.exec_()
                warning = True
                return warning 
        return warning 






For importing the character I wrote a utility function that I kept in a separate utility file since this is defiantly a process that could be used in other scripts. One of the things that I found tricky to grasp when starting scripting in Motion Builder was that things like merge options are their own object in Motion Builder. My function starts by setting merge options then checks if the user wants  a namespace then merges the file. 

#merges in a character to the current scene         
def mergeCharacter(namespace, fileName, FBApp):
    mergeOptions = FBFbxOptions(True)
    if not namespace == None:
        Names = FBStringList(str(namespace))
        mergeOptions.SetMultiLoadNamespaceList(Names)
    FBApp.FileMerge(fileName, False, mergeOptions)







In the Maya version of the script I do both the importing and warning checks in the same function. In the pymel library there is a simple function called namespace that with the flag exists can check if that namespace is in use in the scene which was simpler than the method I used in Motion Builder. Though I did have to get a list of referenced nodes in Maya and compare names to check for the existence of a character much like I did in Motion Builder when a namespace wasn't used.

#checks for characters in the scene and imports character if no warnings 
 def importChar(self):
 importFile = self.path + '\\' + self.prjDropdown.currentText() + '\\' + self.charDropdown.currentText()
 
 #if the user set a namespace and the namespace doesn't exist in maya reference the character
 if self.checkbox.isChecked() == True and (pm.namespace(exists = self.textBox.text()) == False):
    pm.createReference(importFile, namespace = self.textBox.text())
   
 #if the user did not set a namespace check if character is already in the scene    
 elif self.checkbox.isChecked() == False:
    
  charExists = False 
  #gets list of referecned nodes 
  list = pm.ls(rf = True)
  #gets charater name from character dropdown menu
  fileName = self.charDropdown.currentText()
  #removes .fbx and adds RN for reference name 
  charName = fileName[0:(len(fileName)-4)] +'RN'
   
  #compares references to character name 
  for node in list:
   #if the character is in the scene it marks character Exists as true 
   if charName == node: 
    charExists = True
    break
    
  #if the character exists give the user a warning           
  if charExists == True:
   charExistsWarning = QtWidgets.QMessageBox()
   charExistsWarning.setWindowTitle('Warning')
   charExistsWarning.setText('This character is already in the scene. Please add a namespace to import.')
   charExistsWarning.exec_()
   
  #if the character doesn't exist refrence the character    
  else:
     pm.createReference(importFile)
     
 #if the users set a namespace and the namespace exists in maya give the user a warning    
 else: 
    
  namespaceWarning = QtWidgets.QMessageBox()
  namespaceWarning.setWindowTitle('Warning')
  namespaceWarning.setText('This namespace is already in use. Please change namespace to import.')
  namespaceWarning.exec_()







The second drop down menu that held the characters for import needed to update based off of the first drop down menu which held the projects. The first thing that this function does is clear the character drop down menu of old characters. Next it alters the path to look in the new project folder and gets a list of the folders contents. After that using a for loop it filters for FBX files by using the endswith() python string function to check for .fbx on the end of file names. This allows me to be sure that the file the user picks is one that either Motion Builder or Maya can open. If it ends with .fbx it's added to the character drop down menu.

#changes the contents of the second dropbox based off the folder selected by the first dropbox    
def setupCharDropdown(self):
 self.charDropdown.clear()
 path = FILEPATH + '\\' + self.prjDropdown.currentText()
 files = os.listdir(path)
 for f in files:
  if f.endswith('.fbx') == True:
   self.charDropdown.addItem(f)

No comments:

Post a Comment