Hey guys, so this is a very simple but useful example of how to easily build an fbx exporter for maya with python. By no means, this one covers all export capabilities which maya offers but it has some nice ideas and concepts which are worth showing to you.
For this one I’ve used the built-in maya GUI. It is overall straight forward, only a few things I want to point out. First, for easier readability and accessibility, I always declare class member on the top and not just somewhere in the code by typing self.myVariable.
import maya.cmds as cmds import maya.mel as mel import os ############## ### ### FBX exporter class which can export multiple poly objects ### it puts each object on the origin point, freezes the transform, ### deletes the construction history and exports them into a specified ### directory with specified name ### ############# class FBXExporterWindow: windowName = 'FBXExporterWindow' textfieldGroupName = 'directoryPathTextfield' columnLayoutName = 'columnLayout' exportButtonName = 'exportButton' textfieldName = 'fileNameTextfield' textFieldUserMessage = 'textFieldUserMessage' #opens the window def open(self): # safety check for window self.__deleteAndResetWindow__() #creates the window exporterWindow = cmds.window(self.windowName, title="FBX exporter", iconName=self.windowName, widthHeight=(280, 120)) #adds all ui elements cmds.columnLayout(self.columnLayoutName, w=375, adjustableColumn=False, parent=self.windowName ) cmds.textField(self.textfieldName,placeholderText='Enter Optional File Name', text='',parent=self.columnLayoutName, width=150) cmds.textFieldButtonGrp(self.textfieldGroupName,placeholderText='Directory Path',label='Save Location', cw3=[80,190,50], text='', buttonLabel='Browse', buttonCommand=self.__openDirectoryBrowser__, parent=self.columnLayoutName) cmds.button(self.exportButtonName, label="Export", parent=self.columnLayoutName, width = 322, command = self.startExport) cmds.separator(parent=self.columnLayoutName, height=10) cmds.text(self.textFieldUserMessage,visible=False, parent=self.columnLayoutName) #shows the window cmds.showWindow(exporterWindow) #removes and resets the window def __deleteAndResetWindow__(self): # deletes old window and preference, if it still exists if(cmds.window(self.windowName, query=True, exists=True)): cmds.deleteUI(self.windowName) if(cmds.windowPref(self.windowName, query=1, exists=1)): cmds.windowPref(self.windowName, r=1)
This part of the code sets up the general UI elements inside of a layout. It provides basic functionality for recreating the window and voids duplicates.
#################### #buttonevents #################### #sets the path of the textfield def __setPath__(self,directoryPath): cmds.textFieldButtonGrp(self.textfieldGroupName, edit=True, text=str(directoryPath)) # exports the objects def __startExport__(self,path): selectedObjects = cmds.ls(selection=True) # error handling in case nothing got selected if len(selectedObjects) == 0: self.__showUserMessage__("No Objects are selected. Abbort!", False) return filePathStr = cmds.textFieldButtonGrp(self.textfieldGroupName, query=True, text=True).strip() # error handling in case no path has been provided if len(filePathStr) == 0: self.__showUserMessage__("No path has been chosen. Abbort!", False) return counter = 0 for obj in selectedObjects: #creating a duplicate so we do not change our object duplicate = cmds.duplicate(obj) # reseting position to center with the help of a point constraint self.__setPositionToOrigin__(duplicate) #setting up the export path textFieldContent = cmds.textField(self.textfieldName,query=True,text=True).strip() if len(textFieldContent) == 0: textFieldContent = obj extension = '{0}.fbx'.format(counter) obj_export = filePathStr + "/" + textFieldContent + extension counter+=1 # freezes transform self.__freezeTransform__(duplicate) try: self.__deleteConstructionHistory__(duplicate) # calling mel fbx export command as there is no python equivalent mel.eval('FBXExport -f "{}" -s'.format(obj_export)) except: self.__showUserMessage__("Ignoring object named: '%s'. Export failed, probably not a polygonal object. "%(obj), False) #after exporting, lets delete the duplicate cmds.delete(duplicate) self.__showUserMessage__("Exporting Complete!", True) #opens the browser to pick the directory def __openDirectoryBrowser__(self): path = cmds.fileDialog2(fileMode=3, dialogStyle=1, caption='Choose Directory') if path != None: self.setPath(path[0])
Following you find three helper methods
#################### ### helper methods #################### #sets position to origin def __setPositionToOrigin__(self,obj): temporaryObject = 'gridCenter' cmds.spaceLocator(name=temporaryObject) cmds.select(obj) cmds.move(temporaryObject) cmds.pointConstraint(temporaryObject, obj) cmds.pointConstraint(remove=True) cmds.delete(temporaryObject) #freezes the transform setting all rotation,scales and position values to default def __freezeTransform__(self, obj): cmds.select(obj) #normals = 2, the normals on polygonal objects will be frozen cmds.makeIdentity( apply=True, translate=1, rotate=1, scale=1, normal=2 ) #deletes the construction history of an object def __deleteConstructionHistory__(self,obj): #lets make sure there's only the target object selected cmds.select(obj, replace=True) #removes full construction history cmds.delete(constructionHistory=True)
This is a nice one. Only adding error or warning messages to the log is a bit unhandy, so I’ve prepared a field which shows errors in the window to make sure that the user always has nice feedback on his actions.
# show a message to the user def __showUserMessage__(self, message, isPositiveMessage): colorHighlight = [1,0,0] if isPositiveMessage: colorHighlight = [0,1,0] cmds.text(self.textFieldUserMessage,edit=True, label=message,visible=True, backgroundColor = colorHighlight)
That’s it. Thanks for reading and have a nice day!