''' Name: animation_retargeting_tool Description: Transfer animation data between rigs or transfer raw mocap from a skeleton to a custom rig. Author: Joar Engberg 2021 Installation: Add animation_retargeting_tool.py to your Maya scripts folder (Username\Documents\maya\scripts). To start the tool within Maya, run these this lines of code from the Maya script editor or add them to a shelf button: import animation_retargeting_tool animation_retargeting_tool.start() ''' from collections import OrderedDict import os import sys import webbrowser import maya.mel import maya.cmds as cmds from functools import partial import maya.OpenMayaUI as omui maya_version = int(cmds.about(version=True)) if maya_version < 2025: from shiboken2 import wrapInstance from PySide2 import QtCore, QtGui, QtWidgets else: from shiboken6 import wrapInstance from PySide6 import QtCore, QtGui, QtWidgets def maya_main_window(): # Return the Maya main window as QMainWindow main_window = omui.MQtUtil.mainWindow() if sys.version_info.major >= 3: return wrapInstance(int(main_window), QtWidgets.QWidget) else: return wrapInstance(long(main_window), QtWidgets.QWidget) # type: ignore class RetargetingTool(QtWidgets.QDialog): ''' Retargeting tool class ''' WINDOW_TITLE = "Animation Retargeting Tool" def __init__(self): super(RetargetingTool, self).__init__(maya_main_window()) self.script_job_ids = [] self.connection_ui_widgets = [] self.color_counter = 0 self.maya_color_index = OrderedDict([(13, "red"), (18, "cyan"), (14, "lime"), (17, "yellow")]) self.cached_connect_nodes = [] self.setWindowTitle(self.WINDOW_TITLE) self.setWindowFlags(self.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint) self.resize(400, 300) self.create_ui_widgets() self.create_ui_layout() self.create_ui_connections() self.create_script_jobs() if cmds.about(macOS=True): self.setWindowFlags(QtCore.Qt.Tool) def create_ui_widgets(self): self.refresh_button = QtWidgets.QPushButton(QtGui.QIcon(":refresh.png"), "") self.simple_conn_button = QtWidgets.QPushButton("Create Connection") self.ik_conn_button = QtWidgets.QPushButton("Create IK Connection") self.bake_button = QtWidgets.QPushButton("Bake Animation") self.bake_button.setStyleSheet("background-color: lightgreen; color: black") self.batch_bake_button = QtWidgets.QPushButton("Batch Bake And Export ...") self.help_button = QtWidgets.QPushButton("?") self.help_button.setFixedWidth(25) self.rot_checkbox = QtWidgets.QCheckBox("Rotation") self.pos_checkbox = QtWidgets.QCheckBox("Translation") self.mo_checkbox = QtWidgets.QCheckBox("Maintain Offset") self.snap_checkbox = QtWidgets.QCheckBox("Align To Position") def create_ui_layout(self): horizontal_layout_1 = QtWidgets.QHBoxLayout() horizontal_layout_1.addWidget(self.pos_checkbox) horizontal_layout_1.addWidget(self.rot_checkbox) horizontal_layout_1.addWidget(self.snap_checkbox) horizontal_layout_1.addStretch() horizontal_layout_1.addWidget(self.help_button) horizontal_layout_2 = QtWidgets.QHBoxLayout() horizontal_layout_2.addWidget(self.simple_conn_button) horizontal_layout_2.addWidget(self.ik_conn_button) horizontal_layout_3 = QtWidgets.QHBoxLayout() horizontal_layout_3.addWidget(self.batch_bake_button) horizontal_layout_3.addWidget(self.bake_button) connection_list_widget = QtWidgets.QWidget() self.connection_layout = QtWidgets.QVBoxLayout(connection_list_widget) self.connection_layout.setContentsMargins(2, 2, 2, 2) self.connection_layout.setSpacing(3) self.connection_layout.setAlignment(QtCore.Qt.AlignTop) list_scroll_area = QtWidgets.QScrollArea() list_scroll_area.setWidgetResizable(True) list_scroll_area.setWidget(connection_list_widget) separator_line = QtWidgets.QFrame(parent=None) separator_line.setFrameShape(QtWidgets.QFrame.HLine) separator_line.setFrameShadow(QtWidgets.QFrame.Sunken) main_layout = QtWidgets.QVBoxLayout(self) main_layout.setContentsMargins(10, 10, 10, 10) main_layout.addWidget(list_scroll_area) main_layout.addLayout(horizontal_layout_1) main_layout.addLayout(horizontal_layout_2) main_layout.addWidget(separator_line) main_layout.addLayout(horizontal_layout_3) def create_ui_connections(self): self.simple_conn_button.clicked.connect(self.create_connection_node) self.ik_conn_button.clicked.connect(self.create_ik_connection_node) self.refresh_button.clicked.connect(self.refresh_ui_list) self.bake_button.clicked.connect(self.bake_animation_confirm) self.batch_bake_button.clicked.connect(self.open_batch_window) self.help_button.clicked.connect(self.help_dialog) self.rot_checkbox.setChecked(True) self.pos_checkbox.setChecked(True) self.snap_checkbox.setChecked(True) def create_script_jobs(self): self.script_job_ids.append(cmds.scriptJob(event=["SelectionChanged", partial(self.refresh_ui_list)])) self.script_job_ids.append(cmds.scriptJob(event=["NameChanged", partial(self.refresh_ui_list)])) def kill_script_jobs(self): for id in self.script_job_ids: if cmds.scriptJob(exists=id): cmds.scriptJob(kill=id) else: pass def refresh_ui_list(self): self.clear_list() connect_nodes_in_scene = RetargetingTool.get_connect_nodes() self.cached_connect_nodes = connect_nodes_in_scene for node in connect_nodes_in_scene: connection_ui_item = ListItemWidget(parent_instance=self, connection_node=node) self.connection_layout.addWidget(connection_ui_item) self.connection_ui_widgets.append(connection_ui_item) def clear_list(self): self.connection_ui_widgets = [] while self.connection_layout.count() > 0: connection_ui_item = self.connection_layout.takeAt(0) if connection_ui_item.widget(): connection_ui_item.widget().deleteLater() def showEvent(self, event): self.refresh_ui_list() def closeEvent(self, event): self.kill_script_jobs() self.clear_list() def create_connection_node(self): try: selected_joint = cmds.ls(selection=True)[0] selected_ctrl = cmds.ls(selection=True)[1] except: return cmds.warning("No selections!") if self.snap_checkbox.isChecked() == True: cmds.matchTransform(selected_ctrl, selected_joint, pos=True) else: pass if self.rot_checkbox.isChecked() == True and self.pos_checkbox.isChecked() == False: suffix = "_ROT" elif self.pos_checkbox.isChecked() == True and self.rot_checkbox.isChecked() == False: suffix = "_TRAN" else: suffix = "_TRAN_ROT" locator = self.create_ctrl_sphere(selected_joint+suffix) # Add message attr cmds.addAttr(locator, longName="ConnectNode", attributeType="message") cmds.addAttr(selected_ctrl, longName="ConnectedCtrl", attributeType="message") cmds.connectAttr(locator+".ConnectNode",selected_ctrl+".ConnectedCtrl") cmds.parent(locator, selected_joint) cmds.xform(locator, rotation=(0, 0, 0)) cmds.xform(locator, translation=(0, 0, 0)) # Select the type of constraint based on the ui checkboxes if self.rot_checkbox.isChecked() == True and self.pos_checkbox.isChecked() == True: cmds.parentConstraint(locator, selected_ctrl, maintainOffset=True) elif self.rot_checkbox.isChecked() == True and self.pos_checkbox.isChecked() == False: cmds.orientConstraint(locator, selected_ctrl, maintainOffset=True) elif self.pos_checkbox.isChecked() == True and self.rot_checkbox.isChecked() == False: cmds.pointConstraint(locator, selected_ctrl, maintainOffset=True) else: cmds.warning("Select translation and/or rotation!") cmds.delete(locator) cmds.deleteAttr(selected_ctrl, at="ConnectedCtrl") self.refresh_ui_list() def create_ik_connection_node(self): try: selected_joint = cmds.ls(selection=True)[0] selected_ctrl = cmds.ls(selection=True)[1] except: return cmds.warning("No selections!") self.rot_checkbox.setChecked(True) self.pos_checkbox.setChecked(True) if self.snap_checkbox.isChecked() == True: cmds.matchTransform(selected_ctrl, selected_joint, pos=True) else: pass tran_locator = self.create_ctrl_sphere(selected_joint+"_TRAN") cmds.parent(tran_locator, selected_joint) cmds.xform(tran_locator, rotation=(0, 0, 0)) cmds.xform(tran_locator, translation=(0, 0, 0)) rot_locator = self.create_ctrl_locator(selected_joint+"_ROT") # Add message attributes and connect them cmds.addAttr(tran_locator, longName="ConnectNode", attributeType="message") cmds.addAttr(rot_locator, longName="ConnectNode", attributeType="message") cmds.addAttr(selected_ctrl, longName="ConnectedCtrl", attributeType="message") cmds.connectAttr(tran_locator+".ConnectNode",selected_ctrl+".ConnectedCtrl") cmds.parent(rot_locator, tran_locator) cmds.xform(rot_locator, rotation=(0, 0, 0)) cmds.xform(rot_locator, translation=(0, 0, 0)) joint_parent = cmds.listRelatives(selected_joint, parent=True)[0] cmds.parent(tran_locator, joint_parent) cmds.makeIdentity(tran_locator, apply=True, translate=True) cmds.orientConstraint(selected_joint, tran_locator, maintainOffset=False) cmds.parentConstraint(rot_locator, selected_ctrl, maintainOffset=True) # Lock and hide attributes cmds.setAttr(rot_locator+".tx", lock=True, keyable=False) cmds.setAttr(rot_locator+".ty", lock=True, keyable=False) cmds.setAttr(rot_locator+".tz", lock=True, keyable=False) cmds.setAttr(tran_locator+".rx", lock=True, keyable=False) cmds.setAttr(tran_locator+".ry", lock=True, keyable=False) cmds.setAttr(tran_locator+".rz", lock=True, keyable=False) self.refresh_ui_list() def scale_ctrl_shape(self, controller, size): cmds.select(self.get_cvs(controller), replace=True) cmds.scale(size, size, size) cmds.select(clear=True) def get_cvs(self, object): children = cmds.listRelatives(object, type="shape", children=True) ctrl_vertices = [] for c in children: spans = int(cmds.getAttr(c+".spans")) + 1 vertices = "{shape}.cv[0:{count}]".format(shape=c, count=spans) ctrl_vertices.append(vertices) return ctrl_vertices def create_ctrl_locator(self, ctrl_shape_name): curves = [] curves.append(cmds.curve(degree=1, p=[(0, 0, 1), (0, 0, -1)], k=[0,1])) curves.append(cmds.curve(degree=1, p=[(1, 0, 0), (-1, 0, 0)], k=[0,1])) curves.append(cmds.curve(degree=1, p=[(0, 1, 0), (0, -1, 0)], k=[0,1])) locator = self.combine_shapes(curves, ctrl_shape_name) cmds.setAttr(locator+".overrideEnabled", 1) cmds.setAttr(locator+".overrideColor", list(self.maya_color_index.keys())[self.color_counter]) return locator def create_ctrl_sphere(self, ctrl_shape_name): circles = [] for n in range(0, 5): circles.append(cmds.circle(normal=(0,0,0), center=(0,0,0))[0]) cmds.rotate(0, 45, 0, circles[0]) cmds.rotate(0, -45, 0, circles[1]) cmds.rotate(0, -90, 0, circles[2]) cmds.rotate(90, 0, 0, circles[3]) sphere = self.combine_shapes(circles, ctrl_shape_name) cmds.setAttr(sphere+".overrideEnabled", 1) cmds.setAttr(sphere+".overrideColor", list(self.maya_color_index.keys())[self.color_counter]) self.scale_ctrl_shape(sphere, 0.5) return sphere def combine_shapes(self, shapes, ctrl_shape_name): shape_nodes = cmds.listRelatives(shapes, shapes=True) output_node = cmds.group(empty=True, name=ctrl_shape_name) cmds.makeIdentity(shapes, apply=True, translate=True, rotate=True, scale=True) cmds.parent(shape_nodes, output_node, shape=True, relative=True) cmds.delete(shape_nodes, constructionHistory=True) cmds.delete(shapes) return output_node def bake_animation_confirm(self): confirm = cmds.confirmDialog(title="Confirm", message="Baking the animation will delete all the connection nodes. Do you wish to proceed?", button=["Yes","No"], defaultButton="Yes", cancelButton="No") if confirm == "Yes": progress_dialog = QtWidgets.QProgressDialog("Baking animation", None, 0, -1, self) progress_dialog.setWindowFlags(progress_dialog.windowFlags() ^ QtCore.Qt.WindowCloseButtonHint) progress_dialog.setWindowFlags(progress_dialog.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint) progress_dialog.setWindowTitle("Progress...") progress_dialog.setWindowModality(QtCore.Qt.WindowModal) progress_dialog.show() QtCore.QCoreApplication.processEvents() # Bake animation self.bake_animation() progress_dialog.close() if confirm == "No": pass self.refresh_ui_list() def help_dialog(self): confirm = cmds.confirmDialog( title="How to use", message="To create a connection simply select the driver and then the driven and click 'Create connection'. For IK hands and IK feet controllers you can use 'Create IK Connection' for more complex retargeting. \n \nAs an example: if you want to transfer animation from a skeleton to a rig, first select the animated joint and then select the controller before you create a connection.", button=["How to use the retargeting tool (Youtube)", "How to use the batch exporter (Youtube)", "Cancel"], defaultButton="Cancel", cancelButton="Cancel", dismissString="Cancel") if confirm == "How to use the retargeting tool (Youtube)": webbrowser.open_new("https://youtu.be/x2-agPVfinc") elif confirm == "How to use the batch exporter (Youtube)": webbrowser.open_new("https://youtu.be/KOURUtN36ko") def open_batch_window(self): try: self.settings_window.close() self.settings_window.deleteLater() except: pass self.settings_window = BatchExport() self.settings_window.show() @classmethod def bake_animation(cls): if len(cls.get_connected_ctrls()) == 0: cmds.warning("No connections found in scene!") if len(cls.get_connected_ctrls()) != 0: time_min = cmds.playbackOptions(query=True, min=True) time_max = cmds.playbackOptions(query=True, max=True) # Bake the animation cmds.refresh(suspend=True) cmds.bakeResults(cls.get_connected_ctrls(), t=(time_min, time_max), sb=1, at=["rx","ry","rz","tx","ty","tz"], hi="none") cmds.refresh(suspend=False) # Delete the connect nodes for node in cls.get_connect_nodes(): try: cmds.delete(node) except: pass # Remove the message attribute from the controllers for ctrl in cls.get_connected_ctrls(): try: cmds.deleteAttr(ctrl, attribute="ConnectedCtrl") except: pass @classmethod def get_connect_nodes(cls): connect_nodes_in_scene = [] for i in cmds.ls(): if cmds.attributeQuery("ConnectNode", node=i, exists=True) == True: connect_nodes_in_scene.append(i) else: pass return connect_nodes_in_scene @classmethod def get_connected_ctrls(cls): connected_ctrls_in_scene = [] for i in cmds.ls(): if cmds.attributeQuery("ConnectedCtrl", node=i, exists=True) == True: connected_ctrls_in_scene.append(i) else: pass return connected_ctrls_in_scene class ListItemWidget(QtWidgets.QWidget): ''' UI list item class. When a new List Item is created it gets added to the connection_list_widget in the RetargetingTool class. ''' def __init__(self, connection_node, parent_instance): super(ListItemWidget, self).__init__() self.connection_node = connection_node self.main = parent_instance self.setFixedHeight(26) self.create_ui_widgets() self.create_ui_layout() self.create_ui_connections() # If there is already connection nodes in the scene update the color counter try: current_override = cmds.getAttr(self.connection_node+".overrideColor") self.main.color_counter = self.main.maya_color_index.keys().index(current_override) except: pass def create_ui_widgets(self): self.color_button = QtWidgets.QPushButton() self.color_button.setFixedSize(20, 20) self.color_button.setStyleSheet("background-color:" + self.get_current_color()) self.sel_button = QtWidgets.QPushButton() self.sel_button.setStyleSheet("background-color: #707070") self.sel_button.setText("Select") self.sel_button.setFixedWidth(80) self.del_button = QtWidgets.QPushButton() self.del_button.setStyleSheet("background-color: #707070") self.del_button.setText("Delete") self.del_button.setFixedWidth(80) self.transform_name_label = QtWidgets.QLabel(self.connection_node) self.transform_name_label.setAlignment(QtCore.Qt.AlignCenter) self.transform_name_label.setStyleSheet("color: darkgray") for selected in cmds.ls(selection=True): if selected == self.connection_node: self.transform_name_label.setStyleSheet("color: white") def create_ui_layout(self): main_layout = QtWidgets.QHBoxLayout(self) main_layout.setContentsMargins(5, 5, 20, 0) main_layout.addWidget(self.color_button) main_layout.addWidget(self.transform_name_label) main_layout.addWidget(self.sel_button) main_layout.addWidget(self.del_button) def create_ui_connections(self): self.sel_button.clicked.connect(self.select_connection_node) self.del_button.clicked.connect(self.delete_connection_node) self.color_button.clicked.connect(self.set_color) def select_connection_node(self): cmds.select(self.connection_node) for widget in self.main.connection_ui_widgets: widget.transform_name_label.setStyleSheet("color: darkgray") self.transform_name_label.setStyleSheet("color: white") def delete_connection_node(self): try: for attr in cmds.listConnections(self.connection_node, destination=True): if cmds.attributeQuery("ConnectedCtrl", node=attr, exists=True): cmds.deleteAttr(attr, at="ConnectedCtrl") except: pass cmds.delete(self.connection_node) self.main.refresh_ui_list() def set_color(self): # Set the color on the connection node and button connection_nodes = self.main.cached_connect_nodes color = list(self.main.maya_color_index.keys()) if self.main.color_counter < 3: self.main.color_counter += 1 else: self.main.color_counter = 0 for node in connection_nodes: cmds.setAttr(node+".overrideEnabled", 1) cmds.setAttr(node+".overrideColor", color[self.main.color_counter]) for widget in self.main.connection_ui_widgets: widget.color_button.setStyleSheet("background-color:"+self.get_current_color()) def get_current_color(self): current_color_index = cmds.getAttr(self.connection_node+".overrideColor") color_name = self.main.maya_color_index.get(current_color_index, "grey") return color_name class BatchExport(QtWidgets.QDialog): ''' Batch exporter class ''' WINDOW_TITLE = "Batch Exporter" def __init__(self): super(BatchExport, self).__init__(maya_main_window()) self.setWindowTitle(self.WINDOW_TITLE) self.setWindowFlags(self.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint) self.resize(400, 250) self.animation_clip_paths = [] self.output_folder = "" if cmds.about(macOS=True): self.setWindowFlags(QtCore.Qt.Tool) self.create_ui() self.create_connections() def create_ui(self): self.file_list_widget = QtWidgets.QListWidget() self.remove_selected_button = QtWidgets.QPushButton("Remove Selected") self.remove_selected_button.setFixedHeight(24) self.load_anim_button = QtWidgets.QPushButton("Load Animations") self.load_anim_button.setFixedHeight(24) self.export_button = QtWidgets.QPushButton("Batch Export Animations") self.export_button.setStyleSheet("background-color: lightgreen; color: black") self.connection_file_line = QtWidgets.QLineEdit() self.connection_file_line.setToolTip("Enter the file path to the connection rig file. A file which contains a rig with connections.") self.connection_filepath_button = QtWidgets.QPushButton() self.connection_filepath_button.setIcon(QtGui.QIcon(":fileOpen.png")) self.connection_filepath_button.setFixedSize(24, 24) self.export_selected_label = QtWidgets.QLabel("Export Selected (Optional):") self.export_selected_line = QtWidgets.QLineEdit() self.export_selected_line.setToolTip("Enter the name(s) of the nodes that should be exported. Leave blank to export all.") self.export_selected_button = QtWidgets.QPushButton() self.export_selected_button.setIcon(QtGui.QIcon(":addClip.png")) self.export_selected_button.setFixedSize(24, 24) self.output_filepath_button = QtWidgets.QPushButton() self.output_filepath_button.setIcon(QtGui.QIcon(":fileOpen.png")) self.file_type_combo = QtWidgets.QComboBox() self.file_type_combo.addItems([".fbx", ".ma"]) horizontal_layout_1 = QtWidgets.QHBoxLayout() horizontal_layout_1.addWidget(QtWidgets.QLabel("Connection Rig File:")) horizontal_layout_1.addWidget(self.connection_file_line) horizontal_layout_1.addWidget(self.connection_filepath_button) horizontal_layout_2 = QtWidgets.QHBoxLayout() horizontal_layout_2.addWidget(self.load_anim_button) horizontal_layout_2.addWidget(self.remove_selected_button) horizontal_layout_3 = QtWidgets.QHBoxLayout() horizontal_layout_3.addWidget(QtWidgets.QLabel("Output File Type:")) horizontal_layout_3.addWidget(self.file_type_combo) horizontal_layout_3.addWidget(self.export_button) horizontal_layout_4 = QtWidgets.QHBoxLayout() horizontal_layout_4.addWidget(self.export_selected_label) horizontal_layout_4.addWidget(self.export_selected_line) horizontal_layout_4.addWidget(self.export_selected_button) main_layout = QtWidgets.QVBoxLayout(self) main_layout.addWidget(self.file_list_widget) main_layout.addLayout(horizontal_layout_2) main_layout.addLayout(horizontal_layout_1) main_layout.addLayout(horizontal_layout_4) main_layout.addLayout(horizontal_layout_3) def create_connections(self): self.connection_filepath_button.clicked.connect(self.connection_filepath_dialog) self.load_anim_button.clicked.connect(self.animation_filepath_dialog) self.export_button.clicked.connect(self.batch_action) self.export_selected_button.clicked.connect(self.add_selected_action) self.remove_selected_button.clicked.connect(self.remove_selected_item) def connection_filepath_dialog(self): file_path = QtWidgets.QFileDialog.getOpenFileName(self, "Select Connection Rig File", "", "Maya ACSII (*.ma);;All files (*.*)") if file_path[0]: self.connection_file_line.setText(file_path[0]) def output_filepath_dialog(self): folder_path = QtWidgets.QFileDialog.getExistingDirectory(self, "Select export folder path", "") if folder_path: self.output_folder = folder_path return True else: return False def animation_filepath_dialog(self): file_paths = QtWidgets.QFileDialog.getOpenFileNames(self, "Select Animation Clips", "", "FBX (*.fbx);;All files (*.*)") file_path_list = file_paths[0] if file_path_list[0]: for i in file_path_list: self.file_list_widget.addItem(i) for i in range(0, self.file_list_widget.count()): self.file_list_widget.item(i).setTextColor(QtGui.QColor("white")) def add_selected_action(self): selection = cmds.ls(selection=True) if len(selection) > 1: text_string = "[" for i in selection: text_string += '"{}", '.format(i) text_string = text_string[:-2] text_string += "]" elif selection[0]: text_string = "{}".format(selection[0]) else: pass self.export_selected_line.setText(text_string) def remove_selected_item(self): try: selected_items = self.file_list_widget.selectedItems() for item in selected_items: self.file_list_widget.takeItem(self.file_list_widget.row(item)) except: pass def batch_action(self): if self.connection_file_line.text() == "": cmds.warning("Connection file textfield is empty. Add a connection rig file to be able to export. This file should contain the rig and connections to a skeleton.") elif self.file_list_widget.count() == 0: cmds.warning("Animation clip list is empty. Add animation clips to the list to be able to export!") else: confirm_dialog = self.output_filepath_dialog() if confirm_dialog == True: self.bake_export() else: pass def bake_export(self): self.animation_clip_paths = [] for i in range(self.file_list_widget.count()): self.animation_clip_paths.append(self.file_list_widget.item(i).text()) number_of_operations = len(self.animation_clip_paths) * 3 current_operation = 0 progress_dialog = QtWidgets.QProgressDialog("Preparing", "Cancel", 0, number_of_operations, self) progress_dialog.setWindowFlags(progress_dialog.windowFlags() ^ QtCore.Qt.WindowCloseButtonHint) progress_dialog.setWindowFlags(progress_dialog.windowFlags() ^ QtCore.Qt.WindowContextHelpButtonHint) progress_dialog.setValue(0) progress_dialog.setWindowTitle("Progress...") progress_dialog.setWindowModality(QtCore.Qt.WindowModal) progress_dialog.show() QtCore.QCoreApplication.processEvents() export_result = [] for i, path in enumerate(self.animation_clip_paths): # Import connection file and animation clip progress_dialog.setLabelText("Baking and exporting {} of {}".format(i + 1, len(self.animation_clip_paths))) self.file_list_widget.item(i).setTextColor(QtGui.QColor("yellow")) cmds.file(new=True, force=True) cmds.file(self.connection_file_line.text(), open=True) maya.mel.eval('FBXImportMode -v "exmerge";') maya.mel.eval('FBXImport -file "{}";'.format(path)) current_operation += 1 progress_dialog.setValue(current_operation) # Bake animation RetargetingTool.bake_animation() current_operation += 1 progress_dialog.setValue(current_operation) # Export animation output_path = self.output_folder + "/" + os.path.splitext(os.path.basename(path))[0] if self.file_type_combo.currentText() == ".fbx": output_path += ".fbx" cmds.file(rename=output_path) if self.export_selected_line.text() != "": cmds.select(self.export_selected_line.text(), replace=True) maya.mel.eval('FBXExport -f "{}" -s'.format(output_path)) else: maya.mel.eval('FBXExport -f "{}"'.format(output_path)) elif self.file_type_combo.currentText() == ".ma": output_path += ".ma" cmds.file(rename=output_path) if self.export_selected_line.text() != "": cmds.select(self.export_selected_line.text(), replace=True) cmds.file(exportSelected=True, type="mayaAscii") else: cmds.file(exportAll=True, type="mayaAscii") current_operation += 1 progress_dialog.setValue(current_operation) if os.path.exists(output_path): self.file_list_widget.item(i).setTextColor(QtGui.QColor("lime")) export_result.append("Sucessfully exported: "+output_path) else: self.file_list_widget.item(i).setTextColor(QtGui.QColor("red")) export_result.append("Failed exporting: "+output_path) print("------") for i in export_result: print(i) print("------") progress_dialog.setValue(number_of_operations) progress_dialog.close() def start(): global retarget_tool_ui try: retarget_tool_ui.close() retarget_tool_ui.deleteLater() except: pass retarget_tool_ui = RetargetingTool() retarget_tool_ui.show() if __name__ == "__main__": start()