From f73f2541810010c987b976f5333771fed65ac71b Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:46:47 +0200 Subject: [PATCH 01/58] added new creator class for setdress --- .../maya/create/colorbleed_setdress.py | 9 ++++++ colorbleed/plugins/maya/load/load_setdress.py | 32 +++++++++++++++++++ .../maya/load/load_setdress_rebuild.py | 0 .../plugins/maya/publish/collect_setdress.py | 0 .../plugins/maya/publish/extract_setdress.py | 0 5 files changed, 41 insertions(+) create mode 100644 colorbleed/plugins/maya/create/colorbleed_setdress.py create mode 100644 colorbleed/plugins/maya/load/load_setdress.py create mode 100644 colorbleed/plugins/maya/load/load_setdress_rebuild.py create mode 100644 colorbleed/plugins/maya/publish/collect_setdress.py create mode 100644 colorbleed/plugins/maya/publish/extract_setdress.py diff --git a/colorbleed/plugins/maya/create/colorbleed_setdress.py b/colorbleed/plugins/maya/create/colorbleed_setdress.py new file mode 100644 index 0000000000..fe0c64dd40 --- /dev/null +++ b/colorbleed/plugins/maya/create/colorbleed_setdress.py @@ -0,0 +1,9 @@ +import avalon.maya + + +class CreateSetDress(avalon.maya.Creator): + """THe animated objects in the scene""" + + name = "setdress" + label = "Set Dress" + family = "colorbleed.setdress" diff --git a/colorbleed/plugins/maya/load/load_setdress.py b/colorbleed/plugins/maya/load/load_setdress.py new file mode 100644 index 0000000000..aaf6834f2f --- /dev/null +++ b/colorbleed/plugins/maya/load/load_setdress.py @@ -0,0 +1,32 @@ +from avalon import api + + +class SetDressAlembicLoader(api.Loader): + """Load the setdress as alembic""" + + families = ["colorbleed.setdress"] + representations = ["abc"] + + label = "Load Alembic" + order = -10 + icon = "code-fork" + color = "orange" + + def process(self, name, namespace, context, data): + + import maya.cmds as cmds + from avalon import maya + + namespace = maya.unique_namespace("{}_".format(name), + format="%03d", + suffix="_abc") + + with maya.maintained_selection(): + nodes = cmds.file(self.fname, + namespace=namespace, + reference=True, + returnNewNodes=True, + groupReference=True, + groupName="{}:{}".format(namespace, name)) + + self[:] = nodes diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/colorbleed/plugins/maya/publish/extract_setdress.py b/colorbleed/plugins/maya/publish/extract_setdress.py new file mode 100644 index 0000000000..e69de29bb2 From 57467b6ea681a3840b70ee396e85989970cc1d9a Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:47:34 +0200 Subject: [PATCH 02/58] loader to recreate setdress in its entirely --- .../maya/load/load_setdress_rebuild.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index e69de29bb2..852410b502 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -0,0 +1,46 @@ +import pprint + +from avalon import api, io, pipeline +from avalon.tools.cbloader import lib + + +class SetDressRebuild(api.Loader): + + families = ["colorbleed.setdress"] + representations = ["abc"] + + label = "Rebuild Set Dress" + order = -9 + icon = "code-fork" + color = "orange" + + def process(self, name, namespace, context, data): + + import json + # from maya import cmds + + print ">>>", lib.__file__ + + # Ensure + data_file = self.fname.replace(".abc", ".json") + with open(data_file, "r") as fp: + build_data = json.load(fp) + + pprint.pprint(build_data) + for _id, instances in build_data.items(): + # Rebuild filename + for inst in instances: + nodes = self.run_loader(_id) + # cmds.xform(nodes, matrix=inst["matrix"]) + + def run_loader(self, _id): + # get all registered plugins + obj_id = io.ObjectId(_id) + loader_inst = lib.iter_loaders(obj_id) + if loader_inst is None: + raise RuntimeError("Could not find matching loader") + + # strip the generator layer from the found loader + loader = list(loader_inst)[0] + context = lib.get_representation_context(obj_id) + loader.process(**context) From c8cd19957f2de4a395edc129c450c9c3482e54ef Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:47:59 +0200 Subject: [PATCH 03/58] loader for abc reference of setdress --- colorbleed/plugins/maya/load/load_setdress.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/load/load_setdress.py b/colorbleed/plugins/maya/load/load_setdress.py index aaf6834f2f..2ad1a58339 100644 --- a/colorbleed/plugins/maya/load/load_setdress.py +++ b/colorbleed/plugins/maya/load/load_setdress.py @@ -1,4 +1,4 @@ -from avalon import api +from avalon import api, io class SetDressAlembicLoader(api.Loader): From 7969169db42d82f23686545012f95ad227442312 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:48:16 +0200 Subject: [PATCH 04/58] added setdress to integration --- colorbleed/plugins/publish/integrate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/colorbleed/plugins/publish/integrate.py b/colorbleed/plugins/publish/integrate.py index 2c96d69556..43b29edc63 100644 --- a/colorbleed/plugins/publish/integrate.py +++ b/colorbleed/plugins/publish/integrate.py @@ -30,6 +30,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "colorbleed.mayaAscii", "colorbleed.model", "colorbleed.pointcache", + "colorbleed.setdress", "colorbleed.rig"] def process(self, instance): From ced6eaa1563b00c38ee581b10a06eb57af1e8b7b Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:48:34 +0200 Subject: [PATCH 05/58] added collector for setdress --- .../plugins/maya/publish/collect_setdress.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index e69de29bb2..c3865726b3 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -0,0 +1,64 @@ +from collections import defaultdict +import pyblish.api + +from maya import cmds, mel +from avalon import maya as amaya + + +class CollectSetDress(pyblish.api.InstancePlugin): + """Collect all relevant setdress items + + Collected data: + + * File name + * Compatible loader + * Matrix per instance + * Namespace + """ + + order = pyblish.api.CollectorOrder + 0.49 + label = "Set Dress" + families = ["colorbleed.setdress"] + + def process(self, instance): + + # Find containers + containers = amaya.ls() + + # Get all content from the instance + instance_lookup = set(cmds.ls(instance, type="transform", long=True)) + alembic_data = defaultdict(list) + + for container in containers: + + members = cmds.sets(container["objectName"], query=True) + transforms = cmds.ls(members, type="transform", long=True) + if not transforms: + self.log.warning("Container is invalid, missing transform:" + "%s", container["objectName"]) + continue + if len(transforms) > 1: + self.log.warning("Container is invalid, more than one " + "transform: %s", container['objectName']) + continue + + root = transforms[0] + if root not in instance_lookup: + continue + + representation_id = container["representation"] + matrix = cmds.xform(root, query=True, matrix=True) + + # Gather info for new data entry + reference_node = cmds.ls(members, type="reference")[0] + namespace = cmds.referenceQuery(reference_node, namespace=True) + alembic_data[representation_id].append({ + "loader": container["loader"], + "matrix": matrix, + "namespace": namespace + }) + + instance.data["scenedata"] = dict(alembic_data) + + def get_file_rule(self, rule): + return mel.eval('workspace -query -fileRuleEntry "{}"'.format(rule)) From c3fca3a4004f722f16243b8e7131337db028c7ee Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:49:21 +0200 Subject: [PATCH 06/58] cosmetics --- colorbleed/plugins/maya/load/load_model.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/load/load_model.py b/colorbleed/plugins/maya/load/load_model.py index 15637c9e81..7df8a0421c 100644 --- a/colorbleed/plugins/maya/load/load_model.py +++ b/colorbleed/plugins/maya/load/load_model.py @@ -17,6 +17,9 @@ class ModelLoader(api.Loader): import maya.cmds as cmds from avalon import maya + # Ensure Alembic is loaded + cmds.loadPlugin("AbcImport", quiet=True) + # Create a readable namespace # Namespace should contain asset name and counter # TEST_001{_descriptor} where `descriptor` can be `_abc` for example @@ -31,4 +34,4 @@ class ModelLoader(api.Loader): groupReference=True, groupName="{}:{}".format(namespace, name)) - self[:] = nodes \ No newline at end of file + self[:] = nodes From 70571e6610116c25822a40a2a87564703def7fd6 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:50:10 +0200 Subject: [PATCH 07/58] added default value for start and endframe if dict has no value --- colorbleed/plugins/maya/publish/extract_pointcache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/maya/publish/extract_pointcache.py b/colorbleed/plugins/maya/publish/extract_pointcache.py index bd80029010..727649ccf5 100644 --- a/colorbleed/plugins/maya/publish/extract_pointcache.py +++ b/colorbleed/plugins/maya/publish/extract_pointcache.py @@ -25,8 +25,8 @@ class ExtractColorbleedAlembic(colorbleed.api.Extractor): nodes = instance[:] # Collect the start and end including handles - start = instance.data["startFrame"] - end = instance.data["endFrame"] + start = instance.data.get("startFrame", 1) + end = instance.data.get("endFrame", 1) handles = instance.data.get("handles", 0) if handles: start -= handles From d333caaf5809c37c88afc94c2685d8923e3fcaef Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 9 Oct 2017 14:50:37 +0200 Subject: [PATCH 08/58] added setdress extractor --- .../plugins/maya/publish/extract_setdress.py | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/colorbleed/plugins/maya/publish/extract_setdress.py b/colorbleed/plugins/maya/publish/extract_setdress.py index e69de29bb2..5e1d13abef 100644 --- a/colorbleed/plugins/maya/publish/extract_setdress.py +++ b/colorbleed/plugins/maya/publish/extract_setdress.py @@ -0,0 +1,54 @@ +import json + +import os + +import colorbleed.api +from colorbleed.maya.lib import extract_alembic + +from maya import cmds + + +class ExtractSetDress(colorbleed.api.Extractor): + """Produce an alembic of just point positions and normals. + + Positions and normals are preserved, but nothing more, + for plain and predictable point caches. + + """ + + label = "Extract Set Dress" + hosts = ["maya"] + families = ["colorbleed.setdress"] + + def process(self, instance): + + # Dump json + self.log.info("Dumping scene data for debugging ..") + + data = instance.data + + self.log.info("Extracting point cache") + + parent_dir = self.staging_dir(instance) + filename = "{}.abc".format(instance.name) + path = os.path.join(parent_dir, filename) + json_filename = "{}.json".format(instance.name) + json_path = os.path.join(parent_dir, json_filename) + + with open(json_path, "w") as fp: + json.dump(data["scenedata"], fp, ensure_ascii=False, indent=4) + + cmds.select(instance) + + # Run basic alembic exporter + extract_alembic(file=path, + startFrame=1.0, + endFrame=1.0, + **{"step": 1.0, + "attr": ["cbId"], + "writeVisibility": True, + "writeCreases": True, + "uvWrite": True, + "selection": True}) + + instance.data["files"] = [json_path, path] From 7e922275921a87a900ad3d5da654228b457ff08b Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 10:13:22 +0200 Subject: [PATCH 09/58] added assign look ui to menu --- colorbleed/maya/menu.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/colorbleed/maya/menu.json b/colorbleed/maya/menu.json index eb26bb0f08..e29c231c79 100644 --- a/colorbleed/maya/menu.json +++ b/colorbleed/maya/menu.json @@ -808,6 +808,20 @@ "title": "Set filename prefix", "tooltip": "Set the render file name prefix." }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\assign_look_ui.py", + "sourcetype": "file", + "tags": [ + "shading", + "lookdev", + "assign", + "shaders", + "auto" + ], + "title": "Assign Look UI", + "tooltip": "Open the Assign Look UI for custom look assignment" + }, { "type": "action", "command": "$COLORBLEED_SCRIPTS\\shading\\autoLookdevAssignment.py", From 606459f1d4850263517fb4e597eeb97d4bc6e68b Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 10:17:51 +0200 Subject: [PATCH 10/58] added matrix constant, added function for container transforms --- colorbleed/maya/lib.py | 93 ++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 968560b461..d752ca8e3e 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -13,6 +13,7 @@ from collections import OrderedDict, defaultdict from maya import cmds, mel from avalon import maya, io +from cb.utils.maya import core log = logging.getLogger(__name__) @@ -42,6 +43,42 @@ SHAPE_ATTRS = ["castsShadows", SHAPE_ATTRS = set(SHAPE_ATTRS) +DEFAULT_MATRIX = [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0] + +# The maya alembic export types +_alembic_options = { + "startFrame": float, + "endFrame": float, + "frameRange": str, # "start end"; overrides startFrame & endFrame + "eulerFilter": bool, + "frameRelativeSample": float, + "noNormals": bool, + "renderableOnly": bool, + "step": float, + "stripNamespaces": bool, + "uvWrite": bool, + "wholeFrameGeo": bool, + "worldSpace": bool, + "writeVisibility": bool, + "writeColorSets": bool, + "writeFaceSets": bool, + "writeCreases": bool, # Maya 2015 Ext1+ + "dataFormat": str, + "root": (list, tuple), + "attr": (list, tuple), + "attrPrefix": (list, tuple), + "userAttr": (list, tuple), + "melPerFrameCallback": str, + "melPostJobCallback": str, + "pythonPerFrameCallback": str, + "pythonPostJobCallback": str, + "selection": bool +} + + def unique(name): assert isinstance(name, basestring), "`name` must be string" @@ -362,37 +399,6 @@ def is_visible(node, return True -# The maya alembic export types -_alembic_options = { - "startFrame": float, - "endFrame": float, - "frameRange": str, # "start end"; overrides startFrame & endFrame - "eulerFilter": bool, - "frameRelativeSample": float, - "noNormals": bool, - "renderableOnly": bool, - "step": float, - "stripNamespaces": bool, - "uvWrite": bool, - "wholeFrameGeo": bool, - "worldSpace": bool, - "writeVisibility": bool, - "writeColorSets": bool, - "writeFaceSets": bool, - "writeCreases": bool, # Maya 2015 Ext1+ - "dataFormat": str, - "root": (list, tuple), - "attr": (list, tuple), - "attrPrefix": (list, tuple), - "userAttr": (list, tuple), - "melPerFrameCallback": str, - "melPostJobCallback": str, - "pythonPerFrameCallback": str, - "pythonPostJobCallback": str, - "selection": bool -} - - def extract_alembic(file, startFrame=None, endFrame=None, @@ -1017,3 +1023,28 @@ def get_related_sets(node): sets = [s for s in sets if s not in defaults] return sets + + +def get_container_transfroms(container, members=None, root=False): + """Retrieve the root node of the container content + + When a container is created through a Loader the content + of the file will be grouped under a transform. The name of the root + transform is stored in the container information + + Args: + container (dict): the container + members (list): optional and convenience argument + + Returns: + root (str): highest node in hierarchy + """ + + if not members: + members = cmds.sets(container["objectName"], query=True) + + results = cmds.ls(members, type="transform", long=True) + if root: + results = core.getHighestInHierarchy(results)[0] + + return results From fc0933f0827d3dbcc121df41e037195a08e93144 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 10:18:25 +0200 Subject: [PATCH 11/58] implemented matrix constant from lib --- .../maya/publish/validate_rig_controllers.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/colorbleed/plugins/maya/publish/validate_rig_controllers.py b/colorbleed/plugins/maya/publish/validate_rig_controllers.py index 465c6d3f7b..f504a0f47e 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_controllers.py +++ b/colorbleed/plugins/maya/publish/validate_rig_controllers.py @@ -4,6 +4,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.lib as lib log = logging.getLogger("Rig Controllers") @@ -78,13 +79,10 @@ class ValidateRigControllers(pyblish.api.InstancePlugin): @staticmethod def validate_transforms(control): tolerance = 1e-30 - identity = [1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0] matrix = cmds.xform(control, query=True, matrix=True, objectSpace=True) - if not all(abs(x - y) < tolerance for x, y in zip(identity, matrix)): + if not all(abs(x - y) < tolerance for x, y in zip(lib.DEFAULT_MATRIX, + matrix)): log.error("%s matrix : %s" % (control, matrix)) return False return True @@ -106,11 +104,6 @@ class ValidateRigControllers(pyblish.api.InstancePlugin): @classmethod def repair(cls, instance): - identity = [1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0] - # lock all controllers in controls_SET controls = cmds.sets("controls_SET", query=True) for control in controls: @@ -123,4 +116,6 @@ class ValidateRigControllers(pyblish.api.InstancePlugin): log.info("Repairing matrix") if not cls.validate_transforms(control): - cmds.xform(control, matrix=identity, objectSpace=True) + cmds.xform(control, + matrix=lib.DEFAULT_MATRIX, + objectSpace=True) From 9145371a2633e9eb325af493226521e845195b44 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 10:19:12 +0200 Subject: [PATCH 12/58] added collector for setdress --- .../plugins/maya/publish/collect_setdress.py | 55 +++++++++++++------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index c3865726b3..be3c3b9092 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -3,6 +3,7 @@ import pyblish.api from maya import cmds, mel from avalon import maya as amaya +from colorbleed.maya import lib class CollectSetDress(pyblish.api.InstancePlugin): @@ -27,38 +28,56 @@ class CollectSetDress(pyblish.api.InstancePlugin): # Get all content from the instance instance_lookup = set(cmds.ls(instance, type="transform", long=True)) - alembic_data = defaultdict(list) + data = defaultdict(list) for container in containers: - members = cmds.sets(container["objectName"], query=True) - transforms = cmds.ls(members, type="transform", long=True) - if not transforms: - self.log.warning("Container is invalid, missing transform:" - "%s", container["objectName"]) - continue - if len(transforms) > 1: - self.log.warning("Container is invalid, more than one " - "transform: %s", container['objectName']) - continue - - root = transforms[0] + transforms = lib.get_container_transfroms(container, members) + root = lib.get_container_transfroms(container, members, root=True) if root not in instance_lookup: continue representation_id = container["representation"] - matrix = cmds.xform(root, query=True, matrix=True) + shapes = [m for m in members if + cmds.objectType(m, isAType="shape") is True] + + look_ids = [self.get_look_id(shape) for shape in shapes] + + # Support for re-opened setdress scenes where the only connected + # transform node is the root node. This is due to how references + # are loaded in combination with the containers. + + matrix_data = self.get_matrix_data(transforms) # Gather info for new data entry reference_node = cmds.ls(members, type="reference")[0] namespace = cmds.referenceQuery(reference_node, namespace=True) - alembic_data[representation_id].append({ + data[representation_id].append({ "loader": container["loader"], - "matrix": matrix, - "namespace": namespace + "matrix": matrix_data, + "namespace": namespace, + "look_id": look_ids }) - instance.data["scenedata"] = dict(alembic_data) + instance.data["scenedata"] = dict(data) def get_file_rule(self, rule): return mel.eval('workspace -query -fileRuleEntry "{}"'.format(rule)) + + def get_matrix_data(self, members): + + matrix_data = [] + for member in members: + self.log.info(member) + matrix = cmds.xform(member, query=True, matrix=True) + matrix_data.append(matrix) + + return matrix_data + + def get_look_id(self, node): + """Get the look id of the assigned shader""" + shad_engine = cmds.listConnections(node, type="shadingEngine") + if not shad_engine or shad_engine == "initialShadingEngine": + return + + return cmds.getAttr("{}.cbId".format(shad_engine[0])).split(":")[0] From f53184c193a92af3ce759fb55707f76bd927feec Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 10:19:59 +0200 Subject: [PATCH 13/58] added setdress loader --- .../maya/load/load_setdress_rebuild.py | 50 +++++++++++-------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index 852410b502..5a422c9667 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -1,7 +1,4 @@ -import pprint - -from avalon import api, io, pipeline -from avalon.tools.cbloader import lib +from avalon import api class SetDressRebuild(api.Loader): @@ -17,30 +14,39 @@ class SetDressRebuild(api.Loader): def process(self, name, namespace, context, data): import json - # from maya import cmds - - print ">>>", lib.__file__ + from maya import cmds + from avalon.tools.cbloader import lib + from colorbleed.maya import lib as clib # Ensure data_file = self.fname.replace(".abc", ".json") with open(data_file, "r") as fp: build_data = json.load(fp) - pprint.pprint(build_data) - for _id, instances in build_data.items(): - # Rebuild filename + for representation_id, instances in build_data.items(): + + # Find the corresponding loader + loaders = list(lib.iter_loaders(representation_id)) + + # Ensure context can be passed on for inst in instances: - nodes = self.run_loader(_id) - # cmds.xform(nodes, matrix=inst["matrix"]) + # Get the uses loader + Loader = next((x for x in loaders + if x.__name__ == inst['loader']), + None) - def run_loader(self, _id): - # get all registered plugins - obj_id = io.ObjectId(_id) - loader_inst = lib.iter_loaders(obj_id) - if loader_inst is None: - raise RuntimeError("Could not find matching loader") + if Loader is None: + self.log.warning("Loader is missing: %s. Skipping %s", + inst['loader'], inst) + continue - # strip the generator layer from the found loader - loader = list(loader_inst)[0] - context = lib.get_representation_context(obj_id) - loader.process(**context) + # Run the loader + container = lib.run_loader(Loader, representation_id, + namespace=inst['namespace']) + + # Apply transformations + container_data = {"objectName": container} + transforms = clib.get_container_transfroms(container_data, + root=True) + for transform, matrix in zip(transforms, inst["matrix"]): + cmds.xform(transform, matrix=matrix) From 74b100b1138c12e376444522db0153c72ad32361 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 10:20:22 +0200 Subject: [PATCH 14/58] added setdress extract --- .../plugins/maya/publish/extract_setdress.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/colorbleed/plugins/maya/publish/extract_setdress.py b/colorbleed/plugins/maya/publish/extract_setdress.py index 5e1d13abef..4be7dcbe29 100644 --- a/colorbleed/plugins/maya/publish/extract_setdress.py +++ b/colorbleed/plugins/maya/publish/extract_setdress.py @@ -22,22 +22,20 @@ class ExtractSetDress(colorbleed.api.Extractor): def process(self, instance): - # Dump json - self.log.info("Dumping scene data for debugging ..") - - data = instance.data - - self.log.info("Extracting point cache") - parent_dir = self.staging_dir(instance) filename = "{}.abc".format(instance.name) path = os.path.join(parent_dir, filename) json_filename = "{}.json".format(instance.name) json_path = os.path.join(parent_dir, json_filename) - with open(json_path, "w") as fp: - json.dump(data["scenedata"], fp, ensure_ascii=False, indent=4) + self.log.info("Dumping scene data for debugging ..") + with open(json_path, "w") as filepath: + json.dump(instance.data["scenedata"], + filepath, + ensure_ascii=False, + indent=4) + self.log.info("Extracting point cache ..") cmds.select(instance) # Run basic alembic exporter @@ -52,3 +50,5 @@ class ExtractSetDress(colorbleed.api.Extractor): "selection": True}) instance.data["files"] = [json_path, path] + + cmds.select(clear=True) From 640e8e6eb71f899b80ea8146ee532b18852688c5 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 11:08:24 +0200 Subject: [PATCH 15/58] string to bson.ObjectId conversion by default --- colorbleed/maya/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index d752ca8e3e..cdfc0353bd 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -767,7 +767,7 @@ def list_looks(asset_id): # # get all subsets with look leading in # the name associated with the asset - subset = io.find({"parent": asset_id, + subset = io.find({"parent": bson.ObjectId(asset_id), "type": "subset", "name": {"$regex": "look*"}}) From 3a4d89c389ee779dddd53b9eec30a94ea4c93655 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 14:26:02 +0200 Subject: [PATCH 16/58] integrate is all string, sorting transforms --- .../plugins/maya/publish/collect_setdress.py | 42 +++++++------------ 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index be3c3b9092..942e91c090 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -5,6 +5,8 @@ from maya import cmds, mel from avalon import maya as amaya from colorbleed.maya import lib +import pprint + class CollectSetDress(pyblish.api.InstancePlugin): """Collect all relevant setdress items @@ -32,31 +34,23 @@ class CollectSetDress(pyblish.api.InstancePlugin): for container in containers: members = cmds.sets(container["objectName"], query=True) - transforms = lib.get_container_transfroms(container, members) - root = lib.get_container_transfroms(container, members, root=True) + transforms = lib.get_container_transforms(container, members) + root = lib.get_container_transforms(container, transforms, + root=True) if root not in instance_lookup: continue - representation_id = container["representation"] - shapes = [m for m in members if - cmds.objectType(m, isAType="shape") is True] - - look_ids = [self.get_look_id(shape) for shape in shapes] - - # Support for re-opened setdress scenes where the only connected - # transform node is the root node. This is due to how references - # are loaded in combination with the containers. - - matrix_data = self.get_matrix_data(transforms) + # retrieve all matrix data + matrix_data = self.get_matrix_data(sorted(transforms)) # Gather info for new data entry reference_node = cmds.ls(members, type="reference")[0] namespace = cmds.referenceQuery(reference_node, namespace=True) + representation_id = container["representation"] data[representation_id].append({ "loader": container["loader"], "matrix": matrix_data, - "namespace": namespace, - "look_id": look_ids + "namespace": namespace }) instance.data["scenedata"] = dict(data) @@ -66,18 +60,12 @@ class CollectSetDress(pyblish.api.InstancePlugin): def get_matrix_data(self, members): - matrix_data = [] - for member in members: - self.log.info(member) + matrix_data = {} + for idx, member in enumerate(members): matrix = cmds.xform(member, query=True, matrix=True) - matrix_data.append(matrix) + if matrix == lib.DEFAULT_MATRIX: + continue + + matrix_data[str(idx)] = matrix return matrix_data - - def get_look_id(self, node): - """Get the look id of the assigned shader""" - shad_engine = cmds.listConnections(node, type="shadingEngine") - if not shad_engine or shad_engine == "initialShadingEngine": - return - - return cmds.getAttr("{}.cbId".format(shad_engine[0])).split(":")[0] From 5fc5c26e6e268ea4b2d0b0860063de1ac39f3e1c Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 14:26:51 +0200 Subject: [PATCH 17/58] sorting tranforms to match collector data --- .../maya/load/load_setdress_rebuild.py | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index 5a422c9667..6a4e9aa316 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -7,7 +7,7 @@ class SetDressRebuild(api.Loader): representations = ["abc"] label = "Rebuild Set Dress" - order = -9 + order = -10 icon = "code-fork" color = "orange" @@ -31,8 +31,8 @@ class SetDressRebuild(api.Loader): # Ensure context can be passed on for inst in instances: # Get the uses loader - Loader = next((x for x in loaders - if x.__name__ == inst['loader']), + Loader = next((x for x in loaders if + x.__name__ == inst['loader']), None) if Loader is None: @@ -41,12 +41,18 @@ class SetDressRebuild(api.Loader): continue # Run the loader - container = lib.run_loader(Loader, representation_id, - namespace=inst['namespace']) + namespace = inst['namespace'].strip(":") + container = lib.run_loader(Loader, + representation_id, + namespace=namespace) # Apply transformations + if not inst["matrix"]: + continue + container_data = {"objectName": container} - transforms = clib.get_container_transfroms(container_data, - root=True) - for transform, matrix in zip(transforms, inst["matrix"]): - cmds.xform(transform, matrix=matrix) + transforms = clib.get_container_transforms(container_data) + # Force sort order, similar to collector + transforms = sorted(transforms) + for idx, matrix in inst["matrix"].items(): + cmds.xform(transforms[int(idx)], matrix=matrix) From d9bfbaea98a2a52c7112f41a3b192308cdff1eb8 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 14:28:45 +0200 Subject: [PATCH 18/58] cosmetics --- colorbleed/maya/lib.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index cdfc0353bd..db89b7c403 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -805,8 +805,7 @@ def assign_look_by_version(nodes, version_id): log.info("Loading lookdev for the first time..") # Define namespace - assetname = shader_file['context']['asset'] - ns_assetname = "{}_".format(assetname) + ns_assetname = "{}_".format(shader_file['context']['asset']) namespace = maya.unique_namespace(ns_assetname, format="%03d", suffix="_look") @@ -822,8 +821,7 @@ def assign_look_by_version(nodes, version_id): # give along a fake "context" with only `representation` # because `maya.containerise` only used that key anyway context = {"representation": shader_file} - subset_name = shader_file["context"]["subset"] - maya.containerise(name=subset_name, + maya.containerise(name=shader_file["context"]["subset"], namespace=namespace, nodes=shader_nodes, context=context) @@ -1025,7 +1023,7 @@ def get_related_sets(node): return sets -def get_container_transfroms(container, members=None, root=False): +def get_container_transforms(container, members=None, root=False): """Retrieve the root node of the container content When a container is created through a Loader the content @@ -1037,7 +1035,7 @@ def get_container_transfroms(container, members=None, root=False): members (list): optional and convenience argument Returns: - root (str): highest node in hierarchy + root (list / str): highest node in hierarchy """ if not members: From 8ca4117cbdb4fbf76199ded11e64ff5f71791df2 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 15:06:17 +0200 Subject: [PATCH 19/58] reduced data for version in database --- colorbleed/plugins/publish/integrate.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/publish/integrate.py b/colorbleed/plugins/publish/integrate.py index 43b29edc63..e26d45350b 100644 --- a/colorbleed/plugins/publish/integrate.py +++ b/colorbleed/plugins/publish/integrate.py @@ -319,9 +319,9 @@ class IntegrateAsset(pyblish.api.InstancePlugin): current_families = instance.data.get("families", list()) instance_family = instance.data.get("family", None) - families += current_families if instance_family is not None: families.append(instance_family) + families += current_families # create relative source path for DB relative_path = os.path.relpath(context.data["currentFile"], @@ -334,4 +334,10 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "source": source, "comment": context.data.get("comment")} - return dict(instance.data, **version_data) + # Include optional data if present in + optionals = ["startFrame", "endFrame", "step", "handles"] + for key in optionals: + if key in instance.data: + version_data[key] = instance.data[key] + + return version_data From be74fc99217982a6643c29d519fe175b17d0d3ea Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 15:07:45 +0200 Subject: [PATCH 20/58] removed type conversion for key in matrix_data --- colorbleed/plugins/maya/publish/collect_setdress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index 942e91c090..cdd4f851a1 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -42,6 +42,7 @@ class CollectSetDress(pyblish.api.InstancePlugin): # retrieve all matrix data matrix_data = self.get_matrix_data(sorted(transforms)) + pprint.pprint(matrix_data) # Gather info for new data entry reference_node = cmds.ls(members, type="reference")[0] @@ -65,7 +66,6 @@ class CollectSetDress(pyblish.api.InstancePlugin): matrix = cmds.xform(member, query=True, matrix=True) if matrix == lib.DEFAULT_MATRIX: continue - - matrix_data[str(idx)] = matrix + matrix_data[idx] = matrix return matrix_data From de3c663adeb91686a68a7a1f907ba663a7f03c11 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 15:10:26 +0200 Subject: [PATCH 21/58] removed indent from json dump, remove scenedata after extract --- colorbleed/plugins/maya/publish/extract_setdress.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/colorbleed/plugins/maya/publish/extract_setdress.py b/colorbleed/plugins/maya/publish/extract_setdress.py index 4be7dcbe29..5d0e5cdb49 100644 --- a/colorbleed/plugins/maya/publish/extract_setdress.py +++ b/colorbleed/plugins/maya/publish/extract_setdress.py @@ -30,10 +30,7 @@ class ExtractSetDress(colorbleed.api.Extractor): self.log.info("Dumping scene data for debugging ..") with open(json_path, "w") as filepath: - json.dump(instance.data["scenedata"], - filepath, - ensure_ascii=False, - indent=4) + json.dump(instance.data["scenedata"], filepath, ensure_ascii=False) self.log.info("Extracting point cache ..") cmds.select(instance) @@ -51,4 +48,7 @@ class ExtractSetDress(colorbleed.api.Extractor): instance.data["files"] = [json_path, path] + # Remove data + instance.data.pop("scenedata", None) + cmds.select(clear=True) From 9bea6535a1200e9d68389bce7f775de50fe08403 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 11 Oct 2017 15:10:45 +0200 Subject: [PATCH 22/58] removed prints --- colorbleed/plugins/maya/publish/collect_setdress.py | 1 - 1 file changed, 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index cdd4f851a1..96c3d6a477 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -42,7 +42,6 @@ class CollectSetDress(pyblish.api.InstancePlugin): # retrieve all matrix data matrix_data = self.get_matrix_data(sorted(transforms)) - pprint.pprint(matrix_data) # Gather info for new data entry reference_node = cmds.ls(members, type="reference")[0] From cec5b6908033a5d8e58d6e32736c3b18c04336a6 Mon Sep 17 00:00:00 2001 From: aardschok Date: Thu, 12 Oct 2017 11:16:04 +0200 Subject: [PATCH 23/58] removed namespace altering, falling back to default --- .../{load_animation.py => load_alembic.py} | 52 +++++++++++-------- colorbleed/plugins/maya/load/load_look.py | 8 --- .../plugins/maya/load/load_mayaascii.py | 6 --- colorbleed/plugins/maya/load/load_model.py | 9 ---- colorbleed/plugins/maya/load/load_rig.py | 4 +- colorbleed/plugins/maya/load/load_setdress.py | 32 ------------ .../maya/load/load_setdress_rebuild.py | 32 +++++++----- 7 files changed, 50 insertions(+), 93 deletions(-) rename colorbleed/plugins/maya/load/{load_animation.py => load_alembic.py} (50%) delete mode 100644 colorbleed/plugins/maya/load/load_setdress.py diff --git a/colorbleed/plugins/maya/load/load_animation.py b/colorbleed/plugins/maya/load/load_alembic.py similarity index 50% rename from colorbleed/plugins/maya/load/load_animation.py rename to colorbleed/plugins/maya/load/load_alembic.py index b9f187655d..6cea82a832 100644 --- a/colorbleed/plugins/maya/load/load_animation.py +++ b/colorbleed/plugins/maya/load/load_alembic.py @@ -1,18 +1,14 @@ -import os - - from avalon import api -class AbcLoader(api.Loader): +class AnimationLoader(api.Loader): """Specific loader of Alembic for the avalon.animation family""" families = ["colorbleed.animation", "colorbleed.camera", "colorbleed.pointcache"] - representations = ["abc"] - label = "Reference animation" + representations = ["abc"] order = -10 icon = "code-fork" color = "orange" @@ -20,23 +16,8 @@ class AbcLoader(api.Loader): def process(self, name, namespace, context, data): import maya.cmds as cmds - from avalon import maya cmds.loadPlugin("AbcImport.mll", quiet=True) - # Prevent identical alembic nodes from being shared - # Create unique namespace for the cameras - - # Get name from asset being loaded - # Assuming name is subset name from the animation, we split the number - # suffix from the name to ensure the namespace is unique - name = name.split("_")[0] - namespace = maya.unique_namespace("{}_".format(name), - format="%03d", - suffix="_abc") - - # hero_001 (abc) - # asset_counter{optional} - nodes = cmds.file(self.fname, namespace=namespace, sharedReferenceFile=False, @@ -45,5 +26,30 @@ class AbcLoader(api.Loader): reference=True, returnNewNodes=True) - # load colorbleed ID attribute - self[:] = nodes \ No newline at end of file + self[:] = nodes + + +class SetDressAlembicLoader(api.Loader): + """Load the setdress as alembic""" + + families = ["colorbleed.setdress"] + label = "Reference Alembic" + representations = ["abc"] + order = -10 + icon = "code-fork" + color = "orange" + + def process(self, name, namespace, context, data): + + import maya.cmds as cmds + + cmds.loadPlugin("AbcImport.mll", quiet=True) + nodes = cmds.file(self.fname, + namespace=namespace, + sharedReferenceFile=False, + groupReference=True, + groupName="{}:{}".format(namespace, name), + reference=True, + returnNewNodes=True) + + self[:] = nodes diff --git a/colorbleed/plugins/maya/load/load_look.py b/colorbleed/plugins/maya/load/load_look.py index 6a265b64ce..03fe7fe7b5 100644 --- a/colorbleed/plugins/maya/load/load_look.py +++ b/colorbleed/plugins/maya/load/load_look.py @@ -32,14 +32,6 @@ class LookLoader(api.Loader): from avalon import maya import colorbleed.maya.lib as lib - # improve readability of the namespace - assetname = context["asset"]["name"] - ns_assetname = "{}_".format(assetname) - - namespace = maya.unique_namespace(ns_assetname, - format="%03d", - suffix="_look") - # try / except here is to ensure that the get_reference_node # does not fail when the file doesn't exist yet reference_node = None diff --git a/colorbleed/plugins/maya/load/load_mayaascii.py b/colorbleed/plugins/maya/load/load_mayaascii.py index 7bc75fca45..bce8ab611e 100644 --- a/colorbleed/plugins/maya/load/load_mayaascii.py +++ b/colorbleed/plugins/maya/load/load_mayaascii.py @@ -17,12 +17,6 @@ class MayaAsciiLoader(api.Loader): import maya.cmds as cmds from avalon import maya - # Create a readable namespace - # Namespace should contain asset name and counter - # TEST_001{_descriptor} where `descriptor` can be `_abc` for example - assetname = "{}_".format(namespace.split("_")[0]) - namespace = maya.unique_namespace(assetname, format="%03d") - with maya.maintained_selection(): nodes = cmds.file(self.fname, namespace=namespace, diff --git a/colorbleed/plugins/maya/load/load_model.py b/colorbleed/plugins/maya/load/load_model.py index 7df8a0421c..366cb93288 100644 --- a/colorbleed/plugins/maya/load/load_model.py +++ b/colorbleed/plugins/maya/load/load_model.py @@ -17,15 +17,6 @@ class ModelLoader(api.Loader): import maya.cmds as cmds from avalon import maya - # Ensure Alembic is loaded - cmds.loadPlugin("AbcImport", quiet=True) - - # Create a readable namespace - # Namespace should contain asset name and counter - # TEST_001{_descriptor} where `descriptor` can be `_abc` for example - assetname = "{}_".format(namespace.split("_")[0]) - namespace = maya.unique_namespace(assetname, format="%03d") - with maya.maintained_selection(): nodes = cmds.file(self.fname, namespace=namespace, diff --git a/colorbleed/plugins/maya/load/load_rig.py b/colorbleed/plugins/maya/load/load_rig.py index 9fb0ed9981..894dadf6bb 100644 --- a/colorbleed/plugins/maya/load/load_rig.py +++ b/colorbleed/plugins/maya/load/load_rig.py @@ -22,8 +22,6 @@ class RigLoader(api.Loader): def process(self, name, namespace, context, data): - assetname = "{}_".format(context["asset"]["name"]) - unique_namespace = maya.unique_namespace(assetname, format="%03d") nodes = cmds.file(self.fname, namespace=namespace, reference=True, @@ -34,7 +32,7 @@ class RigLoader(api.Loader): # Store for post-process self[:] = nodes if data.get("post_process", True): - self._post_process(name, unique_namespace, context, data) + self._post_process(name, namespace, context, data) def _post_process(self, name, namespace, context, data): from avalon import maya diff --git a/colorbleed/plugins/maya/load/load_setdress.py b/colorbleed/plugins/maya/load/load_setdress.py deleted file mode 100644 index 2ad1a58339..0000000000 --- a/colorbleed/plugins/maya/load/load_setdress.py +++ /dev/null @@ -1,32 +0,0 @@ -from avalon import api, io - - -class SetDressAlembicLoader(api.Loader): - """Load the setdress as alembic""" - - families = ["colorbleed.setdress"] - representations = ["abc"] - - label = "Load Alembic" - order = -10 - icon = "code-fork" - color = "orange" - - def process(self, name, namespace, context, data): - - import maya.cmds as cmds - from avalon import maya - - namespace = maya.unique_namespace("{}_".format(name), - format="%03d", - suffix="_abc") - - with maya.maintained_selection(): - nodes = cmds.file(self.fname, - namespace=namespace, - reference=True, - returnNewNodes=True, - groupReference=True, - groupName="{}:{}".format(namespace, name)) - - self[:] = nodes diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index 6a4e9aa316..00544bf938 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -4,10 +4,10 @@ from avalon import api class SetDressRebuild(api.Loader): families = ["colorbleed.setdress"] - representations = ["abc"] + representations = ["json"] label = "Rebuild Set Dress" - order = -10 + order = -9 icon = "code-fork" color = "orange" @@ -17,12 +17,14 @@ class SetDressRebuild(api.Loader): from maya import cmds from avalon.tools.cbloader import lib from colorbleed.maya import lib as clib + from cb.utils.maya import core - # Ensure - data_file = self.fname.replace(".abc", ".json") - with open(data_file, "r") as fp: + with open(self.fname, "r") as fp: build_data = json.load(fp) + cmds.namespace(add=namespace) + + new_transforms = [] for representation_id, instances in build_data.items(): # Find the corresponding loader @@ -30,7 +32,7 @@ class SetDressRebuild(api.Loader): # Ensure context can be passed on for inst in instances: - # Get the uses loader + # Get the used loader Loader = next((x for x in loaders if x.__name__ == inst['loader']), None) @@ -41,18 +43,24 @@ class SetDressRebuild(api.Loader): continue # Run the loader - namespace = inst['namespace'].strip(":") + instance_ns = ":".join([namespace, + inst['namespace'].strip(":")]) container = lib.run_loader(Loader, representation_id, - namespace=namespace) - - # Apply transformations - if not inst["matrix"]: - continue + namespace=instance_ns) container_data = {"objectName": container} transforms = clib.get_container_transforms(container_data) # Force sort order, similar to collector transforms = sorted(transforms) + for idx, matrix in inst["matrix"].items(): cmds.xform(transforms[int(idx)], matrix=matrix) + + # Store the created container + self.append(container) + new_transforms.extend(transforms) + + roots = core.getHighestInHierarchy(new_transforms) + cmds.group(roots, name="{}:{}".format(namespace, + context['subset']['name'])) From bf289ab75bc26918c20b825ff757dc4cef541ff1 Mon Sep 17 00:00:00 2001 From: aardschok Date: Thu, 12 Oct 2017 11:17:48 +0200 Subject: [PATCH 24/58] stripping namespace from the root `:` --- colorbleed/plugins/maya/publish/collect_setdress.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index 96c3d6a477..42bf040f47 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -5,8 +5,6 @@ from maya import cmds, mel from avalon import maya as amaya from colorbleed.maya import lib -import pprint - class CollectSetDress(pyblish.api.InstancePlugin): """Collect all relevant setdress items @@ -50,7 +48,7 @@ class CollectSetDress(pyblish.api.InstancePlugin): data[representation_id].append({ "loader": container["loader"], "matrix": matrix_data, - "namespace": namespace + "namespace": namespace.strip(":") }) instance.data["scenedata"] = dict(data) From b40af5d190436c3a60c8de72efe8b047d59749b2 Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 13 Oct 2017 17:45:53 +0200 Subject: [PATCH 25/58] implemented monkey code --- .../maya/load/load_setdress_rebuild.py | 62 +++++-------------- 1 file changed, 14 insertions(+), 48 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index 00544bf938..b616d42893 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -1,5 +1,9 @@ +import site + from avalon import api +site.addsitedir(r"C:\Users\User\Documents\development\research\setdress") + class SetDressRebuild(api.Loader): @@ -13,54 +17,16 @@ class SetDressRebuild(api.Loader): def process(self, name, namespace, context, data): - import json from maya import cmds - from avalon.tools.cbloader import lib - from colorbleed.maya import lib as clib - from cb.utils.maya import core + import avalon.maya as amaya - with open(self.fname, "r") as fp: - build_data = json.load(fp) + context_ns = context["subset"]["name"] + with amaya.maintained_selection(): + file_nodes = cmds.file(self.fname, + namespace=context_ns, + reference=True, + returnNewNodes=True, + groupReference=True, + groupName="{}:{}".format(context_ns, name)) - cmds.namespace(add=namespace) - - new_transforms = [] - for representation_id, instances in build_data.items(): - - # Find the corresponding loader - loaders = list(lib.iter_loaders(representation_id)) - - # Ensure context can be passed on - for inst in instances: - # Get the used loader - Loader = next((x for x in loaders if - x.__name__ == inst['loader']), - None) - - if Loader is None: - self.log.warning("Loader is missing: %s. Skipping %s", - inst['loader'], inst) - continue - - # Run the loader - instance_ns = ":".join([namespace, - inst['namespace'].strip(":")]) - container = lib.run_loader(Loader, - representation_id, - namespace=instance_ns) - - container_data = {"objectName": container} - transforms = clib.get_container_transforms(container_data) - # Force sort order, similar to collector - transforms = sorted(transforms) - - for idx, matrix in inst["matrix"].items(): - cmds.xform(transforms[int(idx)], matrix=matrix) - - # Store the created container - self.append(container) - new_transforms.extend(transforms) - - roots = core.getHighestInHierarchy(new_transforms) - cmds.group(roots, name="{}:{}".format(namespace, - context['subset']['name'])) + self[:] = file_nodes From f07e0a31a4e5515285e9d15b401d321406c08b0c Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 13 Oct 2017 17:46:47 +0200 Subject: [PATCH 26/58] cosmetics --- colorbleed/plugins/maya/publish/collect_setdress.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index 42bf040f47..885cdcaf2a 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -33,7 +33,8 @@ class CollectSetDress(pyblish.api.InstancePlugin): for container in containers: members = cmds.sets(container["objectName"], query=True) transforms = lib.get_container_transforms(container, members) - root = lib.get_container_transforms(container, transforms, + root = lib.get_container_transforms(container, + transforms, root=True) if root not in instance_lookup: continue @@ -57,6 +58,16 @@ class CollectSetDress(pyblish.api.InstancePlugin): return mel.eval('workspace -query -fileRuleEntry "{}"'.format(rule)) def get_matrix_data(self, members): + """Get the matrix of all members when they are not default + + Each matrix which differs from the default will be stored in a + dictionary + + Args: + members (list): list of transform nmodes + Returns: + dict + """ matrix_data = {} for idx, member in enumerate(members): From 39c5bcac1066083b4620cfe1ea26231c7b03ee77 Mon Sep 17 00:00:00 2001 From: aardschok Date: Wed, 18 Oct 2017 17:02:36 +0200 Subject: [PATCH 27/58] quick dump --- colorbleed/maya/lib.py | 7 +++- .../plugins/maya/publish/collect_setdress.py | 41 +++++++++++-------- .../plugins/maya/publish/extract_setdress.py | 10 ++--- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index db89b7c403..3c75e6c14d 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1033,9 +1033,10 @@ def get_container_transforms(container, members=None, root=False): Args: container (dict): the container members (list): optional and convenience argument + root (bool): return highest node in hierachy if True Returns: - root (list / str): highest node in hierarchy + root (list / str): """ if not members: @@ -1043,6 +1044,8 @@ def get_container_transforms(container, members=None, root=False): results = cmds.ls(members, type="transform", long=True) if root: - results = core.getHighestInHierarchy(results)[0] + root = core.getHighestInHierarchy(results) + if root: + results = root[0] return results diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index 885cdcaf2a..5eaa5822ba 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -27,10 +27,12 @@ class CollectSetDress(pyblish.api.InstancePlugin): containers = amaya.ls() # Get all content from the instance + topnode = cmds.sets(instance.name, query=True)[0] instance_lookup = set(cmds.ls(instance, type="transform", long=True)) data = defaultdict(list) - for container in containers: + hierarchy_nodes = [] + for i, container in enumerate(containers): members = cmds.sets(container["objectName"], query=True) transforms = lib.get_container_transforms(container, members) root = lib.get_container_transforms(container, @@ -39,25 +41,35 @@ class CollectSetDress(pyblish.api.InstancePlugin): if root not in instance_lookup: continue - # retrieve all matrix data - matrix_data = self.get_matrix_data(sorted(transforms)) + # Retrieve all matrix data + hierarchy = cmds.listRelatives(root, parent=True, fullPath=True)[0] + relative_hierarchy = hierarchy.replace(topnode, "*") + hierarchy_nodes.append(relative_hierarchy) # Gather info for new data entry reference_node = cmds.ls(members, type="reference")[0] namespace = cmds.referenceQuery(reference_node, namespace=True) representation_id = container["representation"] - data[representation_id].append({ - "loader": container["loader"], - "matrix": matrix_data, - "namespace": namespace.strip(":") - }) + + instance_data = {"loader": container["loader"], + "hierarchy": hierarchy, + "namespace": namespace.strip(":")} + + # Check if matrix differs from default and store changes + matrix_data = self.get_matrix_data(root) + if matrix_data: + instance_data["matrix"] = matrix_data + + data[representation_id].append(instance_data) instance.data["scenedata"] = dict(data) + instance.data["hierarchy"] = list(set(hierarchy_nodes)) + def get_file_rule(self, rule): return mel.eval('workspace -query -fileRuleEntry "{}"'.format(rule)) - def get_matrix_data(self, members): + def get_matrix_data(self, node): """Get the matrix of all members when they are not default Each matrix which differs from the default will be stored in a @@ -69,11 +81,8 @@ class CollectSetDress(pyblish.api.InstancePlugin): dict """ - matrix_data = {} - for idx, member in enumerate(members): - matrix = cmds.xform(member, query=True, matrix=True) - if matrix == lib.DEFAULT_MATRIX: - continue - matrix_data[idx] = matrix + matrix = cmds.xform(node, query=True, matrix=True) + if matrix == lib.DEFAULT_MATRIX: + return - return matrix_data + return matrix diff --git a/colorbleed/plugins/maya/publish/extract_setdress.py b/colorbleed/plugins/maya/publish/extract_setdress.py index 5d0e5cdb49..d902c2073d 100644 --- a/colorbleed/plugins/maya/publish/extract_setdress.py +++ b/colorbleed/plugins/maya/publish/extract_setdress.py @@ -23,8 +23,8 @@ class ExtractSetDress(colorbleed.api.Extractor): def process(self, instance): parent_dir = self.staging_dir(instance) - filename = "{}.abc".format(instance.name) - path = os.path.join(parent_dir, filename) + hierarchy_filename = "{}.abc".format(instance.name) + hierarchy_path = os.path.join(parent_dir, hierarchy_filename) json_filename = "{}.json".format(instance.name) json_path = os.path.join(parent_dir, json_filename) @@ -33,10 +33,10 @@ class ExtractSetDress(colorbleed.api.Extractor): json.dump(instance.data["scenedata"], filepath, ensure_ascii=False) self.log.info("Extracting point cache ..") - cmds.select(instance) + cmds.select(cmds.ls(instance.data["hierarchy"], long=True)) # Run basic alembic exporter - extract_alembic(file=path, + extract_alembic(file=hierarchy_path, startFrame=1.0, endFrame=1.0, **{"step": 1.0, @@ -46,7 +46,7 @@ class ExtractSetDress(colorbleed.api.Extractor): "uvWrite": True, "selection": True}) - instance.data["files"] = [json_path, path] + instance.data["files"] = [json_path, hierarchy_path] # Remove data instance.data.pop("scenedata", None) From fa935a923fc4b31603c4b10aa67fa7a1351724d7 Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 20 Oct 2017 14:31:21 +0200 Subject: [PATCH 28/58] added new set dress loader --- .../maya/load/load_setdress_rebuild.py | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index b616d42893..b8c84ef767 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -17,16 +17,18 @@ class SetDressRebuild(api.Loader): def process(self, name, namespace, context, data): - from maya import cmds - import avalon.maya as amaya + # Hack + import sys - context_ns = context["subset"]["name"] - with amaya.maintained_selection(): - file_nodes = cmds.file(self.fname, - namespace=context_ns, - reference=True, - returnNewNodes=True, - groupReference=True, - groupName="{}:{}".format(context_ns, name)) + p = r"C:\Users\User\Documents\development\research\setdress" + if p not in sys.path: + sys.path.insert(0, p) - self[:] = file_nodes + import loader + reload(loader) + + containers = loader.load_package(filepath=self.fname, + name=name, + namespace=namespace) + + self[:] = containers From 09f55ccf83f918496719dcf78c67c9d175bcc404 Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 20 Oct 2017 14:31:44 +0200 Subject: [PATCH 29/58] changed hierarchy key to parent --- colorbleed/plugins/maya/publish/collect_setdress.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index 5eaa5822ba..52877ed8aa 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -5,6 +5,8 @@ from maya import cmds, mel from avalon import maya as amaya from colorbleed.maya import lib +# TODO : Publish of setdress: -unique namespace for all assets, VALIDATOR! + class CollectSetDress(pyblish.api.InstancePlugin): """Collect all relevant setdress items @@ -42,8 +44,8 @@ class CollectSetDress(pyblish.api.InstancePlugin): continue # Retrieve all matrix data - hierarchy = cmds.listRelatives(root, parent=True, fullPath=True)[0] - relative_hierarchy = hierarchy.replace(topnode, "*") + parent = cmds.listRelatives(root, parent=True, fullPath=True)[0] + relative_hierarchy = parent.replace(topnode, "*") hierarchy_nodes.append(relative_hierarchy) # Gather info for new data entry @@ -52,7 +54,7 @@ class CollectSetDress(pyblish.api.InstancePlugin): representation_id = container["representation"] instance_data = {"loader": container["loader"], - "hierarchy": hierarchy, + "parent": parent, "namespace": namespace.strip(":")} # Check if matrix differs from default and store changes @@ -65,7 +67,6 @@ class CollectSetDress(pyblish.api.InstancePlugin): instance.data["scenedata"] = dict(data) instance.data["hierarchy"] = list(set(hierarchy_nodes)) - def get_file_rule(self, rule): return mel.eval('workspace -query -fileRuleEntry "{}"'.format(rule)) From fcd0498f7cad0cca744f624f2d3262c90b40f0c3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 20 Oct 2017 17:35:49 +0200 Subject: [PATCH 30/58] Implement #195 --- colorbleed/plugins/maya/load/actions.py | 4 ++-- colorbleed/plugins/maya/load/load_animation.py | 7 ++----- colorbleed/plugins/maya/load/load_imagesequence.py | 2 +- colorbleed/plugins/maya/load/load_look.py | 4 ++-- colorbleed/plugins/maya/load/load_mayaascii.py | 4 ++-- colorbleed/plugins/maya/load/load_model.py | 4 ++-- colorbleed/plugins/maya/load/load_rig.py | 14 +++++++------- 7 files changed, 18 insertions(+), 21 deletions(-) diff --git a/colorbleed/plugins/maya/load/actions.py b/colorbleed/plugins/maya/load/actions.py index 9e54195294..7b63c7d194 100644 --- a/colorbleed/plugins/maya/load/actions.py +++ b/colorbleed/plugins/maya/load/actions.py @@ -18,7 +18,7 @@ class SetFrameRangeLoader(api.Loader): icon = "clock-o" color = "white" - def process(self, name, namespace, context, data): + def load(self, context, name, namespace, data): import maya.cmds as cmds @@ -52,7 +52,7 @@ class SetFrameRangeWithHandlesLoader(api.Loader): icon = "clock-o" color = "white" - def process(self, name, namespace, context, data): + def load(self, context, name, namespace, data): import maya.cmds as cmds diff --git a/colorbleed/plugins/maya/load/load_animation.py b/colorbleed/plugins/maya/load/load_animation.py index b9f187655d..cabb06d886 100644 --- a/colorbleed/plugins/maya/load/load_animation.py +++ b/colorbleed/plugins/maya/load/load_animation.py @@ -1,10 +1,7 @@ -import os +import avalon.maya.pipeline -from avalon import api - - -class AbcLoader(api.Loader): +class AbcLoader(avalon.maya.pipeline.ReferenceLoader): """Specific loader of Alembic for the avalon.animation family""" families = ["colorbleed.animation", diff --git a/colorbleed/plugins/maya/load/load_imagesequence.py b/colorbleed/plugins/maya/load/load_imagesequence.py index 27e24f4782..2386f0c6e7 100644 --- a/colorbleed/plugins/maya/load/load_imagesequence.py +++ b/colorbleed/plugins/maya/load/load_imagesequence.py @@ -26,7 +26,7 @@ class OpenImageSequence(api.Loader): icon = "play-circle" color = "orange" - def process(self, name, namespace, context, data): + def load(self, context, name, namespace, data): directory = self.fname from avalon.vendor import clique diff --git a/colorbleed/plugins/maya/load/load_look.py b/colorbleed/plugins/maya/load/load_look.py index 6a265b64ce..f31d217282 100644 --- a/colorbleed/plugins/maya/load/load_look.py +++ b/colorbleed/plugins/maya/load/load_look.py @@ -1,10 +1,10 @@ import os import json -from avalon import api +import avalon.maya.pipeline -class LookLoader(api.Loader): +class LookLoader(avalon.maya.pipeline.ReferenceLoader): """Specific loader for lookdev""" families = ["colorbleed.look"] diff --git a/colorbleed/plugins/maya/load/load_mayaascii.py b/colorbleed/plugins/maya/load/load_mayaascii.py index 7bc75fca45..247e832be0 100644 --- a/colorbleed/plugins/maya/load/load_mayaascii.py +++ b/colorbleed/plugins/maya/load/load_mayaascii.py @@ -1,7 +1,7 @@ -from avalon import api +import avalon.maya.pipeline -class MayaAsciiLoader(api.Loader): +class MayaAsciiLoader(avalon.maya.pipeline.ReferenceLoader): """Load the model""" families = ["colorbleed.mayaAscii"] diff --git a/colorbleed/plugins/maya/load/load_model.py b/colorbleed/plugins/maya/load/load_model.py index 15637c9e81..ca54a2d41e 100644 --- a/colorbleed/plugins/maya/load/load_model.py +++ b/colorbleed/plugins/maya/load/load_model.py @@ -1,7 +1,7 @@ -from avalon import api +import avalon.maya.pipeline -class ModelLoader(api.Loader): +class ModelLoader(avalon.maya.pipeline.ReferenceLoader): """Load the model""" families = ["colorbleed.model"] diff --git a/colorbleed/plugins/maya/load/load_rig.py b/colorbleed/plugins/maya/load/load_rig.py index ee54de2ad6..ee7db5d7a1 100644 --- a/colorbleed/plugins/maya/load/load_rig.py +++ b/colorbleed/plugins/maya/load/load_rig.py @@ -2,10 +2,11 @@ import os from maya import cmds +import avalon.maya.pipeline from avalon import api, maya -class RigLoader(api.Loader): +class RigLoader(avalon.maya.pipeline.ReferenceLoader): """Specific loader for rigs This automatically creates an instance for animators upon load. @@ -37,7 +38,6 @@ class RigLoader(api.Loader): self._post_process(name, unique_namespace, context, data) def _post_process(self, name, namespace, context, data): - from avalon import maya # TODO(marcus): We are hardcoding the name "out_SET" here. # Better register this keyword, so that it can be used @@ -62,8 +62,8 @@ class RigLoader(api.Loader): # Create the animation instance with maya.maintained_selection(): cmds.select([output, controls] + roots, noExpand=True) - maya.create(name=namespace, - asset=asset, - family="colorbleed.animation", - options={"useSelection": True}, - data={"dependencies": dependency}) + api.create(name=namespace, + asset=asset, + family="colorbleed.animation", + options={"useSelection": True}, + data={"dependencies": dependency}) From e6de792519854d92a8759dc91c2f2ee9391e163c Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 20 Oct 2017 18:10:24 +0200 Subject: [PATCH 31/58] added validator and setdress api --- colorbleed/plugins/maya/load/setdress_api.py | 463 ++++++++++++++++++ .../publish/validate_setdress_namespaces.py | 0 2 files changed, 463 insertions(+) create mode 100644 colorbleed/plugins/maya/load/setdress_api.py create mode 100644 colorbleed/plugins/maya/publish/validate_setdress_namespaces.py diff --git a/colorbleed/plugins/maya/load/setdress_api.py b/colorbleed/plugins/maya/load/setdress_api.py new file mode 100644 index 0000000000..88ac85c1c3 --- /dev/null +++ b/colorbleed/plugins/maya/load/setdress_api.py @@ -0,0 +1,463 @@ +import logging +import json +import os + +import contextlib +import copy + +from maya import cmds + +import avalon.schema +import avalon.io as io +from avalon.maya.lib import unique_namespace + +log = logging.getLogger("PackageLoader") + + +def matrix_equals(a, b, tolerance=1e-10): + """Compares two matrices with an imperfection tolerance""" + if not all(abs(x - y) < tolerance for x, y in zip(a, b)): + return False + return True + + +def to_namespace(node, namespace): + """Return node name as if it's inside the namespace. + + Args: + node (str): Node name + namespace (str): Namespace + + Returns: + str: The node in the namespace. + + """ + namespace_prefix = "|{}:".format(namespace) + node = namespace_prefix.join(node.split("|")) + return node + + +@contextlib.contextmanager +def namespaced(namespace, new=True): + """Work inside namespace during context + + Args: + new (bool): When enabled this will rename the namespace to a unique + namespace if the input namespace already exists. + + Yields: + str: The namespace that is used during the context + + """ + original = cmds.namespaceInfo(cur=True) + if new: + namespace = unique_namespace(namespace) + cmds.namespace(add=namespace) + + try: + cmds.namespace(set=namespace) + yield namespace + finally: + cmds.namespace(set=original) + + +def load_package(filepath, name, namespace=None): + """Load a package that was gathered elsewhere. + + A package is a group of published instances, possibly with additional data + in a hierarchy. + + """ + + from avalon.tools.cbloader import lib + + if namespace is None: + # Define a unique namespace for the package + namespace = os.path.basename(filepath).split(".")[0] + unique_namespace(namespace) + assert isinstance(namespace, basestring) + + # Load the setdress package data + with open(filepath, "r") as fp: + data = json.load(fp) + + # Load the setdress alembic hierarchy + # We import this into the namespace in which we'll load the package's + # instances into afterwards. + alembic = filepath.replace(".json", ".abc") + hierarchy = cmds.file(alembic, + reference=True, + namespace=namespace, + returnNewNodes=True, + groupReference=True, + groupName="{}:{}".format(namespace, name), + typ="Alembic") + + containers = [] + for representation_id, instances in data.items(): + + # Find the compatible loaders + loaders = list(lib.iter_loaders(representation_id)) + + for instance in instances: + container = _add(instance, representation_id, loaders, name, + namespace) + containers.append(container) + + # TODO: Do we want to cripple? Or do we want to add a 'parent' parameter? + # Cripple the original avalon containers so they don't show up in the + # manager + # for container in containers: + # cmds.setAttr("%s.id" % container, + # "colorbleed.setdress.container", + # type="string") + + # TODO: Lock all loaded nodes + # This is to ensure the hierarchy remains unaltered by the artists + # for node in nodes: + # cmds.lockNode(node, lock=True) + + return containers + hierarchy + + +def _add(instance, representation_id, loaders, name, namespace): + """Add an item from the package + + Args: + instance (dict): + representation_id (str): + loaders (list): + namespace (str): + + Returns: + str: The created Avalon container. + + """ + + from avalon.tools.cbloader import lib + from colorbleed.maya.lib import get_container_transforms + + # Process within the namespace + with namespaced(namespace, new=False) as namespace: + + # Get the used loader + Loader = next((x for x in loaders if + x.__name__ == instance['loader']), + None) + + if Loader is None: + log.warning("Loader is missing: %s. Skipping %s", + instance['loader'], instance) + raise RuntimeError("Loader is missing.") + + container = lib.run_loader(Loader, + representation_id, + namespace=instance['namespace']) + + # Get the root from the loaded container + root = get_container_transforms({"objectName": container}, + root=True) + + # Apply matrix to root node (if any matrix edits) + matrix = instance.get("matrix", None) + if matrix: + cmds.xform(root, objectSpace=True, matrix=matrix) + + # Parent into the setdress hierarchy + # Namespace is missing from parent node(s), add namespace + # manually + parent_grp = instance["parent"] + parent_grp = "{}:{}|".format(namespace, name) + to_namespace(parent_grp, namespace) + + cmds.parent(root, parent_grp, relative=True) + + return container + + +# Store root nodes based on representation and namespace +def _instances_by_namespace(data): + """Rebuild instance data so we can look it up by namespace. + + Note that the `representation` is added into the instance's + data with a `representation` key. + + Args: + data (dict): scene build data + + Returns: + dict + + """ + result = {} + # Add new assets + for representation_id, instances in data.items(): + + # Ensure we leave the source data unaltered + instances = copy.deepcopy(instances) + for instance in instances: + instance['representation'] = representation_id + result[instance['namespace']] = instance + + return result + + +def update_package(set_container, version): + """Update any matrix changes in the scene based on the new data + + Args: + set_container (dict): container data from `ls()` + version (int): version number of the subset + + """ + + from colorbleed.maya.lib import get_representation_file + from avalon.maya.pipeline import parse_container + + # Versioning (from `core.maya.pipeline`) + current_representation = io.find_one({ + "_id": io.ObjectId(set_container["representation"]) + }) + + assert current_representation is not None, "This is a bug" + + version_, subset, asset, project = io.parenthood(current_representation) + + if version == -1: + new_version = io.find_one({ + "type": "version", + "parent": subset["_id"] + }, sort=[("name", -1)]) + else: + new_version = io.find_one({ + "type": "version", + "parent": subset["_id"], + "name": version, + }) + + assert new_version is not None, "This is a bug" + + # Get the new representation (new file) + new_representation = io.find_one({ + "type": "representation", + "parent": new_version["_id"], + "name": current_representation["name"] + }) + + # Load the original package data + current_file = get_representation_file(current_representation) + assert current_file.endswith(".json") + with open(current_file, "r") as fp: + current_data = json.load(fp) + + # Load the new package data + new_file = get_representation_file(new_representation) + assert new_file.endswith(".json") + with open(new_file, "r") as fp: + new_data = json.load(fp) + + # Get avalon containers in the setdress + containers = [] + members = cmds.sets(set_container['objectName'], query=True) + for node in cmds.ls(members, type="objectSet"): + try: + container = parse_container(node) + containers.append(container) + except avalon.schema.ValidationError: + pass + + # Update scene content + update_scene(set_container, containers, current_data, new_data, new_file) + + # TODO: This should be handled by the pipeline itself + cmds.setAttr(set_container['objectName'] + ".representation", + str(new_representation['_id']), type="string") + + +def update_scene(set_container, containers, current_data, new_data, new_file): + """Updates the hierarchy, assets and their matrix + + Updates the following withing the scene: + * Setdress hierarchy alembic + * Matrix + * Parenting + * Representations + + It removes any assets which are not present in the new build data + + Args: + set_container (dict): the setdress container of the scene + containers (list): the list of containers under the setdress container + current_data (dict): the current build data of the setdress + new_data (dict): the new build data of the setdres + + Returns: + processed_containers (list): all new and updated containers + """ + + from colorbleed.maya.lib import DEFAULT_MATRIX, get_container_transforms + from avalon.tools.cbloader import lib + from avalon import api + + set_namespace = set_container['namespace'] + + # Update the setdress hierarchy alembic + set_root = get_container_transforms(set_container, root=True) + set_hierarchy_root = cmds.listRelatives(set_root, fullPath=True)[0] + set_hierarchy_reference = cmds.referenceQuery(set_hierarchy_root, + referenceNode=True) + new_alembic = new_file.replace(".json", ".abc") + assert os.path.exists(new_alembic), "%s does not exist." % new_alembic + cmds.file(new_alembic, + loadReference=set_hierarchy_reference, + type="Alembic") + + identity = DEFAULT_MATRIX[:] + + processed_namespaces = set() + processed_containers = list() + + new_lookup = _instances_by_namespace(new_data) + old_lookup = _instances_by_namespace(current_data) + for container in containers: + # container_repr = cmds.getAttr("{}.representation".format(container)) + container_ns = container['namespace'] + + if container_ns in new_lookup: + root = get_container_transforms(container, root=True) + if not root: + log.error("Can't find root for %s", container['objectName']) + continue + + old_instance = old_lookup.get(container_ns, {}) + new_instance = new_lookup[container_ns] + + # Update the matrix + # check matrix against old_data matrix to find local overrides + current_matrix = cmds.xform(root, + query=True, + matrix=True, + objectSpace=True) + + original_matrix = old_instance.get("matrix", identity) + has_matrix_override = not matrix_equals(current_matrix, + original_matrix) + + if has_matrix_override: + log.warning("Matrix override preserved on %s", container_ns) + + else: + new_matrix = new_instance.get("matrix", identity) + cmds.xform(root, matrix=new_matrix, objectSpace=True) + + # Update the parenting + if old_instance.get("parent", None) != new_instance["parent"]: + + parent = to_namespace(new_instance['parent'], set_namespace) + if not cmds.objExists(parent): + log.error("Can't find parent %s", parent) + continue + + # Set the new parent + cmds.lockNode(root, lock=False) + root = cmds.parent(root, parent, relative=True) + cmds.lockNode(root, lock=True) + + # TODO: Update the representation with the new loader + if new_instance['loader'] != old_instance['loader']: + log.error("Switching loader between updates is not supported.") + continue + + # TODO: Update the representation with the new loader + representation_new = new_instance['representation'] + representation_old = old_instance['representation'] + if representation_new != representation_old: + + print "namespace :", container_ns + + new = io.find_one({"_id": io.ObjectId(representation_new)}) + old = io.find_one({"_id": io.ObjectId(representation_old)}) + + is_valid = compare_representations(old=old, new=new) + if not is_valid: + continue + + new_version = new["context"]["version"] + + host = api.registered_host() + host.update(container, version=new_version) + + else: + # Remove this container because it's not in the new data + log.warning("Removing content: %s", container_ns) + host = api.registered_host() + host.remove(container) + + processed_namespaces.add(container_ns) + processed_containers.append(container['objectName']) + + # Add new assets + for representation_id, instances in new_data.items(): + + # Find the compatible loaders + loaders = list(lib.iter_loaders(representation_id)) + + for instance in instances: + + # Already processed in update functionality + if instance['namespace'] in processed_namespaces: + continue + + container = _add(instance, + representation_id, + loaders, + set_container['name'], + set_container['namespace']) + + # Add to the setdress container + cmds.sets(container, + addElement=set_container['objectName']) + + processed_containers.append(container) + + return processed_containers + + +def compare_representations(old, new): + """Check if the old representation given can be updated + + Due to limitations of the `host.update` function we cannot allow + differences in the following data: + + * Representation name (extension) + * Asset name + * Subset name (variation) + + If any of those data values differs, the function will raise an + RuntimeError + + Args: + old(dict): representation data from the database + new(dict): representation data from the database + + Returns: + bool: False if the representation is not invalid else True + """ + + if new["name"] != old["name"]: + log.error("Cannot switch extensions") + return False + + new_context = new["context"] + old_context = old["context"] + + if new_context["asset"] != old_context["asset"]: + log.error("Changing assets between updates is " + "not supported.") + return False + + if new_context["subset"] != old_context["subset"]: + log.error("Changing subsets between updates is " + "not supported.") + return False + + return True diff --git a/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py b/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py new file mode 100644 index 0000000000..e69de29bb2 From f5eddfbfaf1504565ebf3f95c1df7dfa90e16c1c Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 20 Oct 2017 18:11:06 +0200 Subject: [PATCH 32/58] fixed wrong key --- colorbleed/maya/lib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index efb29ed35c..56191d880e 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -700,7 +700,7 @@ def get_representation_file(representation, template=TEMPLATE): """ context = representation["context"].copy() - context["root"] = os.environ["AVALON_ROOT"] + context["root"] = os.environ["AVALON_PROJECTS"] return template.format(**context) From afae0fa893828503c904737d095043a347ae4517 Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 20 Oct 2017 18:11:35 +0200 Subject: [PATCH 33/58] removed site.addsitedir, use import instead --- .../maya/load/load_setdress_rebuild.py | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index b8c84ef767..77be943708 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -1,9 +1,5 @@ -import site - from avalon import api -site.addsitedir(r"C:\Users\User\Documents\development\research\setdress") - class SetDressRebuild(api.Loader): @@ -17,18 +13,10 @@ class SetDressRebuild(api.Loader): def process(self, name, namespace, context, data): - # Hack - import sys + import setdress_api - p = r"C:\Users\User\Documents\development\research\setdress" - if p not in sys.path: - sys.path.insert(0, p) - - import loader - reload(loader) - - containers = loader.load_package(filepath=self.fname, - name=name, - namespace=namespace) + containers = setdress_api.load_package(filepath=self.fname, + name=name, + namespace=namespace) self[:] = containers From 3b9f78ea5c16dffe0e3a8601e0c8adc396de079f Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 20 Oct 2017 18:12:06 +0200 Subject: [PATCH 34/58] removed enumerate, unused --- colorbleed/plugins/maya/publish/collect_setdress.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index 52877ed8aa..e7ffa84a54 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -34,7 +34,7 @@ class CollectSetDress(pyblish.api.InstancePlugin): data = defaultdict(list) hierarchy_nodes = [] - for i, container in enumerate(containers): + for container in containers: members = cmds.sets(container["objectName"], query=True) transforms = lib.get_container_transforms(container, members) root = lib.get_container_transforms(container, From e7f7efad8cbc11ce9b33429d95bd78c62a992e4c Mon Sep 17 00:00:00 2001 From: aardschok Date: Fri, 20 Oct 2017 18:12:38 +0200 Subject: [PATCH 35/58] initial commit for ns validator --- .../publish/validate_setdress_namespaces.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py b/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py index e69de29bb2..09015faddd 100644 --- a/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py +++ b/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py @@ -0,0 +1,35 @@ +import pyblish.api +from collection import defaultdict + + +class ValidateSetdressNamespaces(pyblish.api.InstancePlugin): + """Ensure namespaces are not nested""" + + label = "Validate Setdress Namespaces" + order = pyblish.api.ValidatorOrder + families = ["colorbleed.setdress"] + + def process(self, instance): + + self.log.info("Checking namespace for %s", instance.name) + if self.get_invalid(instance): + self.log.error("Nested namespaces found") + + @classmethod + def get_invalid(cls, instance): + + from maya import cmds + + invalid = [] + + namspace_lookup = defaultdict(list) + for item in cmds.ls(instance): + namespace, node = item.rsplit(":", 1)[0] + namspace_lookup[namespace].append(node) + + for namespace, nodes in namspace_lookup.items(): + parts = [p for p in namespace.split(":") if p != ""] + if len(parts) > 1: + invalid.extend(nodes) + + return invalid \ No newline at end of file From 88ff47311efaa38b7b99033e5b1fae033524c421 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 20 Oct 2017 18:32:13 +0200 Subject: [PATCH 36/58] Implement gpuCache loader prototype --- colorbleed/plugins/maya/load/load_model.py | 86 +++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/load/load_model.py b/colorbleed/plugins/maya/load/load_model.py index ca54a2d41e..d2a05efd35 100644 --- a/colorbleed/plugins/maya/load/load_model.py +++ b/colorbleed/plugins/maya/load/load_model.py @@ -1,4 +1,5 @@ import avalon.maya.pipeline +import avalon.api class ModelLoader(avalon.maya.pipeline.ReferenceLoader): @@ -31,4 +32,87 @@ class ModelLoader(avalon.maya.pipeline.ReferenceLoader): groupReference=True, groupName="{}:{}".format(namespace, name)) - self[:] = nodes \ No newline at end of file + self[:] = nodes + + +class GpuCacheLoader(avalon.api.Loader): + """Load model Alembic as gpuCache""" + + families = ["colorbleed.model"] + representations = ["abc"] + + label = "Import Gpu Cache" + order = -5 + icon = "code-fork" + color = "orange" + + def load(self, context, name, namespace, data): + + import maya.cmds as cmds + import avalon.maya.lib as lib + + asset = context['asset']['name'] + subset = context['subset']['name'] + + name = name or subset + namespace = namespace or lib.unique_namespace( + asset + "_", + prefix="_" if asset[0].isdigit() else "", + suffix="_", + ) + + cmds.loadPlugin("gpuCache", quiet=True) + + # Root group + label = "{}:{}".format(namespace, name) + root = cmds.group(name=label, empty=True) + + # Create transform with shape + transform_name = label + "_GPU" + transform = cmds.createNode("transform", name=transform_name, + parent=root) + cache = cmds.createNode("gpuCache", + parent=transform, + name="{0}Shape".format(transform_name)) + + # Set the cache filepath + cmds.setAttr(cache + '.cacheFileName', self.fname, type="string") + cmds.setAttr(cache + '.cacheGeomPath', "|", type="string") # root + + # Lock parenting of the transform and cache + cmds.lockNode([transform, cache], lock=True) + + nodes = [root, transform, cache] + self[:] = nodes + + return avalon.maya.pipeline.containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) + + def update(self, container, representation): + + import maya.cmds as cmds + + path = avalon.api.get_representation_path(representation) + + # Update the cache + members = cmds.sets(container['objectName'], query=True) + caches = cmds.ls(members, type="gpuCache", long=True) + + assert len(caches) == 1, "This is a bug" + + for cache in caches: + cmds.setAttr(cache + ".cacheFileName", path, type="string") + + cmds.setAttr(container["objectName"] + ".representation", + str(representation["_id"]), + type="string") + + def remove(self, container): + import maya.cmds as cmds + members = cmds.sets(container['objectName'], query=True) + cmds.lockNode(members, lock=False) + cmds.delete([container['objectName']] + members) From 758b4a116f8588d998eac3361bc0173c33b3dd49 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 09:26:20 +0200 Subject: [PATCH 37/58] Move setdress_api.py so it can be loaded automatically --- colorbleed/plugins/maya/load/load_setdress_rebuild.py | 2 +- colorbleed/{plugins/maya/load => }/setdress_api.py | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename colorbleed/{plugins/maya/load => }/setdress_api.py (100%) diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py index 77be943708..7fd5dd30e4 100644 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ b/colorbleed/plugins/maya/load/load_setdress_rebuild.py @@ -13,7 +13,7 @@ class SetDressRebuild(api.Loader): def process(self, name, namespace, context, data): - import setdress_api + from colorbleed import setdress_api containers = setdress_api.load_package(filepath=self.fname, name=name, diff --git a/colorbleed/plugins/maya/load/setdress_api.py b/colorbleed/setdress_api.py similarity index 100% rename from colorbleed/plugins/maya/load/setdress_api.py rename to colorbleed/setdress_api.py From 76a6ce4ce6dba54ac157f557e8807b319f492162 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 09:29:57 +0200 Subject: [PATCH 38/58] Remove unused import --- colorbleed/setdress_api.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/colorbleed/setdress_api.py b/colorbleed/setdress_api.py index 88ac85c1c3..db5c9641aa 100644 --- a/colorbleed/setdress_api.py +++ b/colorbleed/setdress_api.py @@ -7,7 +7,6 @@ import copy from maya import cmds -import avalon.schema import avalon.io as io from avalon.maya.lib import unique_namespace @@ -210,7 +209,7 @@ def update_package(set_container, version): """ - from colorbleed.maya.lib import get_representation_file + import avalon.api from avalon.maya.pipeline import parse_container # Versioning (from `core.maya.pipeline`) @@ -244,13 +243,13 @@ def update_package(set_container, version): }) # Load the original package data - current_file = get_representation_file(current_representation) + current_file = avalon.api.get_representation_path(new_representation) assert current_file.endswith(".json") with open(current_file, "r") as fp: current_data = json.load(fp) # Load the new package data - new_file = get_representation_file(new_representation) + new_file = avalon.api.get_representation_path(new_representation) assert new_file.endswith(".json") with open(new_file, "r") as fp: new_data = json.load(fp) From 8449e10244190f227d5f911e64be010b5c373bf6 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 09:32:04 +0200 Subject: [PATCH 39/58] Refactor host.update and host.remove to api.update and api.remove --- colorbleed/setdress_api.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/colorbleed/setdress_api.py b/colorbleed/setdress_api.py index db5c9641aa..117fe240cd 100644 --- a/colorbleed/setdress_api.py +++ b/colorbleed/setdress_api.py @@ -382,14 +382,12 @@ def update_scene(set_container, containers, current_data, new_data, new_file): new_version = new["context"]["version"] - host = api.registered_host() - host.update(container, version=new_version) + api.update(container, version=new_version) else: # Remove this container because it's not in the new data log.warning("Removing content: %s", container_ns) - host = api.registered_host() - host.remove(container) + api.remove(container) processed_namespaces.add(container_ns) processed_containers.append(container['objectName']) @@ -424,7 +422,7 @@ def update_scene(set_container, containers, current_data, new_data, new_file): def compare_representations(old, new): """Check if the old representation given can be updated - Due to limitations of the `host.update` function we cannot allow + Due to limitations of the `api.update` function we cannot allow differences in the following data: * Representation name (extension) From 1ca84cb94ad799a62a9dc21f8369c2155234c577 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 12:05:16 +0200 Subject: [PATCH 40/58] Refactor loaders --- .../{load_animation.py => _load_animation.py} | 0 colorbleed/plugins/maya/load/load_alembic.py | 51 ++++++++++--------- 2 files changed, 26 insertions(+), 25 deletions(-) rename colorbleed/plugins/maya/load/{load_animation.py => _load_animation.py} (100%) diff --git a/colorbleed/plugins/maya/load/load_animation.py b/colorbleed/plugins/maya/load/_load_animation.py similarity index 100% rename from colorbleed/plugins/maya/load/load_animation.py rename to colorbleed/plugins/maya/load/_load_animation.py diff --git a/colorbleed/plugins/maya/load/load_alembic.py b/colorbleed/plugins/maya/load/load_alembic.py index 6cea82a832..b0b2b86101 100644 --- a/colorbleed/plugins/maya/load/load_alembic.py +++ b/colorbleed/plugins/maya/load/load_alembic.py @@ -1,7 +1,8 @@ from avalon import api +import avalon.maya.pipeline -class AnimationLoader(api.Loader): +class AbcLoader(api.Loader): """Specific loader of Alembic for the avalon.animation family""" families = ["colorbleed.animation", @@ -29,27 +30,27 @@ class AnimationLoader(api.Loader): self[:] = nodes -class SetDressAlembicLoader(api.Loader): - """Load the setdress as alembic""" - - families = ["colorbleed.setdress"] - label = "Reference Alembic" - representations = ["abc"] - order = -10 - icon = "code-fork" - color = "orange" - - def process(self, name, namespace, context, data): - - import maya.cmds as cmds - - cmds.loadPlugin("AbcImport.mll", quiet=True) - nodes = cmds.file(self.fname, - namespace=namespace, - sharedReferenceFile=False, - groupReference=True, - groupName="{}:{}".format(namespace, name), - reference=True, - returnNewNodes=True) - - self[:] = nodes +# class SetDressAlembicLoader(avalon.maya.pipeline.ReferenceLoader): +# """Load the setdress as alembic""" +# +# families = ["colorbleed.setdress"] +# label = "Reference Alembic" +# representations = ["abc"] +# order = -10 +# icon = "code-fork" +# color = "orange" +# +# def process(self, name, namespace, context, data): +# +# import maya.cmds as cmds +# +# cmds.loadPlugin("AbcImport.mll", quiet=True) +# nodes = cmds.file(self.fname, +# namespace=namespace, +# sharedReferenceFile=False, +# groupReference=True, +# groupName="{}:{}".format(namespace, name), +# reference=True, +# returnNewNodes=True) +# +# self[:] = nodes From 2b4d1a4ad70e4259d2caaa82d190e5aee77b6556 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 12:05:35 +0200 Subject: [PATCH 41/58] Simplify ModelLoader logic --- colorbleed/plugins/maya/load/load_model.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_model.py b/colorbleed/plugins/maya/load/load_model.py index d2a05efd35..e5d9dd960a 100644 --- a/colorbleed/plugins/maya/load/load_model.py +++ b/colorbleed/plugins/maya/load/load_model.py @@ -18,12 +18,6 @@ class ModelLoader(avalon.maya.pipeline.ReferenceLoader): import maya.cmds as cmds from avalon import maya - # Create a readable namespace - # Namespace should contain asset name and counter - # TEST_001{_descriptor} where `descriptor` can be `_abc` for example - assetname = "{}_".format(namespace.split("_")[0]) - namespace = maya.unique_namespace(assetname, format="%03d") - with maya.maintained_selection(): nodes = cmds.file(self.fname, namespace=namespace, @@ -52,9 +46,6 @@ class GpuCacheLoader(avalon.api.Loader): import avalon.maya.lib as lib asset = context['asset']['name'] - subset = context['subset']['name'] - - name = name or subset namespace = namespace or lib.unique_namespace( asset + "_", prefix="_" if asset[0].isdigit() else "", From 2ab4d0bdc219c04b323017a6ab1d55447043039d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 12:06:27 +0200 Subject: [PATCH 42/58] Fix setdress api and implement `load`, `update`, `remove` correctly on SetDressLoader --- colorbleed/plugins/maya/load/load_setdress.py | 81 +++++++++++++++++++ .../maya/load/load_setdress_rebuild.py | 22 ----- colorbleed/setdress_api.py | 79 ++++++++++++------ 3 files changed, 134 insertions(+), 48 deletions(-) create mode 100644 colorbleed/plugins/maya/load/load_setdress.py delete mode 100644 colorbleed/plugins/maya/load/load_setdress_rebuild.py diff --git a/colorbleed/plugins/maya/load/load_setdress.py b/colorbleed/plugins/maya/load/load_setdress.py new file mode 100644 index 0000000000..675e6b81a9 --- /dev/null +++ b/colorbleed/plugins/maya/load/load_setdress.py @@ -0,0 +1,81 @@ +from avalon import api + + +class SetDressLoader(api.Loader): + + families = ["colorbleed.setdress"] + representations = ["json"] + + label = "Load Set Dress" + order = -9 + icon = "code-fork" + color = "orange" + + def load(self, context, name, namespace, data): + + from avalon.maya.pipeline import containerise + from avalon.maya import lib + + asset = context['asset']['name'] + namespace = namespace or lib.unique_namespace( + asset + "_", + prefix="_" if asset[0].isdigit() else "", + suffix="_", + ) + + from colorbleed import setdress_api + + containers = setdress_api.load_package(filepath=self.fname, + name=name, + namespace=namespace) + + self[:] = containers + + # Only containerize if any nodes were loaded by the Loader + nodes = self[:] + if not nodes: + return + + return containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) + + def update(self, container, representation): + + from colorbleed import setdress_api + return setdress_api.update_package(container, + representation) + + def remove(self, container): + """Remove all sub containers""" + + from avalon import api + from colorbleed import setdress_api + import maya.cmds as cmds + + # Remove all members + member_containers = setdress_api.get_contained_containers(container) + for member_container in member_containers: + print 'member_container', member_container + self.log.info("Removing container %s", + member_container['objectName']) + api.remove(member_container) + + # Remove alembic hierarchy reference + # TODO: Check whether removing all contained references is safe enough + members = cmds.sets(container['objectName'], query=True) or [] + references = cmds.ls(members, type="reference") + for reference in references: + self.log.info("Removing %s", reference) + fname = cmds.referenceQuery(reference, filename=True) + cmds.file(fname, removeReference=True) + + # Delete container and its contents + if cmds.objExists(container['objectName']): + members = cmds.sets(container['objectName'], query=True) or [] + cmds.delete([container['objectName']] + members) + + # TODO: Ensure namespace is gone \ No newline at end of file diff --git a/colorbleed/plugins/maya/load/load_setdress_rebuild.py b/colorbleed/plugins/maya/load/load_setdress_rebuild.py deleted file mode 100644 index 7fd5dd30e4..0000000000 --- a/colorbleed/plugins/maya/load/load_setdress_rebuild.py +++ /dev/null @@ -1,22 +0,0 @@ -from avalon import api - - -class SetDressRebuild(api.Loader): - - families = ["colorbleed.setdress"] - representations = ["json"] - - label = "Rebuild Set Dress" - order = -9 - icon = "code-fork" - color = "orange" - - def process(self, name, namespace, context, data): - - from colorbleed import setdress_api - - containers = setdress_api.load_package(filepath=self.fname, - name=name, - namespace=namespace) - - self[:] = containers diff --git a/colorbleed/setdress_api.py b/colorbleed/setdress_api.py index 117fe240cd..cf775ee7ea 100644 --- a/colorbleed/setdress_api.py +++ b/colorbleed/setdress_api.py @@ -133,7 +133,7 @@ def _add(instance, representation_id, loaders, name, namespace): """ - from avalon.tools.cbloader import lib + import avalon.api as api from colorbleed.maya.lib import get_container_transforms # Process within the namespace @@ -149,9 +149,9 @@ def _add(instance, representation_id, loaders, name, namespace): instance['loader'], instance) raise RuntimeError("Loader is missing.") - container = lib.run_loader(Loader, - representation_id, - namespace=instance['namespace']) + container = api.load(Loader, + representation_id, + namespace=instance['namespace']) # Get the root from the loaded container root = get_container_transforms({"objectName": container}, @@ -200,21 +200,39 @@ def _instances_by_namespace(data): return result -def update_package(set_container, version): - """Update any matrix changes in the scene based on the new data - +def get_contained_containers(container): + """Get the Avalon containers in this container + Args: - set_container (dict): container data from `ls()` - version (int): version number of the subset - + container (dict): The container dict. + + Returns: + list: A list of member container dictionaries. + """ - import avalon.api + import avalon.schema from avalon.maya.pipeline import parse_container + # Get avalon containers in this package setdress container + containers = [] + members = cmds.sets(container['objectName'], query=True) + for node in cmds.ls(members, type="objectSet"): + try: + member_container = parse_container(node) + containers.append(member_container) + except avalon.schema.ValidationError: + pass + + return containers + + +def update_package_version(container, version): + """Update package by version number""" + # Versioning (from `core.maya.pipeline`) current_representation = io.find_one({ - "_id": io.ObjectId(set_container["representation"]) + "_id": io.ObjectId(container["representation"]) }) assert current_representation is not None, "This is a bug" @@ -242,34 +260,44 @@ def update_package(set_container, version): "name": current_representation["name"] }) + update_package(container, new_representation) + + +def update_package(set_container, representation): + """Update any matrix changes in the scene based on the new data + + Args: + set_container (dict): container data from `ls()` + version (int): version number of the subset + + """ + + import avalon.api + # Load the original package data - current_file = avalon.api.get_representation_path(new_representation) + current_representation = io.find_one({ + "_id": io.ObjectId(set_container['representation']), + "type": "representation" + }) + + current_file = avalon.api.get_representation_path(current_representation) assert current_file.endswith(".json") with open(current_file, "r") as fp: current_data = json.load(fp) # Load the new package data - new_file = avalon.api.get_representation_path(new_representation) + new_file = avalon.api.get_representation_path(representation) assert new_file.endswith(".json") with open(new_file, "r") as fp: new_data = json.load(fp) - # Get avalon containers in the setdress - containers = [] - members = cmds.sets(set_container['objectName'], query=True) - for node in cmds.ls(members, type="objectSet"): - try: - container = parse_container(node) - containers.append(container) - except avalon.schema.ValidationError: - pass - # Update scene content + containers = get_contained_containers(set_container) update_scene(set_container, containers, current_data, new_data, new_file) # TODO: This should be handled by the pipeline itself cmds.setAttr(set_container['objectName'] + ".representation", - str(new_representation['_id']), type="string") + str(representation['_id']), type="string") def update_scene(set_container, containers, current_data, new_data, new_file): @@ -318,7 +346,6 @@ def update_scene(set_container, containers, current_data, new_data, new_file): new_lookup = _instances_by_namespace(new_data) old_lookup = _instances_by_namespace(current_data) for container in containers: - # container_repr = cmds.getAttr("{}.representation".format(container)) container_ns = container['namespace'] if container_ns in new_lookup: From a0dced9981a332b944a4389e33d8459f52ca9691 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 12:25:36 +0200 Subject: [PATCH 43/58] Remove debugging print statement --- colorbleed/plugins/maya/load/load_setdress.py | 1 - 1 file changed, 1 deletion(-) diff --git a/colorbleed/plugins/maya/load/load_setdress.py b/colorbleed/plugins/maya/load/load_setdress.py index 675e6b81a9..8cc857fc3f 100644 --- a/colorbleed/plugins/maya/load/load_setdress.py +++ b/colorbleed/plugins/maya/load/load_setdress.py @@ -59,7 +59,6 @@ class SetDressLoader(api.Loader): # Remove all members member_containers = setdress_api.get_contained_containers(container) for member_container in member_containers: - print 'member_container', member_container self.log.info("Removing container %s", member_container['objectName']) api.remove(member_container) From 0057e5ed1a92c758d240628ba6d8bcaaf1367c9b Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 16:23:46 +0200 Subject: [PATCH 44/58] Fix issue where overrides on instance would add a new one on update, support parenting into hierarchy more solidly, support parenting during update when root is parented to something. --- colorbleed/setdress_api.py | 81 ++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/colorbleed/setdress_api.py b/colorbleed/setdress_api.py index cf775ee7ea..fc3a4319bf 100644 --- a/colorbleed/setdress_api.py +++ b/colorbleed/setdress_api.py @@ -92,6 +92,9 @@ def load_package(filepath, name, namespace=None): groupName="{}:{}".format(namespace, name), typ="Alembic") + # Get the top root node (the reference group) + root = "{}:{}".format(namespace, name) + containers = [] for representation_id, instances in data.items(): @@ -99,8 +102,11 @@ def load_package(filepath, name, namespace=None): loaders = list(lib.iter_loaders(representation_id)) for instance in instances: - container = _add(instance, representation_id, loaders, name, - namespace) + container = _add(instance=instance, + representation_id=representation_id, + loaders=loaders, + namespace=namespace, + root=root) containers.append(container) # TODO: Do we want to cripple? Or do we want to add a 'parent' parameter? @@ -119,7 +125,7 @@ def load_package(filepath, name, namespace=None): return containers + hierarchy -def _add(instance, representation_id, loaders, name, namespace): +def _add(instance, representation_id, loaders, namespace, root="|"): """Add an item from the package Args: @@ -154,21 +160,19 @@ def _add(instance, representation_id, loaders, name, namespace): namespace=instance['namespace']) # Get the root from the loaded container - root = get_container_transforms({"objectName": container}, - root=True) + loaded_root = get_container_transforms({"objectName": container}, + root=True) # Apply matrix to root node (if any matrix edits) matrix = instance.get("matrix", None) if matrix: - cmds.xform(root, objectSpace=True, matrix=matrix) + cmds.xform(loaded_root, objectSpace=True, matrix=matrix) # Parent into the setdress hierarchy # Namespace is missing from parent node(s), add namespace # manually - parent_grp = instance["parent"] - parent_grp = "{}:{}|".format(namespace, name) + to_namespace(parent_grp, namespace) - - cmds.parent(root, parent_grp, relative=True) + parent = root + to_namespace(instance["parent"], namespace) + cmds.parent(loaded_root, parent, relative=True) return container @@ -348,6 +352,11 @@ def update_scene(set_container, containers, current_data, new_data, new_file): for container in containers: container_ns = container['namespace'] + # Consider it processed here, even it it fails we want to store that + # the namespace was already available. + processed_namespaces.add(container_ns) + processed_containers.append(container['objectName']) + if container_ns in new_lookup: root = get_container_transforms(container, root=True) if not root: @@ -388,27 +397,42 @@ def update_scene(set_container, containers, current_data, new_data, new_file): root = cmds.parent(root, parent, relative=True) cmds.lockNode(root, lock=True) - # TODO: Update the representation with the new loader - if new_instance['loader'] != old_instance['loader']: - log.error("Switching loader between updates is not supported.") - continue - - # TODO: Update the representation with the new loader - representation_new = new_instance['representation'] + # Update the representation + representation_current = container['representation'] representation_old = old_instance['representation'] - if representation_new != representation_old: + representation_new = new_instance['representation'] + has_representation_override = (representation_current != + representation_old) - print "namespace :", container_ns + if representation_new != representation_current: + if has_representation_override: + log.warning("Your scene had local representation " + "overrides within the set. New " + "representations not loaded for %s.", + container_ns) + continue + + # We check it against the current 'loader' in the scene instead + # of the original data of the package that was loaded because + # an Artist might have made scene local overrides + if new_instance['loader'] != container['loader']: + log.error("Switching loader between updates is not " + "supported. Skipping: %s", container_ns) + continue + + # Check whether the conversion can be done by the Loader. + # They *must* use the same asset, subset and Loader for + # `api.update` to make sense. + old = io.find_one({"_id": io.ObjectId(representation_current)}) new = io.find_one({"_id": io.ObjectId(representation_new)}) - old = io.find_one({"_id": io.ObjectId(representation_old)}) - is_valid = compare_representations(old=old, new=new) if not is_valid: + log.error("Skipping: %s. See log for details.", + container_ns) continue new_version = new["context"]["version"] - api.update(container, version=new_version) else: @@ -416,9 +440,6 @@ def update_scene(set_container, containers, current_data, new_data, new_file): log.warning("Removing content: %s", container_ns) api.remove(container) - processed_namespaces.add(container_ns) - processed_containers.append(container['objectName']) - # Add new assets for representation_id, instances in new_data.items(): @@ -431,11 +452,11 @@ def update_scene(set_container, containers, current_data, new_data, new_file): if instance['namespace'] in processed_namespaces: continue - container = _add(instance, - representation_id, - loaders, - set_container['name'], - set_container['namespace']) + container = _add(instance=instance, + representation_id=representation_id, + loaders=loaders, + namespace=set_container['namespace'], + root=set_root) # Add to the setdress container cmds.sets(container, From 6905534738ebd78534dc09ceb226d989bbd6134d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 16:31:55 +0200 Subject: [PATCH 45/58] Improve logging order and log warning when instance is empty --- .../plugins/maya/publish/collect_instances.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_instances.py b/colorbleed/plugins/maya/publish/collect_instances.py index cfeb2d4098..c25856c12c 100644 --- a/colorbleed/plugins/maya/publish/collect_instances.py +++ b/colorbleed/plugins/maya/publish/collect_instances.py @@ -47,12 +47,6 @@ class CollectInstances(pyblish.api.ContextPlugin): objectset = cmds.ls("*.id", long=True, type="objectSet", recursive=True, objectsOnly=True) for objset in objectset: - self.log.info("Creating instance for {}".format(objset)) - - members = cmds.sets(objset, query=True) - if members is None: - self.log.info("Skipped empty Set: \"%s\" " % objset) - continue if not cmds.attributeQuery("id", node=objset, exists=True): continue @@ -68,6 +62,13 @@ class CollectInstances(pyblish.api.ContextPlugin): exists=True) assert has_family, "\"%s\" was missing a family" % objset + members = cmds.sets(objset, query=True) + if members is None: + self.log.warning("Skipped empty instance: \"%s\" " % objset) + continue + + self.log.info("Creating instance for {}".format(objset)) + data = dict() # Apply each user defined attribute as data From c77f305269b48949219eda51baba6a7d36800e13 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sat, 21 Oct 2017 23:52:46 +0200 Subject: [PATCH 46/58] Add icons and cleanup docstrings --- colorbleed/plugins/maya/create/colorbleed_animation.py | 3 ++- colorbleed/plugins/maya/create/colorbleed_camera.py | 3 ++- colorbleed/plugins/maya/create/colorbleed_look.py | 3 ++- colorbleed/plugins/maya/create/colorbleed_mayaascii.py | 3 ++- colorbleed/plugins/maya/create/colorbleed_model.py | 3 ++- colorbleed/plugins/maya/create/colorbleed_pointcache.py | 3 ++- colorbleed/plugins/maya/create/colorbleed_rig.py | 3 ++- colorbleed/plugins/maya/create/colorbleed_setdress.py | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) diff --git a/colorbleed/plugins/maya/create/colorbleed_animation.py b/colorbleed/plugins/maya/create/colorbleed_animation.py index 68f7ec2744..b559e15ec9 100644 --- a/colorbleed/plugins/maya/create/colorbleed_animation.py +++ b/colorbleed/plugins/maya/create/colorbleed_animation.py @@ -5,11 +5,12 @@ from colorbleed.maya import lib class CreateAnimation(avalon.maya.Creator): - """THe animated objects in the scene""" + """Animation output for character rigs""" name = "animationDefault" label = "Animation" family = "colorbleed.animation" + icon = "male" def __init__(self, *args, **kwargs): super(CreateAnimation, self).__init__(*args, **kwargs) diff --git a/colorbleed/plugins/maya/create/colorbleed_camera.py b/colorbleed/plugins/maya/create/colorbleed_camera.py index 0029380d34..94c1a82225 100644 --- a/colorbleed/plugins/maya/create/colorbleed_camera.py +++ b/colorbleed/plugins/maya/create/colorbleed_camera.py @@ -4,11 +4,12 @@ from colorbleed.maya import lib class CreateCamera(avalon.maya.Creator): - """Single baked camera extraction""" + """Single baked camera""" name = "cameraDefault" label = "Camera" family = "colorbleed.camera" + icon = "video-camera" def __init__(self, *args, **kwargs): super(CreateCamera, self).__init__(*args, **kwargs) diff --git a/colorbleed/plugins/maya/create/colorbleed_look.py b/colorbleed/plugins/maya/create/colorbleed_look.py index 9ac1cbe934..d5c0255360 100644 --- a/colorbleed/plugins/maya/create/colorbleed_look.py +++ b/colorbleed/plugins/maya/create/colorbleed_look.py @@ -4,11 +4,12 @@ from colorbleed.maya import lib class CreateLook(avalon.maya.Creator): - """Polygonal geometry for animation""" + """Shader connections defining shape look""" name = "look" label = "Look" family = "colorbleed.look" + icon = "paint-brush" def __init__(self, *args, **kwargs): super(CreateLook, self).__init__(*args, **kwargs) diff --git a/colorbleed/plugins/maya/create/colorbleed_mayaascii.py b/colorbleed/plugins/maya/create/colorbleed_mayaascii.py index 2c19e13711..d8cc9f9897 100644 --- a/colorbleed/plugins/maya/create/colorbleed_mayaascii.py +++ b/colorbleed/plugins/maya/create/colorbleed_mayaascii.py @@ -2,8 +2,9 @@ import avalon.maya class CreateMayaAscii(avalon.maya.Creator): - """Raw Maya Ascii file of the item(s)""" + """Raw Maya Ascii file export""" name = "mayaAscii" label = "Maya Ascii" family = "colorbleed.mayaAscii" + icon = "file-archive-o" diff --git a/colorbleed/plugins/maya/create/colorbleed_model.py b/colorbleed/plugins/maya/create/colorbleed_model.py index 364d00dc8d..b55b3dc3dd 100644 --- a/colorbleed/plugins/maya/create/colorbleed_model.py +++ b/colorbleed/plugins/maya/create/colorbleed_model.py @@ -2,8 +2,9 @@ import avalon.maya class CreateModel(avalon.maya.Creator): - """Polygonal geometry for animation""" + """Polygonal static geometry""" name = "modelDefault" label = "Model" family = "colorbleed.model" + icon = "cube" diff --git a/colorbleed/plugins/maya/create/colorbleed_pointcache.py b/colorbleed/plugins/maya/create/colorbleed_pointcache.py index 4dd5e11cde..c6a4edc5c1 100644 --- a/colorbleed/plugins/maya/create/colorbleed_pointcache.py +++ b/colorbleed/plugins/maya/create/colorbleed_pointcache.py @@ -5,11 +5,12 @@ from colorbleed.maya import lib class CreatePointCache(avalon.maya.Creator): - """Alembic extract""" + """Alembic pointcache for animated data""" name = "pointcache" label = "Point Cache" family = "colorbleed.pointcache" + icon = "gears" def __init__(self, *args, **kwargs): super(CreatePointCache, self).__init__(*args, **kwargs) diff --git a/colorbleed/plugins/maya/create/colorbleed_rig.py b/colorbleed/plugins/maya/create/colorbleed_rig.py index 3fa718d756..6947aaac31 100644 --- a/colorbleed/plugins/maya/create/colorbleed_rig.py +++ b/colorbleed/plugins/maya/create/colorbleed_rig.py @@ -4,11 +4,12 @@ import avalon.maya class CreateRig(avalon.maya.Creator): - """Skeleton and controls for manipulation of the geometry""" + """Artist-friendly rig with controls to direct motion""" name = "rigDefault" label = "Rig" family = "colorbleed.rig" + icon = "wheelchair" def process(self): instance = super(CreateRig, self).process() diff --git a/colorbleed/plugins/maya/create/colorbleed_setdress.py b/colorbleed/plugins/maya/create/colorbleed_setdress.py index fe0c64dd40..47089bea21 100644 --- a/colorbleed/plugins/maya/create/colorbleed_setdress.py +++ b/colorbleed/plugins/maya/create/colorbleed_setdress.py @@ -2,8 +2,9 @@ import avalon.maya class CreateSetDress(avalon.maya.Creator): - """THe animated objects in the scene""" + """A grouped package of loaded content""" name = "setdress" label = "Set Dress" family = "colorbleed.setdress" + icon = "cubes" \ No newline at end of file From 4396ec06bad0638712b4ac121415241e5ee8314c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 22 Oct 2017 00:05:52 +0200 Subject: [PATCH 47/58] Implement PLN-22 --- colorbleed/plugins/publish/integrate.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/colorbleed/plugins/publish/integrate.py b/colorbleed/plugins/publish/integrate.py index e26d45350b..07b416f3b0 100644 --- a/colorbleed/plugins/publish/integrate.py +++ b/colorbleed/plugins/publish/integrate.py @@ -50,7 +50,6 @@ class IntegrateAsset(pyblish.api.InstancePlugin): # Required environment variables PROJECT = os.environ["AVALON_PROJECT"] ASSET = instance.data.get("asset") or os.environ["AVALON_ASSET"] - SILO = os.environ["AVALON_SILO"] LOCATION = os.getenv("AVALON_LOCATION") context = instance.context @@ -140,7 +139,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): root = api.registered_root() template_data = {"root": root, "project": PROJECT, - "silo": SILO, + "silo": asset['silo'], "asset": ASSET, "subset": subset["name"], "version": version["name"]} @@ -214,7 +213,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "context": { "project": PROJECT, "asset": ASSET, - "silo": SILO, + "silo": asset['silo'], "subset": subset["name"], "version": version["name"], "representation": ext[1:] From 0f0479490e9c2ec4d782878ab35aa2321921239d Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Sun, 22 Oct 2017 00:34:27 +0200 Subject: [PATCH 48/58] Fix generate uuid action so it uses pipeline methods and applies the ids as required by the instance.data['asset'] (instead of current context asset) --- colorbleed/action.py | 52 ++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/colorbleed/action.py b/colorbleed/action.py index 5566518588..4913fe8518 100644 --- a/colorbleed/action.py +++ b/colorbleed/action.py @@ -142,7 +142,7 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): receive new UUIDs are actually invalid. Requires: - - currentFile on context + - instance.data["asset"] """ @@ -166,44 +166,38 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): instances = pyblish.api.instances_by_plugin(errored_instances, plugin) # Get the nodes from the all instances that ran through this plug-in - invalid = [] + all_invalid = [] for instance in instances: - invalid_nodes = plugin.get_invalid(instance) - if invalid_nodes: - invalid.extend(invalid_nodes) + invalid = plugin.get_invalid(instance) + if invalid: - if not invalid: + self.log.info("Fixing instance {}".format(instance.name)) + self._update_id_attribute(instance, invalid) + + all_invalid.extend(invalid) + + if not all_invalid: self.log.info("No invalid nodes found.") return - # Ensure unique ( process each node only once ) - invalid = list(set(invalid)) + all_invalid = list(set(all_invalid)) + self.log.info("Generated ids on nodes: {0}".format(all_invalid)) - # Parse context from current file - self.log.info("Updating node IDs ...") - # Update the attributes - self._update_id_attribute(invalid) - - self.log.info("Generated ids on nodes: {0}".format(invalid)) - - def _update_id_attribute(self, nodes): + def _update_id_attribute(self, instance, nodes): """Delete the id attribute Args: - nodes (list): all nodes to remove the attribute from + instance: The instance we're fixing for + nodes (list): all nodes to regenerate ids on """ + import colorbleed.maya.lib as lib + import avalon.io as io + + asset = instance.data['asset'] + asset_id = io.find_one({"name": asset, "type": "asset"}, + projection={"_id": True})['_id'] for node in nodes: - - # get the database asset id - attr = "{}.cbId".format(node) - id_attr = cmds.getAttr(attr) - asset_id = id_attr.split(":")[0] - - # create a new unique id - _, uid = str(uuid.uuid4()).rsplit("-", 1) - cb_uid = "{}:{}".format(asset_id, uid) - - # set the new id - cmds.setAttr(attr, cb_uid, type="string") + lib.remove_id(node) + lib.set_id(asset_id, node) From 8a516e7bb9195e0e6cba9b6fc0f336e6bbdb8d1a Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 10:09:27 +0200 Subject: [PATCH 49/58] cleaned code, removed line --- colorbleed/plugins/maya/publish/collect_setdress.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index e7ffa84a54..09282ef773 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -36,17 +36,13 @@ class CollectSetDress(pyblish.api.InstancePlugin): hierarchy_nodes = [] for container in containers: members = cmds.sets(container["objectName"], query=True) - transforms = lib.get_container_transforms(container, members) - root = lib.get_container_transforms(container, - transforms, - root=True) + root = lib.get_container_transforms(container, root=True) if root not in instance_lookup: continue # Retrieve all matrix data parent = cmds.listRelatives(root, parent=True, fullPath=True)[0] - relative_hierarchy = parent.replace(topnode, "*") - hierarchy_nodes.append(relative_hierarchy) + hierarchy_nodes.append(parent) # Gather info for new data entry reference_node = cmds.ls(members, type="reference")[0] From ec6c3f9dd4a826d82f14361b6d2a9eb195c935d4 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 10:14:49 +0200 Subject: [PATCH 50/58] clean up --- colorbleed/plugins/maya/publish/collect_setdress.py | 1 - 1 file changed, 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index 09282ef773..f1ec53a346 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -29,7 +29,6 @@ class CollectSetDress(pyblish.api.InstancePlugin): containers = amaya.ls() # Get all content from the instance - topnode = cmds.sets(instance.name, query=True)[0] instance_lookup = set(cmds.ls(instance, type="transform", long=True)) data = defaultdict(list) From cf24e3978f7f15cc4659201d989be281dfd6bdab Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 10:28:23 +0200 Subject: [PATCH 51/58] now selects only hierarchy nodes from collect_setdress --- colorbleed/plugins/maya/publish/extract_setdress.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/extract_setdress.py b/colorbleed/plugins/maya/publish/extract_setdress.py index d902c2073d..1d04358c83 100644 --- a/colorbleed/plugins/maya/publish/extract_setdress.py +++ b/colorbleed/plugins/maya/publish/extract_setdress.py @@ -33,7 +33,7 @@ class ExtractSetDress(colorbleed.api.Extractor): json.dump(instance.data["scenedata"], filepath, ensure_ascii=False) self.log.info("Extracting point cache ..") - cmds.select(cmds.ls(instance.data["hierarchy"], long=True)) + cmds.select(instance.data["hierarchy"]) # Run basic alembic exporter extract_alembic(file=hierarchy_path, From cb4e0323ef1b3b98e894eab33e7b69bf77bf51b5 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 10:51:12 +0200 Subject: [PATCH 52/58] removed old logic for get representation --- colorbleed/maya/lib.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 56191d880e..f2c92fc670 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -12,7 +12,7 @@ from collections import OrderedDict, defaultdict from maya import cmds, mel -from avalon import maya, io +from avalon import api, maya, io from cb.utils.maya import core @@ -688,22 +688,6 @@ def remove_id(node): cmds.deleteAttr("{}.cbId".format(node)) -def get_representation_file(representation, template=TEMPLATE): - """ - Rebuild the filepath of the representation's context - Args: - representation (dict): data of the registered in the database - template (str): the template to fill - - Returns: - str - - """ - context = representation["context"].copy() - context["root"] = os.environ["AVALON_PROJECTS"] - return template.format(**context) - - def get_reference_node(path): """ Get the reference node when the path is found being used in a reference @@ -813,8 +797,8 @@ def assign_look_by_version(nodes, version_id): "name": "json"}) # Load file - shader_filepath = get_representation_file(shader_file) - shader_relation = get_representation_file(shader_relations) + shader_filepath = api.get_representation_path(shader_file["_id"]) + shader_relation = api.get_representation_path(shader_relations["_id"]) reference_node = get_reference_node(shader_filepath) if reference_node is None: From 7197049643b790f5c202036da714841b4028edd7 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 11:15:59 +0200 Subject: [PATCH 53/58] fixed bug in representation reqeust --- colorbleed/maya/lib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index f2c92fc670..37d493f816 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -797,8 +797,8 @@ def assign_look_by_version(nodes, version_id): "name": "json"}) # Load file - shader_filepath = api.get_representation_path(shader_file["_id"]) - shader_relation = api.get_representation_path(shader_relations["_id"]) + shader_filepath = api.get_representation_path(shader_file) + shader_relation = api.get_representation_path(shader_relations) reference_node = get_reference_node(shader_filepath) if reference_node is None: From e30d14bc4d5cde8f21d84ebc192d7517c97503db Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 11:34:07 +0200 Subject: [PATCH 54/58] added support for ither model types --- colorbleed/plugins/maya/publish/collect_setdress.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index f1ec53a346..e019183041 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -2,7 +2,7 @@ from collections import defaultdict import pyblish.api from maya import cmds, mel -from avalon import maya as amaya +from avalon import maya as avalon from colorbleed.maya import lib # TODO : Publish of setdress: -unique namespace for all assets, VALIDATOR! @@ -26,7 +26,7 @@ class CollectSetDress(pyblish.api.InstancePlugin): def process(self, instance): # Find containers - containers = amaya.ls() + containers = avalon.ls() # Get all content from the instance instance_lookup = set(cmds.ls(instance, type="transform", long=True)) @@ -34,7 +34,7 @@ class CollectSetDress(pyblish.api.InstancePlugin): hierarchy_nodes = [] for container in containers: - members = cmds.sets(container["objectName"], query=True) + root = lib.get_container_transforms(container, root=True) if root not in instance_lookup: continue @@ -44,13 +44,10 @@ class CollectSetDress(pyblish.api.InstancePlugin): hierarchy_nodes.append(parent) # Gather info for new data entry - reference_node = cmds.ls(members, type="reference")[0] - namespace = cmds.referenceQuery(reference_node, namespace=True) representation_id = container["representation"] - instance_data = {"loader": container["loader"], "parent": parent, - "namespace": namespace.strip(":")} + "namespace": container["namespace"]} # Check if matrix differs from default and store changes matrix_data = self.get_matrix_data(root) From 133524880e601511fbcdc91ed68fc4bd2104a823 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 12:24:57 +0200 Subject: [PATCH 55/58] added warning for gpu caches, no full support yet --- .../plugins/maya/publish/collect_setdress.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_setdress.py b/colorbleed/plugins/maya/publish/collect_setdress.py index e019183041..0f405d5cf3 100644 --- a/colorbleed/plugins/maya/publish/collect_setdress.py +++ b/colorbleed/plugins/maya/publish/collect_setdress.py @@ -17,6 +17,10 @@ class CollectSetDress(pyblish.api.InstancePlugin): * Compatible loader * Matrix per instance * Namespace + + Note: GPU caches are currently not supported in the pipeline. There is no + logic yet which supports the swapping of GPU cache to renderable objects. + """ order = pyblish.api.CollectorOrder + 0.49 @@ -39,13 +43,19 @@ class CollectSetDress(pyblish.api.InstancePlugin): if root not in instance_lookup: continue - # Retrieve all matrix data + # Retrieve the hierarchy parent = cmds.listRelatives(root, parent=True, fullPath=True)[0] hierarchy_nodes.append(parent) + # Temporary warning for GPU cache which are not supported yet + loader = container["loader"] + if loader == "GpuCacheLoader": + self.log.warning("GPU Cache Loader is currently not supported" + "in the pipeline, we will export it tho") + # Gather info for new data entry representation_id = container["representation"] - instance_data = {"loader": container["loader"], + instance_data = {"loader": loader, "parent": parent, "namespace": container["namespace"]} From cf5a0cc8ce0b6da5939542b6f75815154987a839 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 12:25:21 +0200 Subject: [PATCH 56/58] moved import of avalon api to the top --- colorbleed/setdress_api.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/colorbleed/setdress_api.py b/colorbleed/setdress_api.py index fc3a4319bf..b2619ff27a 100644 --- a/colorbleed/setdress_api.py +++ b/colorbleed/setdress_api.py @@ -7,6 +7,7 @@ import copy from maya import cmds +from avalon import api import avalon.io as io from avalon.maya.lib import unique_namespace @@ -139,7 +140,6 @@ def _add(instance, representation_id, loaders, namespace, root="|"): """ - import avalon.api as api from colorbleed.maya.lib import get_container_transforms # Process within the namespace @@ -276,21 +276,19 @@ def update_package(set_container, representation): """ - import avalon.api - # Load the original package data current_representation = io.find_one({ "_id": io.ObjectId(set_container['representation']), "type": "representation" }) - current_file = avalon.api.get_representation_path(current_representation) + current_file = api.get_representation_path(current_representation) assert current_file.endswith(".json") with open(current_file, "r") as fp: current_data = json.load(fp) # Load the new package data - new_file = avalon.api.get_representation_path(representation) + new_file = api.get_representation_path(representation) assert new_file.endswith(".json") with open(new_file, "r") as fp: new_data = json.load(fp) @@ -327,7 +325,6 @@ def update_scene(set_container, containers, current_data, new_data, new_file): from colorbleed.maya.lib import DEFAULT_MATRIX, get_container_transforms from avalon.tools.cbloader import lib - from avalon import api set_namespace = set_container['namespace'] From 26f4c22bc9b0495bc6bc51595db77a4c7b7c18a1 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 12:32:34 +0200 Subject: [PATCH 57/58] updated docstrings --- colorbleed/setdress_api.py | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/colorbleed/setdress_api.py b/colorbleed/setdress_api.py index b2619ff27a..a444fad57a 100644 --- a/colorbleed/setdress_api.py +++ b/colorbleed/setdress_api.py @@ -14,9 +14,21 @@ from avalon.maya.lib import unique_namespace log = logging.getLogger("PackageLoader") -def matrix_equals(a, b, tolerance=1e-10): - """Compares two matrices with an imperfection tolerance""" - if not all(abs(x - y) < tolerance for x, y in zip(a, b)): +def matrix_equals(current_matrix, original_matrix, tolerance=1e-10): + """ + Compares two matrices with an imperfection tolerance + + Args: + current_matrix (list, tuple): the matrix to check + original_matrix (list, tuple): the matrix to check against + tolerance (long): the precision of the differences + + Returns: + bool : True or False + + """ + zipped = zip(current_matrix, original_matrix) + if not all(abs(x - y) < tolerance for x, y in zipped): return False return True @@ -232,7 +244,17 @@ def get_contained_containers(container): def update_package_version(container, version): - """Update package by version number""" + """ + Update package by version number + + Args: + container (dict): container data of the container node + version (int): the new version number of the package + + Returns: + None + + """ # Versioning (from `core.maya.pipeline`) current_representation = io.find_one({ @@ -272,7 +294,10 @@ def update_package(set_container, representation): Args: set_container (dict): container data from `ls()` - version (int): version number of the subset + representation (dict): the representation document from the database + + Returns: + None """ @@ -321,6 +346,7 @@ def update_scene(set_container, containers, current_data, new_data, new_file): Returns: processed_containers (list): all new and updated containers + """ from colorbleed.maya.lib import DEFAULT_MATRIX, get_container_transforms From 96e3e04e6c075c04bb7697d5de6ed07b8ef22b08 Mon Sep 17 00:00:00 2001 From: aardschok Date: Mon, 23 Oct 2017 12:51:58 +0200 Subject: [PATCH 58/58] added unlocked context manager for update --- colorbleed/setdress_api.py | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/colorbleed/setdress_api.py b/colorbleed/setdress_api.py index a444fad57a..734f130df0 100644 --- a/colorbleed/setdress_api.py +++ b/colorbleed/setdress_api.py @@ -73,6 +73,28 @@ def namespaced(namespace, new=True): cmds.namespace(set=original) +@contextlib.contextmanager +def unlocked(nodes): + + # Get node state by Maya's uuid + nodes = cmds.ls(nodes, long=True) + uuids = cmds.ls(nodes, uuid=True) + states = cmds.lockNode(nodes, query=True, lock=True) + states = {uuid: state for uuid, state in zip(uuids, states)} + + try: + cmds.lockNode(nodes, lock=False) + yield + finally: + # Reapply original states + for uuid, state in states.iteritems(): + nodes_from_id = cmds.ls(uuid, long=True) + if not nodes_from_id: + log.warning("Node not found: %s", uuid) + continue + cmds.lockNode(nodes_from_id[0], lock=state) + + def load_package(filepath, name, namespace=None): """Load a package that was gathered elsewhere. @@ -361,9 +383,10 @@ def update_scene(set_container, containers, current_data, new_data, new_file): referenceNode=True) new_alembic = new_file.replace(".json", ".abc") assert os.path.exists(new_alembic), "%s does not exist." % new_alembic - cmds.file(new_alembic, - loadReference=set_hierarchy_reference, - type="Alembic") + with unlocked(cmds.listRelatives(set_root, ad=True, fullPath=True)): + cmds.file(new_alembic, + loadReference=set_hierarchy_reference, + type="Alembic") identity = DEFAULT_MATRIX[:]