From 87a39f7bfac57010d0058073898a099dae138890 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Tue, 6 May 2025 09:04:57 +0530 Subject: [PATCH 01/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 147 ++++++++++++++++++ .../plugins/publish/collect_anim_curve.py | 142 +++++++++++++++++ 2 files changed, 289 insertions(+) create mode 100644 client/ayon_maya/plugins/load/load_anim.py create mode 100644 client/ayon_maya/plugins/publish/collect_anim_curve.py diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py new file mode 100644 index 00000000..3fd51df1 --- /dev/null +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -0,0 +1,147 @@ +import os +import json +import maya.cmds as cmds +import ayon_api +from ayon_core.pipeline import get_representation_path +from ayon_core.settings import get_project_settings +from ayon_maya.api.lib import unique_namespace +from ayon_maya.api.pipeline import containerise +from ayon_maya.api import plugin +from ayon_maya.api.plugin import get_load_color_for_product_type +from ayon_core.pipeline.load.plugins import discover_loader_plugins +from ayon_core.pipeline.load.utils import get_representation_context +# from ayon_maya.plugins.load import load_referenece +import logging +import pymel.core as pm + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + +LOADER_PLUGINS = { + "reference": "ReferenceLoader" +} + + +class AnimLoader(plugin.Loader): + """Load anim on character""" + + product_types = {"animation"} + representations = {"anim"} + + label = "Load Anim" + order = -5 + icon = "code-fork" + color = "orange" + + def load(self, context, name, namespace, data): + project_name = context['project']['name'] + anim_file = context['representation']['attrib']['path'] + self.log.info(f"anim_file: {anim_file}") + plugins = {i.__name__: i for i in discover_loader_plugins(project_name)} + name_space = context['representation']['data']['context']['product']['name'].replace( + context['representation']['data']['context']['product']['type'], '') + if not cmds.namespace(exists=name_space): + assets = context['version']['data'].get("assets", []) + self.log.info(f"assets: {assets}") + current_asset = [asset for asset in assets if asset['asset_name'] in anim_file] + if not current_asset: + self.log.warning(f"Asset not found in version data.") + return + current_asset = current_asset[0] + asset_data = ayon_api.get_folder_by_name(project_name=project_name, folder_name=current_asset['asset_name']) + versions = ayon_api.get_last_version_by_product_name(project_name, "rigMain", asset_data['id']) + representations = ayon_api.get_representations( + project_name=project_name, + version_ids={versions['id']}, + fields={"id", "name", "files.path"} + ) + rep_id = None + for rep in representations: + if rep['name'] == 'ma': + rep_id = rep['id'] + break + context = get_representation_context("PRPT", rep_id) + options = {'attach_to_root': True} + _plugin = plugins.get(LOADER_PLUGINS["reference"])() + _plugin.load(context=context, name=context['product']['name'], namespace=name_space, options=options) + ctrl_set = pm.ls(name_space + ":rigMain_controls_SET") + if not ctrl_set: + self.log.warning("No control set found in instance data") + return + ctrls = pm.listConnections(ctrl_set[0], source=1, type='transform') + if not ctrls: + self.log.warning("No controls found in instance data") + return + self.log.debug(f"ctrls: {ctrls}") + self.log.info(f"namespace: {namespace}") + read_anim(filepath=anim_file, objects=ctrls, namespace=name_space) + + +def read_anim(filepath='C:/temp/anim.anim', objects=pm.selected(), namespace=None): + if not os.path.exists(filepath): + return [False, 'invalid filepath'] + with open(filepath, "r", encoding='utf-8') as reader: + anim_data = json.loads(reader.read()) + for j, obj in enumerate(objects): + obj_shot_name = obj.name() + obj_longname = obj.longName() + if namespace: + obj_longname = obj_longname.replace(namespace, '{namespace}') + ctrl_value = anim_data.get(obj_longname, []) + if not ctrl_value: + continue + for attrs in ctrl_value: + if not hasattr(obj, attrs): + continue + if attrs in ['lock']: + continue + try: + cur_attr = getattr(obj, attrs) + except AttributeError as e: + logger.warning('skipping for {0} as attribute {1} was not found'.format(obj_shot_name, attrs)) + continue + key_type = ctrl_value[attrs]['type'] + if key_type == 'static': + key_value = ctrl_value[attrs]['value'] + connected = pm.listConnections(cur_attr, destination=False, source=True) + if not connected and not cur_attr.isLocked(): + cur_attr.set(key_value) + if key_type == 'keyed': + key_values = ctrl_value[attrs]['keys'] + infinity_data = ctrl_value[attrs]['infinity'] + pre_infinity = json.loads(infinity_data.get('preInfinity')) + post_infinity = json.loads(infinity_data.get('postInfinity')) + weighted_tangents = json.loads(infinity_data.get('weightedTangents')) + for keys in key_values: + time = json.loads(keys.get('key')) + value = json.loads(keys.get('value')) + breakdown = json.loads(keys.get('breakdown')) + tan_lock = json.loads(keys.get('lock')) + weight_lock = json.loads(keys.get('weightLock')) + in_type = json.loads(keys.get('inTangentType')) + out_type = json.loads(keys.get('outTangentType')) + tan1 = json.loads(keys.get('inAngle')) + tan2 = json.loads(keys.get('outAngle')) + weight1 = json.loads(keys.get('inWeight')) + weight2 = json.loads(keys.get('outWeight')) + res = pm.setKeyframe(cur_attr, time=time, value=value, bd=breakdown) + if weighted_tangents: + pm.keyTangent(cur_attr, weightedTangents=True, edit=True) + try: + pm.keyTangent(cur_attr, lock=tan_lock, time=time) + except Exception as e: + logger.warning(e) + + if weighted_tangents: + pm.keyTangent(cur_attr, time=time, weightLock=weight_lock) + if in_type != 'fixed' and out_type != 'fixed': + pm.keyTangent(cur_attr, e=1, a=1, time=time, itt=in_type, ott=out_type) + if in_type == 'fixed' and out_type != 'fixed': + pm.keyTangent(cur_attr, e=1, a=1, time=time, inAngle=tan1, inWeight=weight1, itt=in_type, + ott=out_type) + if in_type == 'fixed' and out_type == 'fixed': + pm.keyTangent(cur_attr, e=1, a=1, time=time, inAngle=tan1, inWeight=weight1, outAngle=tan2, + outWeight=weight2, itt=in_type, ott=out_type) + + pm.setInfinity(cur_attr, poi=post_infinity, pri=pre_infinity) + return None diff --git a/client/ayon_maya/plugins/publish/collect_anim_curve.py b/client/ayon_maya/plugins/publish/collect_anim_curve.py new file mode 100644 index 00000000..fe6aac35 --- /dev/null +++ b/client/ayon_maya/plugins/publish/collect_anim_curve.py @@ -0,0 +1,142 @@ +import os +import json +import logging +import pymel.core as pm + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) + +from ayon_maya.api import plugin +from pyblish.api import ExtractorOrder + + +class ExtractAnimCrv(plugin.MayaExtractorPlugin): + order = ExtractorOrder + label = "Extract Animation curves (TEST)" + families = ["animation"] + hosts = ["maya"] + + def process(self, instance): + instance_data = instance.data + # Define output path + staging_dir = self.staging_dir(instance) + filename = "{0}.anim".format(instance_data['variant']) + out_path = os.path.join(staging_dir, filename) + controls = [x for x in instance_data['setMembers'] if x.endswith(":rigMain_controls_SET")] + if not controls: + self.log.warning("No controls found in instance data") + return + ctrls = pm.listConnections(controls[0], source=1, type='transform') + name_space = instance_data['variant'] + reference_node = [x for x in pm.listReferences() if x.namespace == name_space][0] + self.log.info(f"controls: {controls}") + self.write_anim(objects=ctrls, filepath=os.path.realpath(out_path), namespace=name_space) + if "representations" not in instance.data: + instance.data["representations"] = [] + representation = { + 'name': 'anim', 'ext': 'anim', + 'files': os.path.basename(out_path), + 'stagingDir': staging_dir.replace("\\", "/") + } + version_data = instance.data.get("versionData", {}) + assets = version_data.get("assets", []) + if not assets: + version_data["assets"] = [] + asset_data = { + "namespace": name_space, + "path": reference_node.path.__str__(), + "asset_name": name_space.split("_rigMain_")[0], + } + version_data["assets"].append(asset_data) + instance_data["data"] = [name_space + ':' + reference_node.path.__str__()] + instance.data["assets"] = [name_space + ':' + reference_node.path.__str__()] + instance.data["versionData"] = version_data + self.log.info(f"representation: {representation}") + instance.data["representations"].append(representation) + + def write_anim(self, objects=pm.selected(), filepath='C:/temp/anim.anim', namespace=None): + self.log.info(f"objects: {objects}") + self.log.info(f"Writing animation curves to {filepath}") + self.log.info(f"namespace: {namespace}") + anim_data = {} + for j, obj in enumerate(objects): + obj_shot_name = obj.name() + obj_longname = obj.longName() + if namespace: + obj_longname = obj_longname.replace(namespace, '{namespace}') + anim_data[obj_longname] = {} + + channels = obj.listConnections(type='animCurve', connections=True, s=1, d=0) + channel_dict = {} + for i, channel in enumerate(channels): + channel = channel[1] + split_name = obj_shot_name + channel_name = (channels[i][0].name().split(split_name + '.')[1]) + if channel_name not in channel_dict: + channel_dict[channel_name] = {} + channel_dict[channel_name]['type'] = 'keyed' + + keys = pm.animation.keyframe(channel, q=True) + values = pm.animation.keyframe(channel, q=True, valueChange=True) + breakdown = pm.animation.keyframe(channel, q=True, breakdown=True) + in_tangent_type = pm.animation.keyTangent(channel, q=True, inTangentType=True) + out_tangent_type = pm.animation.keyTangent(channel, q=True, outTangentType=True) + lock = pm.animation.keyTangent(channel, q=True, lock=True) + weight_lock = pm.animation.keyTangent(channel, q=True, weightLock=True) + in_angle = pm.animation.keyTangent(channel, q=True, inAngle=True) + out_angle = pm.animation.keyTangent(channel, q=True, outAngle=True) + in_weight = pm.animation.keyTangent(channel, q=True, inWeight=True) + out_weight = pm.animation.keyTangent(channel, q=True, outWeight=True) + weighted_tangents = pm.animation.keyTangent(channel, q=True, weightedTangents=True)[0] + + pre_infinity = channel.preInfinity.get() + post_infinity = channel.postInfinity.get() + channel_dict[channel_name]['infinity'] = { + 'preInfinity': json.dumps(pre_infinity), + 'postInfinity': json.dumps(post_infinity), + 'weightedTangents': json.dumps(weighted_tangents), + } + + channel_dict[channel_name]['keys'] = [] + for y, key in enumerate(keys): + bd = 0 + for bd_item in breakdown: + if bd_item == key: + bd = 1 + channel_dict[channel_name]['keys'].append({ + 'key': json.dumps(keys[y]), + 'value': json.dumps(values[y]), + 'breakdown': json.dumps(bd), + 'inTangentType': json.dumps(in_tangent_type[y]), + 'outTangentType': json.dumps(out_tangent_type[y]), + 'lock': json.dumps(lock[y]), + 'weightLock': json.dumps(weight_lock[y]), + 'inAngle': json.dumps(in_angle[y]), + 'outAngle': json.dumps(out_angle[y]), + 'inWeight': json.dumps(in_weight[y]), + 'outWeight': json.dumps(out_weight[y]) + }) + static_chans = pm.listAnimatable(obj) + for static_chan in static_chans: + test_it = pm.keyframe(static_chan, q=True) + connected = pm.listConnections(static_chan, destination=False, source=True) + if test_it or connected: + logger.warning('skipping for {0} as attribute {1} is connected'.format(obj_shot_name, static_chan)) + continue + if pm.nodeType(static_chan.name().split(".")[0]) == "camera": + static_name = static_chan.name().split('.')[1] + else: + static_name = static_chan.name().split(obj_shot_name + '.') + if not len(static_name) > 1: + continue + static_name = static_name[1] + # static_name = static_chan.name().split(obj_shot_name + '.')[1] + if static_name not in channel_dict: + channel_dict[static_name] = {'type': 'static'} + channel_dict[static_name]['value'] = static_chan.get() + anim_data[obj_longname] = channel_dict + if not os.path.exists(os.path.dirname(filepath)): + os.makedirs(os.path.dirname(filepath)) + with open(filepath, 'w') as json_file: + json.dump(anim_data, json_file, indent=4) + return filepath From cc6288c6be4fa73d79139c64af35a24f1e07e7f7 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Tue, 6 May 2025 13:22:59 +0530 Subject: [PATCH 02/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 13 ++++--------- .../{collect_anim_curve.py => export_anim_curve.py} | 0 2 files changed, 4 insertions(+), 9 deletions(-) rename client/ayon_maya/plugins/publish/{collect_anim_curve.py => export_anim_curve.py} (100%) diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index 3fd51df1..b069de0f 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -2,15 +2,9 @@ import json import maya.cmds as cmds import ayon_api -from ayon_core.pipeline import get_representation_path -from ayon_core.settings import get_project_settings -from ayon_maya.api.lib import unique_namespace -from ayon_maya.api.pipeline import containerise from ayon_maya.api import plugin -from ayon_maya.api.plugin import get_load_color_for_product_type from ayon_core.pipeline.load.plugins import discover_loader_plugins from ayon_core.pipeline.load.utils import get_representation_context -# from ayon_maya.plugins.load import load_referenece import logging import pymel.core as pm @@ -45,7 +39,7 @@ def load(self, context, name, namespace, data): self.log.info(f"assets: {assets}") current_asset = [asset for asset in assets if asset['asset_name'] in anim_file] if not current_asset: - self.log.warning(f"Asset not found in version data.") + self.log.warning("Asset not found in version data.") return current_asset = current_asset[0] asset_data = ayon_api.get_folder_by_name(project_name=project_name, folder_name=current_asset['asset_name']) @@ -60,7 +54,7 @@ def load(self, context, name, namespace, data): if rep['name'] == 'ma': rep_id = rep['id'] break - context = get_representation_context("PRPT", rep_id) + context = get_representation_context(project_name, rep_id) options = {'attach_to_root': True} _plugin = plugins.get(LOADER_PLUGINS["reference"])() _plugin.load(context=context, name=context['product']['name'], namespace=name_space, options=options) @@ -98,6 +92,7 @@ def read_anim(filepath='C:/temp/anim.anim', objects=pm.selected(), namespace=Non try: cur_attr = getattr(obj, attrs) except AttributeError as e: + logger.warning(e) logger.warning('skipping for {0} as attribute {1} was not found'.format(obj_shot_name, attrs)) continue key_type = ctrl_value[attrs]['type'] @@ -124,7 +119,7 @@ def read_anim(filepath='C:/temp/anim.anim', objects=pm.selected(), namespace=Non tan2 = json.loads(keys.get('outAngle')) weight1 = json.loads(keys.get('inWeight')) weight2 = json.loads(keys.get('outWeight')) - res = pm.setKeyframe(cur_attr, time=time, value=value, bd=breakdown) + pm.setKeyframe(cur_attr, time=time, value=value, bd=breakdown) if weighted_tangents: pm.keyTangent(cur_attr, weightedTangents=True, edit=True) try: diff --git a/client/ayon_maya/plugins/publish/collect_anim_curve.py b/client/ayon_maya/plugins/publish/export_anim_curve.py similarity index 100% rename from client/ayon_maya/plugins/publish/collect_anim_curve.py rename to client/ayon_maya/plugins/publish/export_anim_curve.py From b877f68e155685587ac997e822a8320d0fc57dd8 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Tue, 6 May 2025 13:29:09 +0530 Subject: [PATCH 03/10] updated anim export and imp --- client/ayon_maya/plugins/publish/export_anim_curve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ayon_maya/plugins/publish/export_anim_curve.py b/client/ayon_maya/plugins/publish/export_anim_curve.py index fe6aac35..3231ca1f 100644 --- a/client/ayon_maya/plugins/publish/export_anim_curve.py +++ b/client/ayon_maya/plugins/publish/export_anim_curve.py @@ -12,7 +12,7 @@ class ExtractAnimCrv(plugin.MayaExtractorPlugin): order = ExtractorOrder - label = "Extract Animation curves (TEST)" + label = "Extract Animation curves" families = ["animation"] hosts = ["maya"] From 31fa361a36a44b4cc2e1a83f4889fdf03cc98e93 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Wed, 14 May 2025 15:04:24 +0530 Subject: [PATCH 04/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 21 ++++++++----------- .../plugins/publish/export_anim_curve.py | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index b069de0f..fcd78eea 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -3,18 +3,14 @@ import maya.cmds as cmds import ayon_api from ayon_maya.api import plugin -from ayon_core.pipeline.load.plugins import discover_loader_plugins from ayon_core.pipeline.load.utils import get_representation_context +from ayon_maya.plugins.load.load_reference import ReferenceLoader import logging import pymel.core as pm logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) -LOADER_PLUGINS = { - "reference": "ReferenceLoader" -} - class AnimLoader(plugin.Loader): """Load anim on character""" @@ -31,7 +27,6 @@ def load(self, context, name, namespace, data): project_name = context['project']['name'] anim_file = context['representation']['attrib']['path'] self.log.info(f"anim_file: {anim_file}") - plugins = {i.__name__: i for i in discover_loader_plugins(project_name)} name_space = context['representation']['data']['context']['product']['name'].replace( context['representation']['data']['context']['product']['type'], '') if not cmds.namespace(exists=name_space): @@ -39,7 +34,7 @@ def load(self, context, name, namespace, data): self.log.info(f"assets: {assets}") current_asset = [asset for asset in assets if asset['asset_name'] in anim_file] if not current_asset: - self.log.warning("Asset not found in version data.") + self.log.warning(f"Asset not found in version data.") return current_asset = current_asset[0] asset_data = ayon_api.get_folder_by_name(project_name=project_name, folder_name=current_asset['asset_name']) @@ -55,9 +50,10 @@ def load(self, context, name, namespace, data): rep_id = rep['id'] break context = get_representation_context(project_name, rep_id) - options = {'attach_to_root': True} - _plugin = plugins.get(LOADER_PLUGINS["reference"])() - _plugin.load(context=context, name=context['product']['name'], namespace=name_space, options=options) + options = {'attach_to_root': True, "group_name": f"{name_space}:_GRP"} + _plugin = ReferenceLoader() + _plugin.process_reference(context=context, name=context['product']['name'], + namespace=name_space, options=options) ctrl_set = pm.ls(name_space + ":rigMain_controls_SET") if not ctrl_set: self.log.warning("No control set found in instance data") @@ -67,8 +63,9 @@ def load(self, context, name, namespace, data): self.log.warning("No controls found in instance data") return self.log.debug(f"ctrls: {ctrls}") - self.log.info(f"namespace: {namespace}") - read_anim(filepath=anim_file, objects=ctrls, namespace=name_space) + self.log.debug(f"namespace: {namespace}") + self.log.debug(f"anim_file: {anim_file}") + read_anim(filepath=anim_file, objects=ctrls) def read_anim(filepath='C:/temp/anim.anim', objects=pm.selected(), namespace=None): diff --git a/client/ayon_maya/plugins/publish/export_anim_curve.py b/client/ayon_maya/plugins/publish/export_anim_curve.py index 3231ca1f..8191d38f 100644 --- a/client/ayon_maya/plugins/publish/export_anim_curve.py +++ b/client/ayon_maya/plugins/publish/export_anim_curve.py @@ -30,7 +30,7 @@ def process(self, instance): name_space = instance_data['variant'] reference_node = [x for x in pm.listReferences() if x.namespace == name_space][0] self.log.info(f"controls: {controls}") - self.write_anim(objects=ctrls, filepath=os.path.realpath(out_path), namespace=name_space) + self.write_anim(objects=ctrls, filepath=os.path.realpath(out_path)) if "representations" not in instance.data: instance.data["representations"] = [] representation = { From 68559c2a0bc6418df2b02f2eccc9ec9e6db6123c Mon Sep 17 00:00:00 2001 From: pavithraj Date: Mon, 19 May 2025 15:12:40 +0530 Subject: [PATCH 05/10] updated anim export and imp --- client/ayon_maya/api/plugin.py | 3 ++- client/ayon_maya/plugins/load/load_anim.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/ayon_maya/api/plugin.py b/client/ayon_maya/api/plugin.py index cc5fae16..641e321c 100644 --- a/client/ayon_maya/api/plugin.py +++ b/client/ayon_maya/api/plugin.py @@ -789,7 +789,8 @@ def load( loaded_containers = [] for c in range(0, count): - namespace = lib.get_custom_namespace(custom_namespace) + if not namespace: + namespace = lib.get_custom_namespace(custom_namespace) group_name = "{}:{}".format( namespace, custom_group_name diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index fcd78eea..dce88047 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -52,7 +52,7 @@ def load(self, context, name, namespace, data): context = get_representation_context(project_name, rep_id) options = {'attach_to_root': True, "group_name": f"{name_space}:_GRP"} _plugin = ReferenceLoader() - _plugin.process_reference(context=context, name=context['product']['name'], + _plugin.load(context=context, name=context['product']['name'], namespace=name_space, options=options) ctrl_set = pm.ls(name_space + ":rigMain_controls_SET") if not ctrl_set: From 2c2b9d46846c58d207355a643b03f86e8aa5b689 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Mon, 19 May 2025 15:32:00 +0530 Subject: [PATCH 06/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index dce88047..c5a9a8fc 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -50,10 +50,10 @@ def load(self, context, name, namespace, data): rep_id = rep['id'] break context = get_representation_context(project_name, rep_id) - options = {'attach_to_root': True, "group_name": f"{name_space}:_GRP"} + data['attach_to_root'] = True _plugin = ReferenceLoader() _plugin.load(context=context, name=context['product']['name'], - namespace=name_space, options=options) + namespace=name_space, options=data) ctrl_set = pm.ls(name_space + ":rigMain_controls_SET") if not ctrl_set: self.log.warning("No control set found in instance data") From 08cb7a0f0cdc53ace6fb30b8e4dd404c3f78ec73 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Thu, 5 Jun 2025 11:44:53 +0530 Subject: [PATCH 07/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 64 ++++++++----------- .../plugins/publish/export_anim_curve.py | 40 ++++++++---- 2 files changed, 53 insertions(+), 51 deletions(-) diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index c5a9a8fc..cfd4c5b2 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -1,12 +1,15 @@ import os import json +import logging + import maya.cmds as cmds -import ayon_api +import pymel.core as pm + from ayon_maya.api import plugin from ayon_core.pipeline.load.utils import get_representation_context -from ayon_maya.plugins.load.load_reference import ReferenceLoader -import logging -import pymel.core as pm +from ayon_core.pipeline.load import get_loaders_by_name +# from ayon_maya.plugins.load.load_reference import ReferenceLoader +from ayon_maya.api import lib logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) @@ -25,36 +28,26 @@ class AnimLoader(plugin.Loader): def load(self, context, name, namespace, data): project_name = context['project']['name'] - anim_file = context['representation']['attrib']['path'] - self.log.info(f"anim_file: {anim_file}") - name_space = context['representation']['data']['context']['product']['name'].replace( - context['representation']['data']['context']['product']['type'], '') - if not cmds.namespace(exists=name_space): - assets = context['version']['data'].get("assets", []) - self.log.info(f"assets: {assets}") - current_asset = [asset for asset in assets if asset['asset_name'] in anim_file] - if not current_asset: - self.log.warning(f"Asset not found in version data.") - return - current_asset = current_asset[0] - asset_data = ayon_api.get_folder_by_name(project_name=project_name, folder_name=current_asset['asset_name']) - versions = ayon_api.get_last_version_by_product_name(project_name, "rigMain", asset_data['id']) - representations = ayon_api.get_representations( - project_name=project_name, - version_ids={versions['id']}, - fields={"id", "name", "files.path"} - ) - rep_id = None - for rep in representations: - if rep['name'] == 'ma': - rep_id = rep['id'] - break - context = get_representation_context(project_name, rep_id) + folder_name = context["folder"]["name"] + anim_file = self.filepath_from_context(context) + assets = context['version']['data'].get("assets", []) + current_asset = [asset for asset in assets if asset['namespace'] in anim_file] + if not current_asset: + self.log.warning(f"Asset not found in version data for animation file: {anim_file}") + return + current_asset = current_asset[0] + # check if the asset is already loaded + if not cmds.namespace(exists=current_asset['namespace']): + self.log.info(f"Asset namespace {current_asset['namespace']} does not exist, loading asset.") + asset_context = get_representation_context(project_name, current_asset['representation_id']) + self.log.info(f"asset_context: {asset_context}") data['attach_to_root'] = True - _plugin = ReferenceLoader() - _plugin.load(context=context, name=context['product']['name'], - namespace=name_space, options=data) - ctrl_set = pm.ls(name_space + ":rigMain_controls_SET") + loader_classes = get_loaders_by_name() + reference_loader = loader_classes.get('ReferenceLoader')() + reference_loader.load(context=asset_context, name=asset_context['product']['name'], + namespace=current_asset['namespace'], options=data) + + ctrl_set = pm.ls(f"{current_asset['namespace']}:{current_asset['product_name']}_controls_SET") if not ctrl_set: self.log.warning("No control set found in instance data") return @@ -62,13 +55,10 @@ def load(self, context, name, namespace, data): if not ctrls: self.log.warning("No controls found in instance data") return - self.log.debug(f"ctrls: {ctrls}") - self.log.debug(f"namespace: {namespace}") - self.log.debug(f"anim_file: {anim_file}") read_anim(filepath=anim_file, objects=ctrls) -def read_anim(filepath='C:/temp/anim.anim', objects=pm.selected(), namespace=None): +def read_anim(filepath, objects, namespace=None): if not os.path.exists(filepath): return [False, 'invalid filepath'] with open(filepath, "r", encoding='utf-8') as reader: diff --git a/client/ayon_maya/plugins/publish/export_anim_curve.py b/client/ayon_maya/plugins/publish/export_anim_curve.py index 8191d38f..76b06dea 100644 --- a/client/ayon_maya/plugins/publish/export_anim_curve.py +++ b/client/ayon_maya/plugins/publish/export_anim_curve.py @@ -2,15 +2,16 @@ import json import logging import pymel.core as pm +import maya.cmds as cmds logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) -from ayon_maya.api import plugin +from ayon_maya.api import plugin, pipeline from pyblish.api import ExtractorOrder -class ExtractAnimCrv(plugin.MayaExtractorPlugin): +class ExtractAnimCurve(plugin.MayaExtractorPlugin): order = ExtractorOrder label = "Extract Animation curves" families = ["animation"] @@ -18,11 +19,13 @@ class ExtractAnimCrv(plugin.MayaExtractorPlugin): def process(self, instance): instance_data = instance.data + self.log.info(f"instance_data: {instance_data}") + # Define output path staging_dir = self.staging_dir(instance) - filename = "{0}.anim".format(instance_data['variant']) + filename = "{0}.anim".format(instance.data['variant']) out_path = os.path.join(staging_dir, filename) - controls = [x for x in instance_data['setMembers'] if x.endswith(":rigMain_controls_SET")] + controls = [x for x in instance.data['setMembers'] if x.endswith("controls_SET")] if not controls: self.log.warning("No controls found in instance data") return @@ -34,7 +37,8 @@ def process(self, instance): if "representations" not in instance.data: instance.data["representations"] = [] representation = { - 'name': 'anim', 'ext': 'anim', + 'name': 'anim', + 'ext': 'anim', 'files': os.path.basename(out_path), 'stagingDir': staging_dir.replace("\\", "/") } @@ -42,19 +46,28 @@ def process(self, instance): assets = version_data.get("assets", []) if not assets: version_data["assets"] = [] - asset_data = { - "namespace": name_space, - "path": reference_node.path.__str__(), - "asset_name": name_space.split("_rigMain_")[0], - } + asset_data = self.get_asset_data(instance) version_data["assets"].append(asset_data) - instance_data["data"] = [name_space + ':' + reference_node.path.__str__()] - instance.data["assets"] = [name_space + ':' + reference_node.path.__str__()] instance.data["versionData"] = version_data self.log.info(f"representation: {representation}") instance.data["representations"].append(representation) - def write_anim(self, objects=pm.selected(), filepath='C:/temp/anim.anim', namespace=None): + @staticmethod + def get_asset_data(instance): + members = [member.lstrip('|') for member in instance.data['setMembers']] + grp_name = members[0].split(':')[0] + containers = cmds.ls("{}*_CON".format(grp_name)) + rep_id = cmds.getAttr(containers[0] + '.representation') + name_space = cmds.getAttr(containers[0] + '.namespace') + product_name = cmds.getAttr(containers[0] + '.name') + asset_data = { + "namespace": name_space, + "product_name": product_name, + "representation_id": rep_id + } + return asset_data + + def write_anim(self, objects, filepath, namespace=None): self.log.info(f"objects: {objects}") self.log.info(f"Writing animation curves to {filepath}") self.log.info(f"namespace: {namespace}") @@ -130,7 +143,6 @@ def write_anim(self, objects=pm.selected(), filepath='C:/temp/anim.anim', namesp if not len(static_name) > 1: continue static_name = static_name[1] - # static_name = static_chan.name().split(obj_shot_name + '.')[1] if static_name not in channel_dict: channel_dict[static_name] = {'type': 'static'} channel_dict[static_name]['value'] = static_chan.get() From 42ead19e8c39edee04a69c77ae830b555b7b4d2e Mon Sep 17 00:00:00 2001 From: pavithraj Date: Thu, 5 Jun 2025 11:49:58 +0530 Subject: [PATCH 08/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 8 +------- client/ayon_maya/plugins/publish/export_anim_curve.py | 4 ---- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index cfd4c5b2..bbc343ed 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -1,6 +1,5 @@ import os import json -import logging import maya.cmds as cmds import pymel.core as pm @@ -8,12 +7,8 @@ from ayon_maya.api import plugin from ayon_core.pipeline.load.utils import get_representation_context from ayon_core.pipeline.load import get_loaders_by_name -# from ayon_maya.plugins.load.load_reference import ReferenceLoader from ayon_maya.api import lib -logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger(__name__) - class AnimLoader(plugin.Loader): """Load anim on character""" @@ -28,7 +23,6 @@ class AnimLoader(plugin.Loader): def load(self, context, name, namespace, data): project_name = context['project']['name'] - folder_name = context["folder"]["name"] anim_file = self.filepath_from_context(context) assets = context['version']['data'].get("assets", []) current_asset = [asset for asset in assets if asset['namespace'] in anim_file] @@ -45,7 +39,7 @@ def load(self, context, name, namespace, data): loader_classes = get_loaders_by_name() reference_loader = loader_classes.get('ReferenceLoader')() reference_loader.load(context=asset_context, name=asset_context['product']['name'], - namespace=current_asset['namespace'], options=data) + namespace=current_asset['namespace'], options=data) ctrl_set = pm.ls(f"{current_asset['namespace']}:{current_asset['product_name']}_controls_SET") if not ctrl_set: diff --git a/client/ayon_maya/plugins/publish/export_anim_curve.py b/client/ayon_maya/plugins/publish/export_anim_curve.py index 76b06dea..93d5b0b4 100644 --- a/client/ayon_maya/plugins/publish/export_anim_curve.py +++ b/client/ayon_maya/plugins/publish/export_anim_curve.py @@ -1,12 +1,8 @@ import os import json -import logging import pymel.core as pm import maya.cmds as cmds -logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger(__name__) - from ayon_maya.api import plugin, pipeline from pyblish.api import ExtractorOrder From e8f098aa9f988bf98b0db4cf63be54e268476485 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Thu, 5 Jun 2025 11:52:58 +0530 Subject: [PATCH 09/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 134 +++++++++--------- .../plugins/publish/export_anim_curve.py | 6 - 2 files changed, 67 insertions(+), 73 deletions(-) diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index bbc343ed..18ecd149 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -49,75 +49,75 @@ def load(self, context, name, namespace, data): if not ctrls: self.log.warning("No controls found in instance data") return - read_anim(filepath=anim_file, objects=ctrls) + self.read_anim(filepath=anim_file, objects=ctrls) -def read_anim(filepath, objects, namespace=None): - if not os.path.exists(filepath): - return [False, 'invalid filepath'] - with open(filepath, "r", encoding='utf-8') as reader: - anim_data = json.loads(reader.read()) - for j, obj in enumerate(objects): - obj_shot_name = obj.name() - obj_longname = obj.longName() - if namespace: - obj_longname = obj_longname.replace(namespace, '{namespace}') - ctrl_value = anim_data.get(obj_longname, []) - if not ctrl_value: - continue - for attrs in ctrl_value: - if not hasattr(obj, attrs): + def read_anim(self, filepath, objects, namespace=None): + if not os.path.exists(filepath): + return [False, 'invalid filepath'] + with open(filepath, "r", encoding='utf-8') as reader: + anim_data = json.loads(reader.read()) + for j, obj in enumerate(objects): + obj_shot_name = obj.name() + obj_longname = obj.longName() + if namespace: + obj_longname = obj_longname.replace(namespace, '{namespace}') + ctrl_value = anim_data.get(obj_longname, []) + if not ctrl_value: continue - if attrs in ['lock']: - continue - try: - cur_attr = getattr(obj, attrs) - except AttributeError as e: - logger.warning(e) - logger.warning('skipping for {0} as attribute {1} was not found'.format(obj_shot_name, attrs)) - continue - key_type = ctrl_value[attrs]['type'] - if key_type == 'static': - key_value = ctrl_value[attrs]['value'] - connected = pm.listConnections(cur_attr, destination=False, source=True) - if not connected and not cur_attr.isLocked(): - cur_attr.set(key_value) - if key_type == 'keyed': - key_values = ctrl_value[attrs]['keys'] - infinity_data = ctrl_value[attrs]['infinity'] - pre_infinity = json.loads(infinity_data.get('preInfinity')) - post_infinity = json.loads(infinity_data.get('postInfinity')) - weighted_tangents = json.loads(infinity_data.get('weightedTangents')) - for keys in key_values: - time = json.loads(keys.get('key')) - value = json.loads(keys.get('value')) - breakdown = json.loads(keys.get('breakdown')) - tan_lock = json.loads(keys.get('lock')) - weight_lock = json.loads(keys.get('weightLock')) - in_type = json.loads(keys.get('inTangentType')) - out_type = json.loads(keys.get('outTangentType')) - tan1 = json.loads(keys.get('inAngle')) - tan2 = json.loads(keys.get('outAngle')) - weight1 = json.loads(keys.get('inWeight')) - weight2 = json.loads(keys.get('outWeight')) - pm.setKeyframe(cur_attr, time=time, value=value, bd=breakdown) - if weighted_tangents: - pm.keyTangent(cur_attr, weightedTangents=True, edit=True) - try: - pm.keyTangent(cur_attr, lock=tan_lock, time=time) - except Exception as e: - logger.warning(e) + for attrs in ctrl_value: + if not hasattr(obj, attrs): + continue + if attrs in ['lock']: + continue + try: + cur_attr = getattr(obj, attrs) + except AttributeError as e: + self.log.warning(e) + self.log.warning('skipping for {0} as attribute {1} was not found'.format(obj_shot_name, attrs)) + continue + key_type = ctrl_value[attrs]['type'] + if key_type == 'static': + key_value = ctrl_value[attrs]['value'] + connected = pm.listConnections(cur_attr, destination=False, source=True) + if not connected and not cur_attr.isLocked(): + cur_attr.set(key_value) + if key_type == 'keyed': + key_values = ctrl_value[attrs]['keys'] + infinity_data = ctrl_value[attrs]['infinity'] + pre_infinity = json.loads(infinity_data.get('preInfinity')) + post_infinity = json.loads(infinity_data.get('postInfinity')) + weighted_tangents = json.loads(infinity_data.get('weightedTangents')) + for keys in key_values: + time = json.loads(keys.get('key')) + value = json.loads(keys.get('value')) + breakdown = json.loads(keys.get('breakdown')) + tan_lock = json.loads(keys.get('lock')) + weight_lock = json.loads(keys.get('weightLock')) + in_type = json.loads(keys.get('inTangentType')) + out_type = json.loads(keys.get('outTangentType')) + tan1 = json.loads(keys.get('inAngle')) + tan2 = json.loads(keys.get('outAngle')) + weight1 = json.loads(keys.get('inWeight')) + weight2 = json.loads(keys.get('outWeight')) + pm.setKeyframe(cur_attr, time=time, value=value, bd=breakdown) + if weighted_tangents: + pm.keyTangent(cur_attr, weightedTangents=True, edit=True) + try: + pm.keyTangent(cur_attr, lock=tan_lock, time=time) + except Exception as e: + self.log.warning(e) - if weighted_tangents: - pm.keyTangent(cur_attr, time=time, weightLock=weight_lock) - if in_type != 'fixed' and out_type != 'fixed': - pm.keyTangent(cur_attr, e=1, a=1, time=time, itt=in_type, ott=out_type) - if in_type == 'fixed' and out_type != 'fixed': - pm.keyTangent(cur_attr, e=1, a=1, time=time, inAngle=tan1, inWeight=weight1, itt=in_type, - ott=out_type) - if in_type == 'fixed' and out_type == 'fixed': - pm.keyTangent(cur_attr, e=1, a=1, time=time, inAngle=tan1, inWeight=weight1, outAngle=tan2, - outWeight=weight2, itt=in_type, ott=out_type) + if weighted_tangents: + pm.keyTangent(cur_attr, time=time, weightLock=weight_lock) + if in_type != 'fixed' and out_type != 'fixed': + pm.keyTangent(cur_attr, e=1, a=1, time=time, itt=in_type, ott=out_type) + if in_type == 'fixed' and out_type != 'fixed': + pm.keyTangent(cur_attr, e=1, a=1, time=time, inAngle=tan1, inWeight=weight1, itt=in_type, + ott=out_type) + if in_type == 'fixed' and out_type == 'fixed': + pm.keyTangent(cur_attr, e=1, a=1, time=time, inAngle=tan1, inWeight=weight1, outAngle=tan2, + outWeight=weight2, itt=in_type, ott=out_type) - pm.setInfinity(cur_attr, poi=post_infinity, pri=pre_infinity) - return None + pm.setInfinity(cur_attr, poi=post_infinity, pri=pre_infinity) + return None diff --git a/client/ayon_maya/plugins/publish/export_anim_curve.py b/client/ayon_maya/plugins/publish/export_anim_curve.py index 93d5b0b4..062f1078 100644 --- a/client/ayon_maya/plugins/publish/export_anim_curve.py +++ b/client/ayon_maya/plugins/publish/export_anim_curve.py @@ -14,10 +14,6 @@ class ExtractAnimCurve(plugin.MayaExtractorPlugin): hosts = ["maya"] def process(self, instance): - instance_data = instance.data - self.log.info(f"instance_data: {instance_data}") - - # Define output path staging_dir = self.staging_dir(instance) filename = "{0}.anim".format(instance.data['variant']) out_path = os.path.join(staging_dir, filename) @@ -26,8 +22,6 @@ def process(self, instance): self.log.warning("No controls found in instance data") return ctrls = pm.listConnections(controls[0], source=1, type='transform') - name_space = instance_data['variant'] - reference_node = [x for x in pm.listReferences() if x.namespace == name_space][0] self.log.info(f"controls: {controls}") self.write_anim(objects=ctrls, filepath=os.path.realpath(out_path)) if "representations" not in instance.data: From 68506f793c5b5b6d24309dc085446237bb056748 Mon Sep 17 00:00:00 2001 From: pavithraj Date: Thu, 5 Jun 2025 14:05:34 +0530 Subject: [PATCH 10/10] updated anim export and imp --- client/ayon_maya/plugins/load/load_anim.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/client/ayon_maya/plugins/load/load_anim.py b/client/ayon_maya/plugins/load/load_anim.py index 18ecd149..5e385c06 100644 --- a/client/ayon_maya/plugins/load/load_anim.py +++ b/client/ayon_maya/plugins/load/load_anim.py @@ -30,18 +30,16 @@ def load(self, context, name, namespace, data): self.log.warning(f"Asset not found in version data for animation file: {anim_file}") return current_asset = current_asset[0] + asset_context = get_representation_context(project_name, current_asset['representation_id']) # check if the asset is already loaded if not cmds.namespace(exists=current_asset['namespace']): self.log.info(f"Asset namespace {current_asset['namespace']} does not exist, loading asset.") - asset_context = get_representation_context(project_name, current_asset['representation_id']) - self.log.info(f"asset_context: {asset_context}") data['attach_to_root'] = True loader_classes = get_loaders_by_name() reference_loader = loader_classes.get('ReferenceLoader')() reference_loader.load(context=asset_context, name=asset_context['product']['name'], namespace=current_asset['namespace'], options=data) - - ctrl_set = pm.ls(f"{current_asset['namespace']}:{current_asset['product_name']}_controls_SET") + ctrl_set = pm.ls(f"{current_asset['namespace']}:{asset_context['product']['name']}_controls_SET") if not ctrl_set: self.log.warning("No control set found in instance data") return