From 4a1ff7b9ca4f5d91a05a365cf45562727378c4a1 Mon Sep 17 00:00:00 2001 From: Simon Anderson Date: Wed, 11 Oct 2023 16:20:18 +1300 Subject: [PATCH 1/4] Added Custom ComboBox, that refreshes on popup --- .../game_tools_fbx/anim_clip_widgets.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py index 377bfd8e..a3c69b83 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py @@ -171,7 +171,7 @@ def set_transparent_button(button): self._clip_name_lineedit.setStatusTip("Clip Name") clip_name_layout.addWidget(self._clip_name_lineedit) - self._anim_layer_combo = QtWidgets.QComboBox() + self._anim_layer_combo = AnimationLayerCB() self._anim_layer_combo.setStatusTip("Animation Layer") clip_name_layout.addWidget(self._anim_layer_combo) @@ -379,3 +379,25 @@ def _on_custom_context_menu_requested(self, pos): utils.open_mgear_playblast_folder ) context_menu.exec_(self.mapToGlobal(pos)) + + +class AnimationLayerCB(QtWidgets.QComboBox): + """ + Custom overloaded QComboBox, this will automatically refresh the combobox everytime + it shows the values. Keep the Combobox up to date with the AnimationLayers available. + """ + def __init__(self, parent=None): + super(AnimationLayerCB, self).__init__(parent=parent) + + def showPopup(self): + super(AnimationLayerCB, self).showPopup() + currentText = self.currentText() + + self.clear() + + anim_layers = utils.all_anim_layers_ordered() + self.addItems(["None"] + anim_layers) + + self.setCurrentText(currentText) + + # TODO: Could to a check here to see if the layer still exists, else add a warning. \ No newline at end of file From 808a25acbbd8c3fd77cc6efc7a542236269c741e Mon Sep 17 00:00:00 2001 From: Simon Anderson Date: Wed, 11 Oct 2023 16:43:59 +1300 Subject: [PATCH 2/4] Timeline start and end is forced to update playback size --- .../mgear/shifter/game_tools_fbx/anim_clip_widgets.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py index a3c69b83..379c2a13 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py @@ -329,12 +329,15 @@ def _on_set_range_button_clicked(self): self._end_frame_box.setText(max_frame) def _on_play_button_clicked(self): - start_time = str(int(cmds.playbackOptions(query=True, ast=True))) - end_time = str(int(cmds.playbackOptions(query=True, aet=True))) + start_time = str(int(cmds.playbackOptions(query=True, min=True))) + end_time = str(int(cmds.playbackOptions(query=True, max=True))) + anim_start_time = str(int(cmds.playbackOptions(query=True, ast=True))) + anim_end_time = str(int(cmds.playbackOptions(query=True, aet=True))) start_frame = self._start_frame_box.text() end_frame = self._end_frame_box.text() - if not (start_frame == start_time and end_frame == end_time): + if not (start_frame == start_time and end_frame == end_time) or \ + not (start_frame == anim_start_time and end_frame == anim_end_time): cmds.playbackOptions( animationStartTime=start_frame, minTime=start_frame, From 124953c2c7c9248cc318e2c3b3118d1d45916c30 Mon Sep 17 00:00:00 2001 From: Simon Anderson Date: Thu, 12 Oct 2023 16:50:51 +1300 Subject: [PATCH 3/4] refactoring export_animation_clip - added Core.animLayer module --- release/scripts/mgear/core/animLayers.py | 81 +++++++++++ .../game_tools_fbx/anim_clip_widgets.py | 4 +- .../shifter/game_tools_fbx/fbx_export_node.py | 2 +- .../shifter/game_tools_fbx/fbx_exporter.py | 22 ++- .../mgear/shifter/game_tools_fbx/utils.py | 132 ++++++------------ 5 files changed, 148 insertions(+), 93 deletions(-) create mode 100644 release/scripts/mgear/core/animLayers.py diff --git a/release/scripts/mgear/core/animLayers.py b/release/scripts/mgear/core/animLayers.py new file mode 100644 index 00000000..ebf550ce --- /dev/null +++ b/release/scripts/mgear/core/animLayers.py @@ -0,0 +1,81 @@ +import maya.cmds as cmds +import maya.api.OpenMaya as om + +def animation_layer_exists(layer_name): + is_anim_layer = False + + if not layer_name: + return False + exists = cmds.objExists(layer_name) + + if exists: + is_anim_layer = cmds.nodeType(layer_name) == "animLayer" + + return exists and is_anim_layer + + +def base_animation_layer_name(): + if animation_layer_exists("BaseAnimation"): + return "BaseAnimation" + + # BaseAnimation Layer might have been renamed, perform dg lookup. + nodes = find_anim_layer_base_nodes() + + # No nodes found + if len(nodes) == 0: + return "" + return om.MFnDependencyNode(nodes[0]).name() + + +def find_anim_layer_base_nodes(): + anim_layer_nodes = [] + + # Create an iterator to iterate through all nodes + it = om.MItDependencyNodes(om.MFn.kAnimLayer) + + while not it.isDone(): + # Get the animation layer node + m_obj = it.thisNode() + + layer_dg = om.MFnDependencyNode(m_obj) + child_plug_obj = layer_dg.findPlug("childrenLayers", False) + child_plug = om.MPlug(child_plug_obj) + + # Animation layer has connected children, then it is the + # "BaseAnimation" layer. + if child_plug.numConnectedElements() > 0: + # Append the animation layer node to the list + anim_layer_nodes.append(m_obj) + + # Move to the next node + it.next() + + return anim_layer_nodes + + +def all_anim_layers_ordered(include_base_animation=True): + """Recursive function that returns all available animation layers within current scene. + + Returns: + list[str]: list of animation layers. + """ + + def _add_node_recursive(layer_node): + all_layers.append(layer_node) + child_layers = ( + cmds.animLayer(layer_node, query=True, children=True) or list() + ) + for child_layer in child_layers: + _add_node_recursive(child_layer) + + all_layers = list() + root_layer = cmds.animLayer(query=True, root=True) + if not root_layer: + return all_layers + _add_node_recursive(root_layer) + + if not include_base_animation: + if "BaseAnimation" in all_layers: + all_layers.remove("BaseAnimation") + + return all_layers \ No newline at end of file diff --git a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py index 379c2a13..e1e1b7e2 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py @@ -4,7 +4,7 @@ from mgear.vendor.Qt import QtWidgets, QtCore, QtGui -from mgear.core import pyqt +from mgear.core import pyqt, utils as coreUtils from mgear.shifter.game_tools_fbx import fbx_export_node, utils @@ -293,7 +293,7 @@ def _on_update_anim_clip(self): anim_clip_data = fbx_export_node.FbxExportNode.ANIM_CLIP_DATA.copy() anim_clip_data["title"] = self._clip_name_lineedit.text() anim_clip_data["enabled"] = self._export_checkbox.isChecked() - # anim_clip_data["frame_rate"] = self._frame_rate_combo.currentText() + anim_clip_data["frame_rate"] = coreUtils.get_frame_rate() anim_clip_data["start_frame"] = int(self._start_frame_box.text()) anim_clip_data["end_frame"] = int(self._end_frame_box.text()) anim_layer = self._anim_layer_combo.currentText() diff --git a/release/scripts/mgear/shifter/game_tools_fbx/fbx_export_node.py b/release/scripts/mgear/shifter/game_tools_fbx/fbx_export_node.py index 9486f295..07455fbb 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/fbx_export_node.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/fbx_export_node.py @@ -33,7 +33,7 @@ class FbxExportNode(object): ANIM_CLIP_DATA = { "title": "Untitled", "enabled": True, - "frame_rate": "30 fps", + "frame_rate": "", "start_frame": int(pm.playbackOptions(min=True, query=True)), "end_frame": int(pm.playbackOptions(max=True, query=True)), } diff --git a/release/scripts/mgear/shifter/game_tools_fbx/fbx_exporter.py b/release/scripts/mgear/shifter/game_tools_fbx/fbx_exporter.py index 54faf52d..a19f82c1 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/fbx_exporter.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/fbx_exporter.py @@ -48,6 +48,7 @@ def closeEvent(self, event): super(FBXExporter, self).closeEvent(event) def dockCloseEventTriggered(self): + # FIXME: Move the save_data before calling the super close. Check all saving works! super(FBXExporter, self).dockCloseEventTriggered() self._save_data_to_export_node() @@ -701,8 +702,25 @@ def export_animation_clips(self): export_config = self._get_current_tool_data() anim_clip_data = export_node.get_animation_clips(joint_root) - export_config["anim_clips"] = anim_clip_data - utils.export_animation_clip(export_config) + + # Stores the selected objects, before performing the export. + # These objects will be selected again, upon completion of + # exporting. + original_selection = cmds.ls(selection=True) + + # Exports each clip + for clip_data in anim_clip_data: + + if not clip_data["enabled"]: + # skip disabled clips. + continue + + result = utils.export_animation_clip(export_config, clip_data) + if not result: + print("\t!!! >>> Failed to export clip: {}".format(clip_data["title"])) + + if original_selection: + pm.select(original_selection) return True diff --git a/release/scripts/mgear/shifter/game_tools_fbx/utils.py b/release/scripts/mgear/shifter/game_tools_fbx/utils.py index 94a857ac..8476ae3d 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/utils.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/utils.py @@ -9,10 +9,11 @@ import pymel.core as pm import maya.cmds as cmds import maya.mel as mel +import maya.api.OpenMaya as om from mgear.vendor.Qt import QtWidgets, QtCore -from mgear.core import pyFBX as pfbx, pyqt, string +from mgear.core import pyFBX as pfbx, pyqt, string, utils as coreUtils, animLayers from mgear.shifter.game_tools_fbx import sdk_utils NO_EXPORT_TAG = "no_export" @@ -295,56 +296,45 @@ def export_skeletal_mesh_partitions(jnt_roots, export_data): return True -def export_animation_clip(root_joint, **export_data): - if not root_joint or not cmds.objExists(root_joint): - cmds.warning( - 'Was not possible to export animation clip because root joint "{}" was not found within scene'.format( - root_joint - ) - ) - return False - - # only enabled animation clips will be exported - enabled = export_data.get("enabled", False) - if not enabled: - return False - - # namespace = pm.PyNode(root_joint).namespace() - # if not namespace: - # cmds.warning('Only animations with namespaces can be exported') - # return False - # namespace = namespace[:-1] +def export_animation_clip(config_data, clip_data): + """ + Exports a singular animation clip. - time_range = cmds.playbackOptions( - query=True, minTime=True - ), cmds.playbackOptions(query=True, maxTime=True) - start_frame = export_data.get("startFrame", time_range[0]) - end_frame = export_data.get("endFrame", time_range[1]) + config_data: The configuration for the scene/session + clip_data: Information about the clip to be exported. + """ + # Clip Data + start_frame = clip_data.get("startFrame", + cmds.playbackOptions(query=True, minTime=True)) + end_frame = clip_data.get("endFrame", + cmds.playbackOptions(query=True, maxTime=True)) + title = clip_data.get("title", "") + frame_rate = clip_data.get("frameRate", coreUtils.get_frame_rate()) + anim_layer = clip_data.get("animLayer", "") + + # Config Data + root_joint = config_data.get("joint_root", "") + file_path = config_data.get("file_path", "") + file_name = config_data.get("file_name", "") + preset_path = config_data.get("preset_path", None) + up_axis = config_data.get("up_axis", cmds.optionVar(query="upAxisDirection")) + file_type = config_data.get("file_type", "binary").lower() + fbx_version = config_data.get("fbx_version", None) + remove_namespaces = config_data.get("remove_namespace") + scene_clean = config_data.get("scene_clean", True) + + # Validate timeline range if start_frame > end_frame: - cmds.error( - "Start frame {} must be lower than the end frame {}".format( - start_frame, end_frame - ) - ) + msg = "Start frame {} must be lower than the end frame {}" + cmds.error(msg.format(start_frame, end_frame)) return False - title = export_data.get("title", "") - file_path = export_data.get("file_path", "") - file_name = export_data.get("file_name", "") - preset_path = export_data.get("preset_path", None) - up_axis = export_data.get("up_axis", None) - file_type = export_data.get("file_type", "binary").lower() - fbx_version = export_data.get("fbx_version", None) - remove_namespaces = export_data.get("remove_namespace") - scene_clean = export_data.get("scene_clean", True) - frame_rate = export_data.get("frameRate", "30 FPS") - anim_layer = export_data.get("animLayer", "") - + # Validate file path if not file_path or not file_name: - cmds.warning( - "No valid file path or file name given for the FBX to export!" - ) + msg = "No valid file path or file name given for the FBX to export!" + cmds.warning(msg) return False + if title: file_name = "{}_{}".format(file_name, title) if not file_name.endswith(".fbx"): @@ -352,28 +342,23 @@ def export_animation_clip(root_joint, **export_data): path = string.normalize_path(os.path.join(file_path, file_name)) print("\t>>> Export Path: {}".format(path)) - original_selection = pm.ls(sl=True) - auto_key_state = pm.autoKeyframe(query=True, state=True) - cycle_check = pm.cycleCheck(query=True, evaluation=True) + auto_key_state = cmds.autoKeyframe(query=True, state=True) + cycle_check = cmds.cycleCheck(query=True, evaluation=True) scene_modified = cmds.file(query=True, modified=True) current_frame_range = cmds.currentUnit(query=True, time=True) current_frame = cmds.currentTime(query=True) - original_start_frame = int(pm.playbackOptions(min=True, query=True)) - original_end_frame = int(pm.playbackOptions(max=True, query=True)) + original_start_frame = cmds.playbackOptions(query=True, minTime=True) + original_end_frame = cmds.playbackOptions(query=True, maxTime=True) temp_mesh = None temp_skin_cluster = None original_anim_layer_weights = None try: # set anim layer to enable - if ( - anim_layer - and cmds.objExists(anim_layer) - and cmds.nodeType(anim_layer) == "animLayer" - ): + if animLayers.animation_layer_exists(anim_layer): to_activate = None to_deactivate = [] - anim_layers = all_anim_layers_ordered(include_base_animation=False) + anim_layers = animLayers.all_anim_layers_ordered(include_base_animation=False) original_anim_layer_weights = { anim_layer: cmds.animLayer(anim_layer, query=True, weight=True) for anim_layer in anim_layers @@ -419,15 +404,15 @@ def export_animation_clip(root_joint, **export_data): # Set frame range cmds.currentTime(start_frame) - old_frame_rate = AS_FRAMES[cmds.currentUnit(query=True, time=True)] - new_frame_rate = FRAMES_PER_SECOND[frame_rate][1] + old_frame_rate = coreUtils.get_frame_rate() + new_frame_rate = frame_rate # only set if frame rate changed mult_rate = new_frame_rate / old_frame_rate if mult_rate != 1: old_range = start_frame, end_frame start_frame = old_range[0] * mult_rate end_frame = old_range[1] * mult_rate - cmds.currentUnit(time=FRAMES_PER_SECOND[frame_rate][0]) + coreUtils.set_frame_rate(frame_rate) pm.autoKeyframe(state=False) pfbx.FBXExportAnimationOnly(v=False) @@ -493,8 +478,6 @@ def export_animation_clip(root_joint, **export_data): pm.cycleCheck(evaluation=cycle_check) cmds.playbackOptions(min=original_start_frame, max=original_end_frame) - if original_selection: - pm.select(original_selection) # if the scene was not modified before doing our changes, we force it back now if scene_modified is False: @@ -741,33 +724,6 @@ def get_end_joint(start_joint): return end_joint -def all_anim_layers_ordered(include_base_animation=True): - """Recursive function that returns all available animation layers within current scene. - - Returns: - list[str]: list of animation layers. - """ - - def _add_node_recursive(layer_node): - all_layers.append(layer_node) - child_layers = ( - cmds.animLayer(layer_node, query=True, children=True) or list() - ) - for child_layer in child_layers: - _add_node_recursive(child_layer) - - all_layers = list() - root_layer = cmds.animLayer(query=True, root=True) - if not root_layer: - return all_layers - _add_node_recursive(root_layer) - - if not include_base_animation: - if "BaseAnimation" in all_layers: - all_layers.remove("BaseAnimation") - - return all_layers - if __name__ == "__main__": if sys.version_info[0] == 2: From 027b076aa30dc3926943ad8e4308ebfdb50c985f Mon Sep 17 00:00:00 2001 From: Simon Anderson Date: Tue, 17 Oct 2023 14:14:28 +1300 Subject: [PATCH 4/4] Added AnimLayers module to mGear Core - added more layer functionality --- release/scripts/mgear/core/animLayers.py | 69 ++++++++++++++++++- .../game_tools_fbx/anim_clip_widgets.py | 6 +- .../mgear/shifter/game_tools_fbx/utils.py | 29 ++------ 3 files changed, 76 insertions(+), 28 deletions(-) diff --git a/release/scripts/mgear/core/animLayers.py b/release/scripts/mgear/core/animLayers.py index ebf550ce..1bc628e2 100644 --- a/release/scripts/mgear/core/animLayers.py +++ b/release/scripts/mgear/core/animLayers.py @@ -2,6 +2,12 @@ import maya.api.OpenMaya as om def animation_layer_exists(layer_name): + """Checks ig animation layer exists. + + :param str layer_name: Name of the layer that will be checked. + :return: True if the animation layer exists + :rtype: bool + """ is_anim_layer = False if not layer_name: @@ -15,6 +21,12 @@ def animation_layer_exists(layer_name): def base_animation_layer_name(): + """Finds the name of the base animation layer, as this base layer might not + be named the default "BaseAnimation". + + :return: name of the base animation layer. + :rtype: str + """ if animation_layer_exists("BaseAnimation"): return "BaseAnimation" @@ -28,6 +40,12 @@ def base_animation_layer_name(): def find_anim_layer_base_nodes(): + """Finds the animation base layer, as this base layer might not + be named the default "BaseAnimation". + + :return: list of Maya Objects. + :rtype: list[om.MObject] + """ anim_layer_nodes = [] # Create an iterator to iterate through all nodes @@ -56,8 +74,8 @@ def find_anim_layer_base_nodes(): def all_anim_layers_ordered(include_base_animation=True): """Recursive function that returns all available animation layers within current scene. - Returns: - list[str]: list of animation layers. + :return: list of animation layers. + :rtype: list[str] """ def _add_node_recursive(layer_node): @@ -78,4 +96,49 @@ def _add_node_recursive(layer_node): if "BaseAnimation" in all_layers: all_layers.remove("BaseAnimation") - return all_layers \ No newline at end of file + return all_layers + + +def get_layer_weights(): + """ + Gets all the animation layer weights. + + :return: Dictionary with the name of the animation layer, followed by the weight. + :rtype: dict + """ + anim_layer_weights = {} + anim_layers = all_anim_layers_ordered(include_base_animation=False) + + for anim_layer in anim_layers: + anim_layer_weights[anim_layer] = cmds.animLayer(anim_layer, query=True, weight=True) + + return anim_layer_weights + + +def set_layer_weights(anim_layer_weights): + """ + Sets the animation layer weights. + + :param dict anim_layer_weights: Dictionary containing all the animation layer names, and the weights to be set for each anim layer name. + """ + for name, weight in anim_layer_weights.items(): + cmds.animLayer(name, edit=True, weight=weight) + + +def set_layer_weight(name, value=1.0, toggle_other_off=False, include_base=False): + """ + Set a specific AnimationLayers weight. + + :param str name: Name of the animation layer to have its weight modified. + :param float value: weight of the animation layer + :param bool toggle_other_off: Turn all other layers off + :param bool include_base: include the base animation layer when toggling off layers. + """ + anim_layers = all_anim_layers_ordered(include_base_animation=include_base) + + if toggle_other_off: + for layer_name in anim_layers: + cmds.animLayer(layer_name, edit=True, weight=0.0) + + cmds.animLayer(name, edit=True, weight=value) + \ No newline at end of file diff --git a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py index e1e1b7e2..d2eaa96b 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/anim_clip_widgets.py @@ -4,7 +4,7 @@ from mgear.vendor.Qt import QtWidgets, QtCore, QtGui -from mgear.core import pyqt, utils as coreUtils +from mgear.core import pyqt, utils as coreUtils, animLayers from mgear.shifter.game_tools_fbx import fbx_export_node, utils @@ -253,7 +253,7 @@ def refresh(self): with pyqt.block_signals(self._anim_layer_combo): self._anim_layer_combo.clear() # TODO: Maybe we should filter display layers that are set with override mode? - anim_layers = utils.all_anim_layers_ordered() + anim_layers = animLayers.all_anim_layers_ordered() self._anim_layer_combo.addItems(["None"] + anim_layers) self._anim_layer_combo.setCurrentText( anim_clip_data.get("anim_layer", "None") @@ -398,7 +398,7 @@ def showPopup(self): self.clear() - anim_layers = utils.all_anim_layers_ordered() + anim_layers = animLayers.all_anim_layers_ordered() self.addItems(["None"] + anim_layers) self.setCurrentText(currentText) diff --git a/release/scripts/mgear/shifter/game_tools_fbx/utils.py b/release/scripts/mgear/shifter/game_tools_fbx/utils.py index 8476ae3d..0262146c 100644 --- a/release/scripts/mgear/shifter/game_tools_fbx/utils.py +++ b/release/scripts/mgear/shifter/game_tools_fbx/utils.py @@ -304,13 +304,13 @@ def export_animation_clip(config_data, clip_data): clip_data: Information about the clip to be exported. """ # Clip Data - start_frame = clip_data.get("startFrame", + start_frame = clip_data.get("start_frame", cmds.playbackOptions(query=True, minTime=True)) - end_frame = clip_data.get("endFrame", + end_frame = clip_data.get("end_frame", cmds.playbackOptions(query=True, maxTime=True)) title = clip_data.get("title", "") - frame_rate = clip_data.get("frameRate", coreUtils.get_frame_rate()) - anim_layer = clip_data.get("animLayer", "") + frame_rate = clip_data.get("geo_root", coreUtils.get_frame_rate()) + anim_layer = clip_data.get("anim_layer", "") # Config Data root_joint = config_data.get("joint_root", "") @@ -351,26 +351,12 @@ def export_animation_clip(config_data, clip_data): original_end_frame = cmds.playbackOptions(query=True, maxTime=True) temp_mesh = None temp_skin_cluster = None - original_anim_layer_weights = None + original_anim_layer_weights = animLayers.get_layer_weights() try: # set anim layer to enable if animLayers.animation_layer_exists(anim_layer): - to_activate = None - to_deactivate = [] - anim_layers = animLayers.all_anim_layers_ordered(include_base_animation=False) - original_anim_layer_weights = { - anim_layer: cmds.animLayer(anim_layer, query=True, weight=True) - for anim_layer in anim_layers - } - for found_anim_layer in anim_layers: - if found_anim_layer != anim_layer: - to_deactivate.append(found_anim_layer) - else: - to_activate = found_anim_layer - for anim_layer_to_deactivate in to_deactivate: - cmds.animLayer(anim_layer_to_deactivate, edit=True, weight=0.0) - cmds.animLayer(to_activate, edit=True, weight=1.0) + animLayers.set_layer_weight(anim_layer, toggle_other_off=True) # disable viewport mel.eval("paneLayout -e -manage false $gMainPane") @@ -463,8 +449,7 @@ def export_animation_clip(config_data, clip_data): finally: # setup again original anim layer weights if anim_layer and original_anim_layer_weights: - for name, weight in original_anim_layer_weights.items(): - cmds.animLayer(name, edit=True, weight=weight) + animLayers.set_layer_weights(original_anim_layer_weights) if temp_skin_cluster and cmds.objExists(temp_skin_cluster): cmds.delete(temp_skin_cluster)