From 822fa2c745132861b97d64a7b60f51807af7e83c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 13 Sep 2018 10:50:58 +0200 Subject: [PATCH 001/121] Added collect animation plugin --- .../houdini/publish/collection_animation.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/collection_animation.py diff --git a/colorbleed/plugins/houdini/publish/collection_animation.py b/colorbleed/plugins/houdini/publish/collection_animation.py new file mode 100644 index 0000000000..a959d9d5c6 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/collection_animation.py @@ -0,0 +1,30 @@ +import pyblish.api + + +class CollectAnimation(pyblish.api.InstancePlugin): + """Collect the animation data for the data base + + Data collected: + - start frame + - end frame + - nr of steps + + """ + + label = "Collect Animation" + families = ["colorbleed.pointcache"] + + def process(self, instance): + + node = instance[0] + + # Get animation parameters for data + parameters = {"f1": "startFrame", + "f2": "endFrame", + "f3": "steps"} + + data = {} + for par, translation in parameters.items(): + data[translation] = node.parm(par).eval() + + instance.data.update(data) From b494ce0041eecf92eb270d204276933ccaf0f845 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 13 Sep 2018 10:54:39 +0200 Subject: [PATCH 002/121] Allow node settings to dictate output settings --- colorbleed/plugins/houdini/publish/extract_alembic.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/extract_alembic.py b/colorbleed/plugins/houdini/publish/extract_alembic.py index 098020b905..f8ae69a635 100644 --- a/colorbleed/plugins/houdini/publish/extract_alembic.py +++ b/colorbleed/plugins/houdini/publish/extract_alembic.py @@ -19,15 +19,14 @@ class ExtractAlembic(colorbleed.api.Extractor): file_name = "{}.abc".format(instance.data["subset"]) tmp_filepath = os.path.join(staging_dir, file_name) - start_frame = float(instance.data["startFrame"]) - end_frame = float(instance.data["endFrame"]) - ropnode = instance[0] - attributes = {"filename": tmp_filepath, - "trange": 2} + # Set file name to staging dir + file name + attributes = {"filename": tmp_filepath} + + # We run the render with the input settings set by the user with lib.attribute_values(ropnode, attributes): - ropnode.render(frame_range=(start_frame, end_frame, 1)) + ropnode.render() if "files" not in instance.data: instance.data["files"] = [] From 940e0b3458dc65ff4f1d271791bb20b32c0a0ae5 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 13 Sep 2018 10:56:31 +0200 Subject: [PATCH 003/121] Set extra parameters --- .../plugins/houdini/create/create_pointcache.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 93cd83a6b3..4995912d8e 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -1,7 +1,5 @@ from collections import OrderedDict -import hou - from avalon import houdini @@ -22,9 +20,9 @@ class CreatePointCache(houdini.Creator): # Set node type to create for output data["node_type"] = "alembic" - # Collect animation data for point cache exporting - start, end = hou.playbar.timelineRange() - data["startFrame"] = start - data["endFrame"] = end - self.data = data + + def process(self): + instance = super(CreatePointCache, self).process() + instance.setParms({"build_from_path": 1, + "path_attrib": "path"}) From 390ccb4a094b03532fcd78f93ad97f790c65a945 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Sep 2018 14:47:08 +0200 Subject: [PATCH 004/121] REN-52: Ensure no default camera is renderable --- .../validate_render_no_default_cameras.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py diff --git a/colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py b/colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py new file mode 100644 index 0000000000..4f55670a8c --- /dev/null +++ b/colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py @@ -0,0 +1,40 @@ +from maya import cmds + +import pyblish.api +import colorbleed.api +import colorbleed.maya.lib as lib + + +class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin): + """Ensure no default (startup) cameras are to be rendered.""" + + order = colorbleed.api.ValidateContentsOrder + hosts = ['maya'] + families = ['colorbleed.renderlayer'] + label = "No Default Cameras Renderable" + actions = [colorbleed.api.SelectInvalidAction] + + @staticmethod + def get_invalid(instance): + + layer = instance.data["setMembers"] + + # Collect default cameras + cameras = cmds.ls(type='camera', long=True) + defaults = [cam for cam in cameras if + cmds.camera(cam, query=True, startupCamera=True)] + + invalid = [] + with lib.renderlayer(layer): + for cam in defaults: + if cmds.getAttr(cam + ".renderable"): + invalid.append(cam) + + return invalid + + def process(self, instance): + """Process all the cameras in the instance""" + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Renderable default cameras " + "found: {0}".format(invalid)) From 147b3320c069a82f183080e1a70098ddf6269a92 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Sep 2018 14:56:28 +0200 Subject: [PATCH 005/121] Validate only a single camera is renderable in a layer --- .../publish/validate_render_single_camera.py | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 colorbleed/plugins/maya/publish/validate_render_single_camera.py diff --git a/colorbleed/plugins/maya/publish/validate_render_single_camera.py b/colorbleed/plugins/maya/publish/validate_render_single_camera.py new file mode 100644 index 0000000000..e45f5f186c --- /dev/null +++ b/colorbleed/plugins/maya/publish/validate_render_single_camera.py @@ -0,0 +1,48 @@ +from maya import cmds + +import pyblish.api +import colorbleed.api +import colorbleed.maya.lib as lib + + +class ValidateRenderSingleCamera(pyblish.api.InstancePlugin): + """Only one camera may be renderable in a layer. + + Currently the pipeline supports only a single camera per layer. + This is because when multiple cameras are rendered the output files + automatically get different names because the render token + is not in the output path. As such the output files conflict with how + our pipeline expects the output. + + """ + + order = colorbleed.api.ValidateContentsOrder + hosts = ['maya'] + families = ['colorbleed.renderlayer'] + label = "Render Single Camera" + actions = [colorbleed.api.SelectInvalidAction] + + @staticmethod + def get_invalid(instance): + + layer = instance.data["setMembers"] + + cameras = cmds.ls(type='camera', long=True) + + with lib.renderlayer(layer): + renderable = [cam for cam in cameras if + cmds.getAttr(cam + ".renderable")] + + if len(renderable) == 0: + raise RuntimeError("No renderable cameras found.") + elif len(renderable) > 1: + return renderable + else: + return [] + + def process(self, instance): + """Process all the cameras in the instance""" + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Multiple renderable cameras" + "found: {0}".format(invalid)) From 559e910190c6302da292a80af485d2f8760cc39c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 13 Sep 2018 15:31:06 +0200 Subject: [PATCH 006/121] Improved flexibility, assume sop path points to output node --- .../houdini/publish/validate_outnode_exists.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/validate_outnode_exists.py b/colorbleed/plugins/houdini/publish/validate_outnode_exists.py index 479579a8f0..011a1f63e7 100644 --- a/colorbleed/plugins/houdini/publish/validate_outnode_exists.py +++ b/colorbleed/plugins/houdini/publish/validate_outnode_exists.py @@ -29,13 +29,21 @@ class ValidatOutputNodeExists(pyblish.api.InstancePlugin): result = set() node = instance[0] - sop_path = node.parm("sop_path").eval() - if not sop_path.endswith("OUT"): - cls.log.error("SOP Path does not end path at output node") - result.add(node.path()) + if node.type().name() == "alembic": + soppath_parm = "sop_path" + else: + # Fall back to geometry node + soppath_parm = "soppath" - if hou.node(sop_path) is None: + sop_path = node.parm(soppath_parm).eval() + output_node = hou.node(sop_path) + + if output_node is None: cls.log.error("Node at '%s' does not exist" % sop_path) result.add(node.path()) + if output_node.type().name() != "output": + cls.log.error("SOP Path does not end path at output node") + result.add(node.path()) + return result From 774ff4bf0d83b375fa729977e7c3c3433c3c125c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Sep 2018 15:39:56 +0200 Subject: [PATCH 007/121] Validate FPS on_open() after update_task_from_path --- colorbleed/maya/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/colorbleed/maya/__init__.py b/colorbleed/maya/__init__.py index 5463cb0097..3f75ffd872 100644 --- a/colorbleed/maya/__init__.py +++ b/colorbleed/maya/__init__.py @@ -128,12 +128,13 @@ def on_open(_): from avalon.vendor.Qt import QtWidgets from ..widgets import popup - # Ensure scene's FPS is set to project config - lib.validate_fps() - # Update current task for the current scene update_task_from_path(cmds.file(query=True, sceneName=True)) + # Validate FPS after update_task_from_path to + # ensure it is using correct FPS for the asset + lib.validate_fps() + if any_outdated(): log.warning("Scene has outdated content.") From e9ec64b7ac9f5746ef5cdad8992744ff5ed0a2cb Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 13 Sep 2018 16:05:00 +0200 Subject: [PATCH 008/121] Added instnace for vdb --- .../houdini/create/create_vbd_cache.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 colorbleed/plugins/houdini/create/create_vbd_cache.py diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py new file mode 100644 index 0000000000..0ba9431403 --- /dev/null +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -0,0 +1,23 @@ +from collections import OrderedDict + +from avalon import houdini + + +class CreateVDBCache(houdini.Creator): + """Alembic pointcache for animated data""" + + name = "vbdcache" + label = "VDB Cache" + family = "colorbleed.vbdcache" + icon = "cloud" + + def __init__(self, *args, **kwargs): + super(CreateVDBCache, self).__init__(*args, **kwargs) + + # create an ordered dict with the existing data first + data = OrderedDict(**self.data) + + # Set node type to create for output + data["node_type"] = "geometry" + + self.data = data From be890bf95af61ee0481a0e8320e8b1bf7431eca9 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 13 Sep 2018 16:51:45 +0200 Subject: [PATCH 009/121] Refactored plugins to match Creator logic --- .../plugins/houdini/create/create_pointcache.py | 13 +++++++++++-- .../plugins/houdini/create/create_vbd_cache.py | 9 +++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 4995912d8e..9169135245 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -24,5 +24,14 @@ class CreatePointCache(houdini.Creator): def process(self): instance = super(CreatePointCache, self).process() - instance.setParms({"build_from_path": 1, - "path_attrib": "path"}) + + parms = {"build_from_path": 1, + "path_attrib": "path", + "use_sop_path": True, + "filename": "$HIP/%s.abc" % self.name} + + if self.nodes: + node = self.nodes[0] + parms.update({"sop_path": "%s/OUT" % node.path()}) + + instance.setParms(parms) diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index 0ba9431403..c76ebd05c1 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -21,3 +21,12 @@ class CreateVDBCache(houdini.Creator): data["node_type"] = "geometry" self.data = data + + def process(self): + instance = super(CreateVDBCache, self).process() + + parms = {} + if self.nodes: + parms.update({"soppath": self.nodes[0].path()}) + + instance.setParms() From cb63ec54c572137c5a9ee9af13a5ebe0ebf031f6 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 13 Sep 2018 16:52:55 +0200 Subject: [PATCH 010/121] Refactored class name --- colorbleed/plugins/houdini/publish/collect_current_file.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/collect_current_file.py b/colorbleed/plugins/houdini/publish/collect_current_file.py index e8612bdc12..7852943b34 100644 --- a/colorbleed/plugins/houdini/publish/collect_current_file.py +++ b/colorbleed/plugins/houdini/publish/collect_current_file.py @@ -3,7 +3,7 @@ import hou import pyblish.api -class CollectMayaCurrentFile(pyblish.api.ContextPlugin): +class CollectHoudiniCurrentFile(pyblish.api.ContextPlugin): """Inject the current working file into context""" order = pyblish.api.CollectorOrder - 0.5 From bc48a8df2bf920f46a8b45c85c1ed39c2ba748da Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Sep 2018 17:55:27 +0200 Subject: [PATCH 011/121] Set nice pyblish instance name with frame range in Pyblish --- colorbleed/plugins/maya/publish/collect_renderlayers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/collect_renderlayers.py b/colorbleed/plugins/maya/publish/collect_renderlayers.py index 907b9367f5..48f7fe113c 100644 --- a/colorbleed/plugins/maya/publish/collect_renderlayers.py +++ b/colorbleed/plugins/maya/publish/collect_renderlayers.py @@ -103,7 +103,12 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin): overrides = self.parse_options(render_globals) data.update(**overrides) - instance = context.create_instance(layername) + # Define nice label + label = "{0} ({1})".format(layername, data["asset"]) + label += " [{0}-{1}]".format(int(data["startFrame"]), + int(data["endFrame"])) + + instance = context.create_instance(label) instance.data.update(data) def get_render_attribute(self, attr): From 1bfff0a74aef0ecb037b528b538341938a2dc6fd Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Sep 2018 17:56:17 +0200 Subject: [PATCH 012/121] Fix getting AOVs code --- .../maya/publish/collect_render_layer_aovs.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py b/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py index 19ab891004..16039fe5e4 100644 --- a/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py +++ b/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py @@ -41,7 +41,7 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin): self.log.info("Renderer found: {}".format(renderer)) - rp_node_types = {"vray": "VRayRenderElement", + rp_node_types = {"vray": ("VRayRenderElement", "VRayRenderElementSet"), "arnold": "aiAOV", "redshift": "RedshiftAOV"} @@ -52,7 +52,8 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin): result = [] # Collect all AOVs / Render Elements - with lib.renderlayer(instance.name): + layer = instance.data["setMembers"] + with lib.renderlayer(layer): node_type = rp_node_types[renderer] render_elements = cmds.ls(type=node_type) @@ -64,32 +65,36 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin): continue pass_name = self.get_pass_name(renderer, element) - render_pass = "%s.%s" % (instance.name, pass_name) + render_pass = "%s.%s" % (instance.data["subset"], pass_name) result.append(render_pass) self.log.info("Found {} render elements / AOVs for " - "'{}'".format(len(result), instance.name)) + "'{}'".format(len(result), instance.data["subset"])) instance.data["renderPasses"] = result def get_pass_name(self, renderer, node): if renderer == "vray": + + # Get render element pass type vray_node_attr = next(attr for attr in cmds.listAttr(node) if attr.startswith("vray_name")) - pass_type = vray_node_attr.rsplit("_", 1)[-1] + + # Support V-Ray extratex explicit name (if set by user) if pass_type == "extratex": - vray_node_attr = "vray_explicit_name_extratex" + explicit_attr = "{}.vray_explicit_name_extratex".format(node) + explicit_name = cmds.getAttr(explicit_attr) + if explicit_name: + return explicit_name # Node type is in the attribute name but we need to check if value # of the attribute as it can be changed - pass_name = cmds.getAttr("{}.{}".format(node, vray_node_attr)) + return cmds.getAttr("{}.{}".format(node, vray_node_attr)) elif renderer in ["arnold", "redshift"]: - pass_name = cmds.getAttr("{}.name".format(node)) + return cmds.getAttr("{}.name".format(node)) else: raise RuntimeError("Unsupported renderer: '{}'".format(renderer)) - - return pass_name \ No newline at end of file From 4d763157d3f85cc5fe27465d364290e36e114b9f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Sep 2018 18:01:10 +0200 Subject: [PATCH 013/121] Fix renderlayer name (since nice label change instance.name) --- colorbleed/plugins/maya/publish/submit_deadline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/submit_deadline.py b/colorbleed/plugins/maya/publish/submit_deadline.py index 5e2c3334ff..dbb987a603 100644 --- a/colorbleed/plugins/maya/publish/submit_deadline.py +++ b/colorbleed/plugins/maya/publish/submit_deadline.py @@ -115,7 +115,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): scene = os.path.splitext(filename)[0] dirname = os.path.join(workspace, "renders") renderlayer = instance.data['setMembers'] # rs_beauty - renderlayer_name = instance.name # beauty + renderlayer_name = instance.data['subset'] # beauty renderlayer_globals = instance.data["renderGlobals"] legacy_layers = renderlayer_globals["UseLegacyRenderLayers"] deadline_user = context.data.get("deadlineUser", getpass.getuser()) From b2ca58723f28b457349eca28f282963221ea9b68 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 13 Sep 2018 18:17:54 +0200 Subject: [PATCH 014/121] Fix FPS check for asset overridden values (and simplify getting required asset fps) --- colorbleed/lib.py | 20 ++++++++++++++----- colorbleed/maya/lib.py | 3 +-- .../maya/publish/validate_maya_units.py | 11 +++++----- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index 119dcd4c25..bc98cf0cc5 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -252,7 +252,7 @@ def collect_container_metadata(container): return hostlib.get_additional_data(container) -def get_project_fps(): +def get_asset_fps(): """Returns project's FPS, if not found will return 25 by default Returns: @@ -260,10 +260,20 @@ def get_project_fps(): """ - data = get_project_data() - fps = data.get("fps", 25.0) + key = "fps" - return fps + # FPS from asset data (if set) + asset_data = get_asset_data() + if key in asset_data: + return asset_data[key] + + # FPS from project data (if set) + project_data = get_project_data() + if key in project_data: + return project_data[key] + + # Fallback to 25 FPS + return 25.0 def get_project_data(): @@ -298,7 +308,7 @@ def get_asset_data(asset=None): Returns: dict """ - + asset_name = asset or avalon.api.Session["AVALON_ASSET"] document = io.find_one({"name": asset_name, "type": "asset"}) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 59ea54c20d..b1351cc19c 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1472,8 +1472,7 @@ def validate_fps(): """ - asset_data = lib.get_asset_data() - fps = asset_data.get("fps", lib.get_project_fps()) # can be int or float + fps = lib.get_asset_fps() current_fps = mel.eval('currentTimeUnitToFPS()') # returns float if current_fps != fps: diff --git a/colorbleed/plugins/maya/publish/validate_maya_units.py b/colorbleed/plugins/maya/publish/validate_maya_units.py index 6548e8b6c3..04db95fdde 100644 --- a/colorbleed/plugins/maya/publish/validate_maya_units.py +++ b/colorbleed/plugins/maya/publish/validate_maya_units.py @@ -16,11 +16,12 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): def process(self, context): + # Collected units linearunits = context.data('linearUnits') angularunits = context.data('angularUnits') - fps = context.data['fps'] - project_fps = lib.get_project_fps() + + asset_fps = lib.get_asset_fps() self.log.info('Units (linear): {0}'.format(linearunits)) self.log.info('Units (angular): {0}'.format(angularunits)) @@ -32,7 +33,7 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): assert angularunits and angularunits == 'deg', ("Scene angular units " "must be degrees") - assert fps and fps == project_fps, "Scene must be %s FPS" % project_fps + assert fps and fps == asset_fps, "Scene must be %s FPS" % asset_fps @classmethod def repair(cls): @@ -49,5 +50,5 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): cls.log.debug(current_linear) cls.log.info("Setting time unit to match project") - project_fps = lib.get_project_fps() - mayalib.set_scene_fps(project_fps) + asset_fps = lib.get_asset_fps() + mayalib.set_scene_fps(asset_fps) From 7e424816f97e41f3a89cedc177818e51d459c4e7 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 09:38:16 +0200 Subject: [PATCH 015/121] Removed staging dir to support large write cache --- .../plugins/houdini/publish/extract_alembic.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/extract_alembic.py b/colorbleed/plugins/houdini/publish/extract_alembic.py index f8ae69a635..29450d0824 100644 --- a/colorbleed/plugins/houdini/publish/extract_alembic.py +++ b/colorbleed/plugins/houdini/publish/extract_alembic.py @@ -2,7 +2,6 @@ import os import pyblish.api import colorbleed.api -from colorbleed.houdini import lib class ExtractAlembic(colorbleed.api.Extractor): @@ -14,19 +13,14 @@ class ExtractAlembic(colorbleed.api.Extractor): def process(self, instance): - staging_dir = self.staging_dir(instance) - - file_name = "{}.abc".format(instance.data["subset"]) - tmp_filepath = os.path.join(staging_dir, file_name) - ropnode = instance[0] - # Set file name to staging dir + file name - attributes = {"filename": tmp_filepath} + # Get the filename from the filename parameter + # `.eval()` will make sure all tokens are resolved + file_name = os.path.basename(ropnode.parm("filename").eval()) - # We run the render with the input settings set by the user - with lib.attribute_values(ropnode, attributes): - ropnode.render() + # We run the render + ropnode.render() if "files" not in instance.data: instance.data["files"] = [] From 13aa727f5ab21afe8d4893100095a18404bdcdb2 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 09:39:29 +0200 Subject: [PATCH 016/121] Passed argument to setParm, added sop ouput --- colorbleed/plugins/houdini/create/create_vbd_cache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index c76ebd05c1..65cac4a7fd 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -25,8 +25,8 @@ class CreateVDBCache(houdini.Creator): def process(self): instance = super(CreateVDBCache, self).process() - parms = {} + parms = {"sopoutput": "$HIP/geo/%s.$F.vdb" % self.name} if self.nodes: parms.update({"soppath": self.nodes[0].path()}) - instance.setParms() + instance.setParms(parms) From 13f43f1925b023577711924790e685f14b7e75cf Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 10:16:13 +0200 Subject: [PATCH 017/121] Added host and order --- .../plugins/houdini/publish/collection_animation.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/collection_animation.py b/colorbleed/plugins/houdini/publish/collection_animation.py index a959d9d5c6..835e52eb68 100644 --- a/colorbleed/plugins/houdini/publish/collection_animation.py +++ b/colorbleed/plugins/houdini/publish/collection_animation.py @@ -11,8 +11,10 @@ class CollectAnimation(pyblish.api.InstancePlugin): """ - label = "Collect Animation" + order = pyblish.api.CollectorOrder families = ["colorbleed.pointcache"] + hosts = ["houdini"] + label = "Collect Animation" def process(self, instance): @@ -23,8 +25,7 @@ class CollectAnimation(pyblish.api.InstancePlugin): "f2": "endFrame", "f3": "steps"} - data = {} - for par, translation in parameters.items(): - data[translation] = node.parm(par).eval() + data = {name: node.parm(par).eval() for par, name in + parameters.items()} instance.data.update(data) From 61faa08752bc2507fe9f468be286f6cfa6629681 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 10:46:26 +0200 Subject: [PATCH 018/121] Fixed family name typo, added frame padding of 4 for frames --- colorbleed/plugins/houdini/create/create_vbd_cache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index 65cac4a7fd..b103a046fa 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -8,7 +8,7 @@ class CreateVDBCache(houdini.Creator): name = "vbdcache" label = "VDB Cache" - family = "colorbleed.vbdcache" + family = "colorbleed.vdbcache" icon = "cloud" def __init__(self, *args, **kwargs): @@ -25,7 +25,7 @@ class CreateVDBCache(houdini.Creator): def process(self): instance = super(CreateVDBCache, self).process() - parms = {"sopoutput": "$HIP/geo/%s.$F.vdb" % self.name} + parms = {"sopoutput": "$HIP/geo/%s.$F4.vdb" % self.name} if self.nodes: parms.update({"soppath": self.nodes[0].path()}) From 7d162aa5bb8727e70bdbc2f8ec3a551be6a51509 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 13:01:54 +0200 Subject: [PATCH 019/121] Check for animation data --- .../houdini/publish/collect_instances.py | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/collect_instances.py b/colorbleed/plugins/houdini/publish/collect_instances.py index 5f77b9d805..0e5301a6a1 100644 --- a/colorbleed/plugins/houdini/publish/collect_instances.py +++ b/colorbleed/plugins/houdini/publish/collect_instances.py @@ -24,8 +24,8 @@ class CollectInstances(pyblish.api.ContextPlugin): """ + order = pyblish.api.CollectorOrder - 0.01 label = "Collect Instances" - order = pyblish.api.CollectorOrder hosts = ["houdini"] def process(self, context): @@ -51,7 +51,17 @@ class CollectInstances(pyblish.api.ContextPlugin): if "active" in data: data["publish"] = data["active"] - instance = context.create_instance(data.get("name", node.name())) + data.update(self.get_frame_data(node)) + + # Create nice name + # All nodes in the Outputs graph have the 'Valid Frame Range' + # attribute, we check here if any frames are set + label = data.get("name", node.name()) + if "startFrame" in data: + frames = "[{startFrame} - {endFrame}]".format(**data) + label = "{} {}".format(label, frames) + + instance = context.create_instance(label) instance[:] = [node] instance.data.update(data) @@ -66,3 +76,27 @@ class CollectInstances(pyblish.api.ContextPlugin): context[:] = sorted(context, key=sort_by_family) return context + + def get_frame_data(self, node): + """Get the frame data: start frame, end frame and steps + Args: + node(hou.Node) + + Returns: + dict + + """ + + data = {} + + if node.parm("trange") is None: + return data + + if node.parm("trange").eval() == 0: + return data + + data["startFrame"] = node.parm("f1").eval() + data["endFrame"] = node.parm("f2").eval() + data["steps"] = node.parm("f3").eval() + + return data From 4abee5618c16cf7a130df3b1070248557ae98e18 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 13:27:48 +0200 Subject: [PATCH 020/121] Collect currently set output of render node --- .../houdini/publish/collect_output_node.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/collect_output_node.py diff --git a/colorbleed/plugins/houdini/publish/collect_output_node.py b/colorbleed/plugins/houdini/publish/collect_output_node.py new file mode 100644 index 0000000000..dbfe8a5890 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/collect_output_node.py @@ -0,0 +1,27 @@ +import pyblish.api + + +class CollectOutputNode(pyblish.api.InstancePlugin): + """Collect the out node which of the instance""" + + order = pyblish.api.CollectorOrder + families = ["*"] + hosts = ["houdini"] + label = "Collect Output Node" + + def process(self, instance): + + import hou + + node = instance[0] + + # Get sop path + if node.type().name() == "alembic": + sop_path_parm = "sop_path" + else: + sop_path_parm = "soppath" + + sop_path = node.parm(sop_path_parm).eval() + out_node = hou.node(sop_path) + + instance.data["output_node"] = out_node From 7001766c93aafd61485c90f16ef2ea5e64887151 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 13:28:06 +0200 Subject: [PATCH 021/121] Generic checks for output node --- .../houdini/publish/validate_output_node.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/validate_output_node.py diff --git a/colorbleed/plugins/houdini/publish/validate_output_node.py b/colorbleed/plugins/houdini/publish/validate_output_node.py new file mode 100644 index 0000000000..be7551cf0d --- /dev/null +++ b/colorbleed/plugins/houdini/publish/validate_output_node.py @@ -0,0 +1,43 @@ +import pyblish.api + + +class ValidateOutputNode(pyblish.api.InstancePlugin): + """Validate if output node: + - exists + - is of type 'output' + - has an input""" + + order = pyblish.api.ValidatorOrder + families = ["*"] + hosts = ["houdini"] + label = "Validate Output Node" + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Output node(s) `%s` are incorrect" % invalid) + + @classmethod + def get_invalid(cls, instance): + + output_node = instance.data["output_node"] + + if output_node is None: + node = instance[0] + cls.log.error("Output node at '%s' does not exist, see source" % + node.path()) + + return node.path() + + # Check if type is correct + if output_node.type().name() != "output": + cls.log.error("Output node `%s` is not if type `output`" % + output_node.path()) + return output_node.path() + + # Check if node has incoming connections + if not output_node.inputConnections(): + cls.log.error("Output node `%s` has no incoming connections" + % output_node.path()) + return output_node.path() From c94d59994597d8aefd40620d7a3b7b003470f7e7 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 13:28:23 +0200 Subject: [PATCH 022/121] Check for vdb output --- .../houdini/publish/valiate_vdb_input_node.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py diff --git a/colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py b/colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py new file mode 100644 index 0000000000..25a8d86ce3 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py @@ -0,0 +1,47 @@ +import pyblish.api +import colorbleed.api + + +class ValidateVDBInputNode(pyblish.api.InstancePlugin): + """Validate that the node connected to the output node is of type VDB + + Regardless of the amount of VDBs create the output will need to have an + equal amount of VDBs, points, primitives and vertices + + A VDB is an inherited type of Prim, holds the following data: + - Primitives: 1 + - Points: 1 + - Vertices: 1 + - VDBs: 1 + + """ + + order = colorbleed.api.ValidateContentsOrder + 0.1 + families = ["colorbleed.vdbcache"] + hosts = ["houdini"] + label = "Validate Input Node (VDB)" + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Node connected to the output node is not" + "of type VDB!") + + @classmethod + def get_invalid(cls, instance): + + node = instance.data["output_node"] + + prims = node.geometry().prims() + nr_of_prims = len(prims) + + nr_of_points = len(node.geometry().points()) + if nr_of_points != nr_of_prims: + cls.log.error("The number of primitives and points do not match") + return [instance] + + for prim in prims: + # print(">>", prim.name()) + if prim.numVertices() != 1: + cls.log.error("Found primitive with more than 1 vertex!") + return [instance] From 925dcdf4bc2379648e7e03ecfb909809ce512667 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 13:28:50 +0200 Subject: [PATCH 023/121] Fixed typo --- colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py b/colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py index 8dcc6e0509..7e298ce952 100644 --- a/colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py +++ b/colorbleed/plugins/houdini/publish/validate_mkpaths_toggled.py @@ -2,7 +2,7 @@ import pyblish.api import colorbleed.api -class ValidatIntermediateDirectoriesChecked(pyblish.api.InstancePlugin): +class ValidateIntermediateDirectoriesChecked(pyblish.api.InstancePlugin): """Validate if node attribute Create intermediate Directories is turned on Rules: From c7584523e559627b6178d014565fc3f063b78cdf Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 13:29:07 +0200 Subject: [PATCH 024/121] Extractor for VDB ROPs --- .../houdini/publish/extract_vdb_cache.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/extract_vdb_cache.py diff --git a/colorbleed/plugins/houdini/publish/extract_vdb_cache.py b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py new file mode 100644 index 0000000000..ad85a5daf0 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py @@ -0,0 +1,42 @@ +import os +import re + +import pyblish.api +import colorbleed.api + + +class ExtractVDBCache(colorbleed.api.Extractor): + + order = pyblish.api.ExtractorOrder + 0.1 + label = "Extract VDB Cache" + families = ["colorbleed.vdbcache"] + hosts = ["houdini"] + + def process(self, instance): + + ropnode = instance[0] + + # Get the filename from the filename parameter + # `.eval()` will make sure all tokens are resolved + output = ropnode.parm("sopoutput").eval() + staging_dir = os.path.dirname(output) + instance.data["stagingDir"] = staging_dir + + # Replace the 4 digits to match file sequence token '%04d' if we have + # a sequence of frames + file_name = os.path.basename(output) + has_frame = re.match("\w\.(d+)\.vdb", file_name) + if has_frame: + frame_nr = has_frame.group() + file_name.replace(frame_nr, "%04d") + + # We run the render + self.log.info( + "Starting render: {startFrame} - {endFrame}".format(**instance.data) + ) + ropnode.render() + + if "files" not in instance.data: + instance.data["files"] = [] + + instance.data["files"].append(file_name) From 13903ebbad379774b7062165b5b0c736a7a3c10f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 14 Sep 2018 14:16:27 +0200 Subject: [PATCH 025/121] Cosmetics - make all a list for consistency --- .../plugins/maya/publish/collect_render_layer_aovs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py b/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py index 16039fe5e4..f39330e10f 100644 --- a/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py +++ b/colorbleed/plugins/maya/publish/collect_render_layer_aovs.py @@ -41,9 +41,9 @@ class CollectRenderLayerAOVS(pyblish.api.InstancePlugin): self.log.info("Renderer found: {}".format(renderer)) - rp_node_types = {"vray": ("VRayRenderElement", "VRayRenderElementSet"), - "arnold": "aiAOV", - "redshift": "RedshiftAOV"} + rp_node_types = {"vray": ["VRayRenderElement", "VRayRenderElementSet"], + "arnold": ["aiAOV"], + "redshift": ["RedshiftAOV"]} if renderer not in rp_node_types.keys(): self.log.error("Unsupported renderer found: '{}'".format(renderer)) From f1bf3d806626dc40148578893e2a2b4affe23052 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 16:54:39 +0200 Subject: [PATCH 026/121] Removed commented line --- colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py | 1 - 1 file changed, 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py b/colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py index 25a8d86ce3..24606046fa 100644 --- a/colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py +++ b/colorbleed/plugins/houdini/publish/valiate_vdb_input_node.py @@ -41,7 +41,6 @@ class ValidateVDBInputNode(pyblish.api.InstancePlugin): return [instance] for prim in prims: - # print(">>", prim.name()) if prim.numVertices() != 1: cls.log.error("Found primitive with more than 1 vertex!") return [instance] From d91ca5c1d4cc766d00332e422fc45c3c59531b67 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 17:18:21 +0200 Subject: [PATCH 027/121] Added alembic output node validator --- .../publish/validate_alembic_input_node.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/validate_alembic_input_node.py diff --git a/colorbleed/plugins/houdini/publish/validate_alembic_input_node.py b/colorbleed/plugins/houdini/publish/validate_alembic_input_node.py new file mode 100644 index 0000000000..91f9e9f97e --- /dev/null +++ b/colorbleed/plugins/houdini/publish/validate_alembic_input_node.py @@ -0,0 +1,37 @@ +import pyblish.api +import colorbleed.api + + +class ValidateAlembicInputNode(pyblish.api.InstancePlugin): + """Validate that the node connected to the output is correct + + The connected node cannot be of the following types for Alembic: + - VDB + - Volumne + + """ + + order = colorbleed.api.ValidateContentsOrder + 0.1 + families = ["colorbleed.pointcache"] + hosts = ["houdini"] + label = "Validate Input Node (Abc)" + + def process(self, instance): + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Node connected to the output node incorrect") + + @classmethod + def get_invalid(cls, instance): + + invalid_nodes = ["VDB", "Volume"] + node = instance.data["output_node"] + + prims = node.geometry().prims() + + for prim in prims: + prim_type = prim.type().name() + if prim_type in invalid_nodes: + cls.log.error("Found a primitive which is of type '%s' !" + % prim_type) + return [instance] From 5ddc333999fc81a4027fcdf7fbf1d4f7c94dbfb7 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 14 Sep 2018 17:18:43 +0200 Subject: [PATCH 028/121] Set staging dir to what user has entered --- colorbleed/plugins/houdini/publish/extract_alembic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/extract_alembic.py b/colorbleed/plugins/houdini/publish/extract_alembic.py index 29450d0824..f66b5bde72 100644 --- a/colorbleed/plugins/houdini/publish/extract_alembic.py +++ b/colorbleed/plugins/houdini/publish/extract_alembic.py @@ -17,9 +17,14 @@ class ExtractAlembic(colorbleed.api.Extractor): # Get the filename from the filename parameter # `.eval()` will make sure all tokens are resolved - file_name = os.path.basename(ropnode.parm("filename").eval()) + output = ropnode.parm("filename").eval() + staging_dir = os.path.dirname(output) + instance.data["stagingDir"] = staging_dir + + file_name = os.path.basename(output) # We run the render + self.log.info("Writing alembic '%s' to '%s'" % (file_name, staging_dir)) ropnode.render() if "files" not in instance.data: From d7ca17c4f94b0a3c0cd91bf0229e3a2a93ffd773 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 17 Sep 2018 09:30:35 +0200 Subject: [PATCH 029/121] Added new family --- colorbleed/plugins/global/publish/integrate.py | 1 + 1 file changed, 1 insertion(+) diff --git a/colorbleed/plugins/global/publish/integrate.py b/colorbleed/plugins/global/publish/integrate.py index 2418971546..6917b43f6f 100644 --- a/colorbleed/plugins/global/publish/integrate.py +++ b/colorbleed/plugins/global/publish/integrate.py @@ -30,6 +30,7 @@ class IntegrateAsset(pyblish.api.InstancePlugin): "colorbleed.mayaAscii", "colorbleed.model", "colorbleed.pointcache", + "colorbleed.vdbcache", "colorbleed.setdress", "colorbleed.rig", "colorbleed.yetiRig", From f03a6b31c4cfb3a908287de0ae5a974252f844cc Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 17 Sep 2018 09:32:09 +0200 Subject: [PATCH 030/121] Updated doc strings --- colorbleed/plugins/maya/load/load_alembic.py | 2 +- colorbleed/plugins/maya/load/load_camera.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_alembic.py b/colorbleed/plugins/maya/load/load_alembic.py index afea761ab8..d34530a8d5 100644 --- a/colorbleed/plugins/maya/load/load_alembic.py +++ b/colorbleed/plugins/maya/load/load_alembic.py @@ -2,7 +2,7 @@ import colorbleed.maya.plugin class AbcLoader(colorbleed.maya.plugin.ReferenceLoader): - """Specific loader of Alembic for the avalon.animation family""" + """Specific loader of Alembic for the colorbleed.animation family""" families = ["colorbleed.animation", "colorbleed.pointcache"] diff --git a/colorbleed/plugins/maya/load/load_camera.py b/colorbleed/plugins/maya/load/load_camera.py index 1d66433a6f..067c4c0cde 100644 --- a/colorbleed/plugins/maya/load/load_camera.py +++ b/colorbleed/plugins/maya/load/load_camera.py @@ -2,7 +2,7 @@ import colorbleed.maya.plugin class CameraLoader(colorbleed.maya.plugin.ReferenceLoader): - """Specific loader of Alembic for the avalon.animation family""" + """Specific loader of Alembic for the colorbleed.camera family""" families = ["colorbleed.camera"] label = "Reference camera" From b229fc20cd78a9f1f971f82eac804f4a2d8fabc7 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 17 Sep 2018 10:09:54 +0200 Subject: [PATCH 031/121] Added VDB cache loader --- .../plugins/maya/load/load_vdb_to_vray.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 colorbleed/plugins/maya/load/load_vdb_to_vray.py diff --git a/colorbleed/plugins/maya/load/load_vdb_to_vray.py b/colorbleed/plugins/maya/load/load_vdb_to_vray.py new file mode 100644 index 0000000000..45294845d4 --- /dev/null +++ b/colorbleed/plugins/maya/load/load_vdb_to_vray.py @@ -0,0 +1,64 @@ +from avalon import api +# import colorbleed.maya.plugin + + +class LoadVDBtoVRay(api.Loader): + + families = ["colorbleed.vdbcache"] + representations = ["vdb"] + + name = "Load VDB to VRay" + icon = "cloud" + color = "orange" + + def load(self, context, name, namespace, data): + + # import pprint + from maya import cmds + import avalon.maya.lib as lib + from avalon.maya.pipeline import containerise + + # Check if viewport drawing engine is Open GL Core (compat) + render_engine = None + compatible = "OpenGLCoreProfileCompat" + if cmds.optionVar(exists="vp2RenderingEngine"): + render_engine = cmds.optionVar(query="vp2RenderingEngine") + + if not render_engine or render_engine != compatible: + raise RuntimeError("Current scene's settings are incompatible." + "See Preferences > Display > Viewport 2.0 to " + "set the render engine to '%s'" % compatible) + + asset = context['asset'] + version = context["version"] + + asset_name = asset["name"] + namespace = namespace or lib.unique_namespace( + asset_name + "_", + prefix="_" if asset_name[0].isdigit() else "", + suffix="_", + ) + + # Root group + label = "{}:{}".format(namespace, name) + root = cmds.group(name=label, empty=True) + + # Create VR + grid_node = cmds.createNode("VRayVolumeGrid", + name="{}VVGShape".format(label), + parent=root) + + # Set attributes + cmds.setAttr("{}.inFile".format(grid_node), self.fname, type="string") + cmds.setAttr("{}.inReadOffset".format(grid_node), + version["startFrames"]) + + nodes = [root, grid_node] + self[:] = nodes + + return containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) From 27992f68e1b540a4fb31a275903996a42556267c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 18 Sep 2018 13:09:15 +0200 Subject: [PATCH 032/121] Added load alembic camera --- .../plugins/houdini/load/load_camera.py | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 colorbleed/plugins/houdini/load/load_camera.py diff --git a/colorbleed/plugins/houdini/load/load_camera.py b/colorbleed/plugins/houdini/load/load_camera.py new file mode 100644 index 0000000000..f827244c6b --- /dev/null +++ b/colorbleed/plugins/houdini/load/load_camera.py @@ -0,0 +1,119 @@ +from avalon import api + +from avalon.houdini import pipeline, lib + + +class CameraLoader(api.Loader): + """Specific loader of Alembic for the avalon.animation family""" + + families = ["colorbleed.camera"] + label = "Load Camera (abc)" + representations = ["abc"] + order = -10 + + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + + import os + import hou + + # Format file name, Houdini only wants forward slashes + file_path = os.path.normpath(self.fname) + file_path = file_path.replace("\\", "/") + + # Get the root node + obj = hou.node("/obj") + + # Create a unique name + counter = 1 + asset_name = context["asset"]["name"] + + namespace = namespace if namespace else asset_name + formatted = "{}_{}".format(namespace, name) if namespace else name + node_name = "{0}_{1:03d}".format(formatted, counter) + + children = lib.children_as_string(hou.node("/obj")) + while node_name in children: + counter += 1 + node_name = "{0}_{1:03d}".format(formatted, counter) + + # Create a archive node + container = self.create_and_connect(obj, "alembicarchive", node_name) + + # TODO: add FPS of project / asset + container.setParms({"fileName": file_path, + "channelRef": True}) + + # Apply some magic + container.parm("buildHierarchy").pressButton() + container.moveToGoodPosition() + + # Create an alembic xform node + nodes = [container] + + self[:] = nodes + + return pipeline.containerise(node_name, + namespace, + nodes, + context, + self.__class__.__name__) + + def update(self, container, representation): + + node = container["node"] + + # Update the file path + file_path = api.get_representation_path(representation) + file_path = file_path.replace("\\", "/") + + # Update attributes + node.setParms({"fileName": file_path, + "representation": str(representation["_id"])}) + + # Rebuild + node.parm("buildHierarchy").pressButton() + + def remove(self, container): + + node = container["node"] + node.destroy() + + def create_and_connect(self, node, node_type, name=None): + """Create a node within a node which and connect it to the input + + Args: + node(hou.Node): parent of the new node + node_type(str) name of the type of node, eg: 'alembic' + name(str, Optional): name of the node + + Returns: + hou.Node + + """ + + import hou + + try: + + if name: + new_node = node.createNode(node_type, node_name=name) + else: + new_node = node.createNode(node_type) + + new_node.moveToGoodPosition() + + try: + input_node = next(i for i in node.allItems() if + isinstance(i, hou.SubnetIndirectInput)) + except StopIteration: + return new_node + + new_node.setInput(0, input_node) + return new_node + + except Exception: + raise RuntimeError("Could not created node type `%s` in node `%s`" + % (node_type, node)) From 3f933d07367c1db68c79e596a525cc6666bf1f44 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 18 Sep 2018 14:16:02 +0200 Subject: [PATCH 033/121] Renamed plugin, added colorbleed.model --- colorbleed/plugins/houdini/load/load_alembic.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index 5043ba5a0d..94e3894d05 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -6,8 +6,10 @@ from avalon.houdini import pipeline, lib class AbcLoader(api.Loader): """Specific loader of Alembic for the avalon.animation family""" - families = ["colorbleed.animation", "colorbleed.pointcache"] - label = "Load Animation" + families = ["colorbleed.model", + "colorbleed.animation", + "colorbleed.pointcache"] + label = "Load Alembic" representations = ["abc"] order = -10 icon = "code-fork" From ad0f846a028da00e37bb71df1d8fe9c43758364f Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 18 Sep 2018 14:23:35 +0200 Subject: [PATCH 034/121] Refactored parameter evaluation --- colorbleed/plugins/houdini/publish/collect_instances.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/collect_instances.py b/colorbleed/plugins/houdini/publish/collect_instances.py index 0e5301a6a1..effd6e392e 100644 --- a/colorbleed/plugins/houdini/publish/collect_instances.py +++ b/colorbleed/plugins/houdini/publish/collect_instances.py @@ -92,11 +92,11 @@ class CollectInstances(pyblish.api.ContextPlugin): if node.parm("trange") is None: return data - if node.parm("trange").eval() == 0: + if node.evalParm("trange") == 0: return data - data["startFrame"] = node.parm("f1").eval() - data["endFrame"] = node.parm("f2").eval() - data["steps"] = node.parm("f3").eval() + data["startFrame"] = node.evalParm("f1") + data["endFrame"] = node.evalParm("f2") + data["steps"] = node.evalParm("f3") return data From a3a32950d15116fdb38ffbd5284df76ca2c84417 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 10:40:32 +0200 Subject: [PATCH 035/121] Added alembic camera instance --- .../houdini/create/create_alembic_camera.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 colorbleed/plugins/houdini/create/create_alembic_camera.py diff --git a/colorbleed/plugins/houdini/create/create_alembic_camera.py b/colorbleed/plugins/houdini/create/create_alembic_camera.py new file mode 100644 index 0000000000..43213f46b0 --- /dev/null +++ b/colorbleed/plugins/houdini/create/create_alembic_camera.py @@ -0,0 +1,36 @@ +from collections import OrderedDict + +from avalon import houdini + + +class CreateAlembicCamera(houdini.Creator): + + name = "camera" + label = "Camera (Abc)" + family = "colorbleed.camera" + icon = "camera" + + def __init__(self, *args, **kwargs): + super(CreateAlembicCamera, self).__init__(*args, **kwargs) + + # create an ordered dict with the existing data first + data = OrderedDict(**self.data) + + # Set node type to create for output + data["node_type"] = "alembic" + + self.data = data + + def process(self): + instance = super(CreateAlembicCamera, self).process() + + parms = {"use_sop_path": True, + "build_from_path": True, + "path_attrib": "path", + "filename": "$HIP/%s.abc" % self.name} + + if self.nodes: + node = self.nodes[0] + parms.update({"sop_path": node.path()}) + + instance.setParms(parms) From e9aac130390f90ddbf25d962c25aa0fa0fd9457e Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 10:41:26 +0200 Subject: [PATCH 036/121] Cosmetics --- colorbleed/plugins/houdini/create/create_pointcache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 9169135245..25671c787e 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -25,9 +25,9 @@ class CreatePointCache(houdini.Creator): def process(self): instance = super(CreatePointCache, self).process() - parms = {"build_from_path": 1, + parms = {"use_sop_path": True, + "build_from_path": True, "path_attrib": "path", - "use_sop_path": True, "filename": "$HIP/%s.abc" % self.name} if self.nodes: From abbcd30f86078664177b84edb57622abe8df7d83 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 10:42:42 +0200 Subject: [PATCH 037/121] Renamed and updated plugin, added colorbleed.camera --- colorbleed/plugins/houdini/publish/extract_alembic.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/extract_alembic.py b/colorbleed/plugins/houdini/publish/extract_alembic.py index f66b5bde72..632b0a7d7b 100644 --- a/colorbleed/plugins/houdini/publish/extract_alembic.py +++ b/colorbleed/plugins/houdini/publish/extract_alembic.py @@ -7,17 +7,16 @@ import colorbleed.api class ExtractAlembic(colorbleed.api.Extractor): order = pyblish.api.ExtractorOrder - label = "Extract Pointcache (Alembic)" + label = "Extract Alembic" hosts = ["houdini"] - families = ["colorbleed.pointcache"] + families = ["colorbleed.pointcache", "colorbleed.camera"] def process(self, instance): ropnode = instance[0] # Get the filename from the filename parameter - # `.eval()` will make sure all tokens are resolved - output = ropnode.parm("filename").eval() + output = ropnode.evalParm("filename") staging_dir = os.path.dirname(output) instance.data["stagingDir"] = staging_dir From d1d6064e1ea8cf60e77caec37a397766ae820e3c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 10:50:02 +0200 Subject: [PATCH 038/121] Improved validator to accept node type camera --- .../houdini/publish/validate_output_node.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/validate_output_node.py b/colorbleed/plugins/houdini/publish/validate_output_node.py index be7551cf0d..18fcb4898f 100644 --- a/colorbleed/plugins/houdini/publish/validate_output_node.py +++ b/colorbleed/plugins/houdini/publish/validate_output_node.py @@ -31,13 +31,15 @@ class ValidateOutputNode(pyblish.api.InstancePlugin): return node.path() # Check if type is correct - if output_node.type().name() != "output": - cls.log.error("Output node `%s` is not if type `output`" % + type_name = output_node.type().name() + if type_name not in ["output", "cam"]: + cls.log.error("Output node `%s` is an accepted type `output` " + "or `camera`" % output_node.path()) - return output_node.path() + return [output_node.path()] - # Check if node has incoming connections - if not output_node.inputConnections(): + # Check if output node has incoming connections + if type_name == "output" and not output_node.inputConnections(): cls.log.error("Output node `%s` has no incoming connections" % output_node.path()) - return output_node.path() + return [output_node.path()] From 8369629415e5a945a7caf13d506c648103ac4f86 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 10:55:57 +0200 Subject: [PATCH 039/121] Added cam to accepted type --- colorbleed/plugins/houdini/publish/validate_outnode_exists.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/validate_outnode_exists.py b/colorbleed/plugins/houdini/publish/validate_outnode_exists.py index 011a1f63e7..b9f24faa32 100644 --- a/colorbleed/plugins/houdini/publish/validate_outnode_exists.py +++ b/colorbleed/plugins/houdini/publish/validate_outnode_exists.py @@ -42,7 +42,8 @@ class ValidatOutputNodeExists(pyblish.api.InstancePlugin): cls.log.error("Node at '%s' does not exist" % sop_path) result.add(node.path()) - if output_node.type().name() != "output": + # Added cam as this is a legit output type (cameras can't + if output_node.type().name() not in ["output", "cam"]: cls.log.error("SOP Path does not end path at output node") result.add(node.path()) From 9ee8485af663318e5bdedfc4ea2c9250a00dfc34 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 12:29:02 +0200 Subject: [PATCH 040/121] Removed commented lines --- colorbleed/plugins/maya/load/load_vdb_to_vray.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/colorbleed/plugins/maya/load/load_vdb_to_vray.py b/colorbleed/plugins/maya/load/load_vdb_to_vray.py index 45294845d4..04f623a3fb 100644 --- a/colorbleed/plugins/maya/load/load_vdb_to_vray.py +++ b/colorbleed/plugins/maya/load/load_vdb_to_vray.py @@ -1,5 +1,4 @@ from avalon import api -# import colorbleed.maya.plugin class LoadVDBtoVRay(api.Loader): @@ -13,7 +12,6 @@ class LoadVDBtoVRay(api.Loader): def load(self, context, name, namespace, data): - # import pprint from maya import cmds import avalon.maya.lib as lib from avalon.maya.pipeline import containerise From 924c80d0956294e4480ba0a6cf155f5aa46c167a Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 12:50:31 +0200 Subject: [PATCH 041/121] Ensure label is used in RMB menu --- colorbleed/plugins/maya/load/load_vdb_to_vray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/load/load_vdb_to_vray.py b/colorbleed/plugins/maya/load/load_vdb_to_vray.py index 04f623a3fb..5d868174ce 100644 --- a/colorbleed/plugins/maya/load/load_vdb_to_vray.py +++ b/colorbleed/plugins/maya/load/load_vdb_to_vray.py @@ -6,7 +6,7 @@ class LoadVDBtoVRay(api.Loader): families = ["colorbleed.vdbcache"] representations = ["vdb"] - name = "Load VDB to VRay" + label = "Load VDB to VRay" icon = "cloud" color = "orange" From 4e544893b91fb413612d384063b63b1d6e0bec83 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 12:51:13 +0200 Subject: [PATCH 042/121] Added VDB to Redshift --- .../plugins/maya/load/load_vdb_to_redshift.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 colorbleed/plugins/maya/load/load_vdb_to_redshift.py diff --git a/colorbleed/plugins/maya/load/load_vdb_to_redshift.py b/colorbleed/plugins/maya/load/load_vdb_to_redshift.py new file mode 100644 index 0000000000..faaf2314e5 --- /dev/null +++ b/colorbleed/plugins/maya/load/load_vdb_to_redshift.py @@ -0,0 +1,68 @@ +from avalon import api + + +class LoadVDBtoRedShift(api.Loader): + + families = ["colorbleed.vdbcache"] + representations = ["vdb"] + + label = "Load VDB to RedShift" + icon = "cloud" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + + from maya import cmds + import avalon.maya.lib as lib + from avalon.maya.pipeline import containerise + + # Check if the plugin for redshift is available on the pc + try: + cmds.loadPlugin("redshift4maya", quiet=True) + except Exception as exc: + self.log.error("Encountered exception:\n%s" % exc) + return + + # Check if viewport drawing engine is Open GL Core (compat) + render_engine = None + compatible = "OpenGL" + if cmds.optionVar(exists="vp2RenderingEngine"): + render_engine = cmds.optionVar(query="vp2RenderingEngine") + + if not render_engine or not render_engine.startswith(compatible): + raise RuntimeError("Current scene's settings are incompatible." + "See Preferences > Display > Viewport 2.0 to " + "set the render engine to '%s'" + % compatible) + + asset = context['asset'] + + asset_name = asset["name"] + namespace = namespace or lib.unique_namespace( + asset_name + "_", + prefix="_" if asset_name[0].isdigit() else "", + suffix="_", + ) + + # Root group + label = "{}:{}".format(namespace, name) + root = cmds.group(name=label, empty=True) + + # Create VR + volume_node = cmds.createNode("RedshiftVolumeShape", + name="{}RVSShape".format(label), + parent=root) + + cmds.setAttr("{}.fileName".format(volume_node), + self.fname, + type="string") + + nodes = [root, volume_node] + self[:] = nodes + + return containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__) From e82f524687015291192683467718ff1b7c97daf5 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 12:52:45 +0200 Subject: [PATCH 043/121] Added doc string --- colorbleed/plugins/maya/load/load_vdb_to_redshift.py | 1 + 1 file changed, 1 insertion(+) diff --git a/colorbleed/plugins/maya/load/load_vdb_to_redshift.py b/colorbleed/plugins/maya/load/load_vdb_to_redshift.py index faaf2314e5..bafb529bda 100644 --- a/colorbleed/plugins/maya/load/load_vdb_to_redshift.py +++ b/colorbleed/plugins/maya/load/load_vdb_to_redshift.py @@ -2,6 +2,7 @@ from avalon import api class LoadVDBtoRedShift(api.Loader): + """Load OpenVDB in a Redshift Volume Shape""" families = ["colorbleed.vdbcache"] representations = ["vdb"] From cadb916df9a1b980cb8dff3734b002c31f6b92e4 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 14:55:34 +0200 Subject: [PATCH 044/121] Fixed fetching parent widget for scene inventory, removed arg and comment --- colorbleed/houdini/__init__.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/colorbleed/houdini/__init__.py b/colorbleed/houdini/__init__.py index f70b2d6198..7e8284e4aa 100644 --- a/colorbleed/houdini/__init__.py +++ b/colorbleed/houdini/__init__.py @@ -29,8 +29,6 @@ log = logging.getLogger("colorbleed.houdini") def install(): - # Set - pyblish.register_plugin_path(PUBLISH_PATH) avalon.register_plugin_path(avalon.Loader, LOAD_PATH) avalon.register_plugin_path(avalon.Creator, CREATE_PATH) @@ -50,7 +48,7 @@ def on_init(_): houdini.on_houdini_initialize() -def on_save(_): +def on_save(): avalon.logger.info("Running callback on save..") @@ -66,15 +64,12 @@ def on_open(): update_task_from_path(hou.hipFile.path()) if any_outdated(): - from avalon.vendor.Qt import QtWidgets from ..widgets import popup log.warning("Scene has outdated content.") - # Find maya main window - top_level_widgets = {w.objectName(): w for w in - QtWidgets.QApplication.topLevelWidgets()} - parent = top_level_widgets.get("MayaWindow", None) + # Get main window + parent = hou.ui.mainQtWindow() if parent is None: log.info("Skipping outdated content pop-up " @@ -96,4 +91,4 @@ def on_open(): def on_task_changed(*args): """Wrapped function of app initialize and maya's on task changed""" - pass \ No newline at end of file + pass From c3d2453ee7780624232f08f66d0b1da84a6623cc Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 19 Sep 2018 15:00:19 +0200 Subject: [PATCH 045/121] Removed unused function --- colorbleed/houdini/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/colorbleed/houdini/__init__.py b/colorbleed/houdini/__init__.py index 7e8284e4aa..4025ef731a 100644 --- a/colorbleed/houdini/__init__.py +++ b/colorbleed/houdini/__init__.py @@ -87,8 +87,3 @@ def on_open(): "your Maya scene.") dialog.on_show.connect(_on_show_inventory) dialog.show() - - -def on_task_changed(*args): - """Wrapped function of app initialize and maya's on task changed""" - pass From 9c6642c25bc83563372d15730ba5190b5bcdc864 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 21 Sep 2018 11:31:08 +0200 Subject: [PATCH 046/121] Remove file node, add normal on points, minor improvements --- .../plugins/houdini/load/load_alembic.py | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index 94e3894d05..b8a7949acf 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -42,8 +42,8 @@ class AbcLoader(api.Loader): container = obj.createNode("geo", node_name=node_name) # Remove the file node, it only loads static meshes - node_path = "/obj/{}/file1".format(node_name) - hou.node(node_path) + file_node = hou.node("/obj/{}/file1".format(node_name)) + file_node.destroy() # Create an alembic node (supports animation) alembic = container.createNode("alembic", node_name=node_name) @@ -54,18 +54,23 @@ class AbcLoader(api.Loader): unpack.setInput(0, alembic) unpack.setParms({"transfer_attributes": "path"}) - # Set new position for unpack node else it gets cluttered - unpack.setPosition([0, -1]) - # set unpack as display node unpack.setDisplayFlag(True) - null_node = container.createNode("null", - node_name="OUT_{}".format(name)) - null_node.setPosition([0, -2]) - null_node.setInput(0, unpack) + # Add normal to points + # normal types order ['point', 'vertex', 'prim', 'detail'] + normal_node = container.createNode("normal") + normal_node.setParms({"type": 0}) - nodes = [container, alembic, unpack, null_node] + normal_node.setInput(0, unpack) + + null = container.createNode("null", node_name="OUT_{}".format(name)) + null.setInput(0, normal_node) + + # Set new position for unpack node else it gets cluttered + nodes = [container, alembic, unpack, normal_node, null] + for nr, node in enumerate(nodes): + node.setPosition([0, (0 - nr)]) self[:] = nodes From f8c05c7c4f74372da0bd013572b553ffa7b2053f Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 21 Sep 2018 17:09:19 +0200 Subject: [PATCH 047/121] fixed bug with None being passed as value --- colorbleed/plugins/maya/load/load_yeti_cache.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/colorbleed/plugins/maya/load/load_yeti_cache.py b/colorbleed/plugins/maya/load/load_yeti_cache.py index c3a3cdfefe..5a1e404975 100644 --- a/colorbleed/plugins/maya/load/load_yeti_cache.py +++ b/colorbleed/plugins/maya/load/load_yeti_cache.py @@ -284,6 +284,8 @@ class YetiCacheLoader(api.Loader): # Apply attributes to pgYetiMaya node for attr, value in attributes.items(): + if value is None: + continue lib.set_attribute(attr, value, yeti_node) # Fix for : YETI-6 From 2864682a909fa2009041175b9af4c912f8f3e3a6 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 24 Sep 2018 10:53:52 +0200 Subject: [PATCH 048/121] Improved naming of utility nodes, set last node to display --- colorbleed/plugins/houdini/load/load_alembic.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index b8a7949acf..ca5741ecc3 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -50,23 +50,25 @@ class AbcLoader(api.Loader): alembic.setParms({"fileName": file_path}) # Add unpack node - unpack = container.createNode("unpack") + unpack_name = "unpack_{}".format(name) + unpack = container.createNode("unpack", node_name=unpack_name) unpack.setInput(0, alembic) unpack.setParms({"transfer_attributes": "path"}) - # set unpack as display node - unpack.setDisplayFlag(True) - # Add normal to points - # normal types order ['point', 'vertex', 'prim', 'detail'] - normal_node = container.createNode("normal") + # Order of menu ['point', 'vertex', 'prim', 'detail'] + normal_name = "normal_{}".format(name) + normal_node = container.createNode("normal", node_name=normal_name) normal_node.setParms({"type": 0}) normal_node.setInput(0, unpack) - null = container.createNode("null", node_name="OUT_{}".format(name)) + null = container.createNode("null", node_name="OUT".format(name)) null.setInput(0, normal_node) + # Set display on last node + null.setDisplayFlag(True) + # Set new position for unpack node else it gets cluttered nodes = [container, alembic, unpack, normal_node, null] for nr, node in enumerate(nodes): From 9f7b85372dd205ff15346f32c3e0d8f1dbebded1 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 24 Sep 2018 13:57:26 +0200 Subject: [PATCH 049/121] Fix #142 - allow to ignore menu install on missing "scriptsmenu" dependency --- colorbleed/maya/menu.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/colorbleed/maya/menu.py b/colorbleed/maya/menu.py index 1e411e1c78..9223271d65 100644 --- a/colorbleed/maya/menu.py +++ b/colorbleed/maya/menu.py @@ -23,10 +23,15 @@ def _get_menu(): def deferred(): - import scriptsmenu.launchformaya as launchformaya - import scriptsmenu.scriptsmenu as scriptsmenu + log.info("Attempting to install scripts menu..") - log.info("Attempting to install ...") + try: + import scriptsmenu.launchformaya as launchformaya + import scriptsmenu.scriptsmenu as scriptsmenu + except ImportError: + log.warning("Skipping colorbleed.menu install, because " + "'scriptsmenu' module seems unavailable.") + return # load configuration of custom menu config_path = os.path.join(os.path.dirname(__file__), "menu.json") @@ -44,7 +49,7 @@ def uninstall(): menu = _get_menu() if menu: - log.info("Attempting to uninstall ..") + log.info("Attempting to uninstall..") try: menu.deleteLater() From 8c8e3789fbfa30a9c4b648018ee0614f2f568b46 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 25 Sep 2018 11:43:46 +0200 Subject: [PATCH 050/121] Throw warning instead of assertion error --- colorbleed/plugins/maya/publish/collect_animation.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_animation.py b/colorbleed/plugins/maya/publish/collect_animation.py index 53251cadc3..c724759180 100644 --- a/colorbleed/plugins/maya/publish/collect_animation.py +++ b/colorbleed/plugins/maya/publish/collect_animation.py @@ -29,8 +29,11 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin): out_set = next((i for i in instance.data["setMembers"] if i.endswith("out_SET")), None) - assert out_set, ("Expecting out_SET for instance of family" - " '%s'" % family) + if out_set is None: + warning = "Expecting out_SET for instance of family '%s'" % family + self.log.warning(warning) + return + members = cmds.ls(cmds.sets(out_set, query=True), long=True) # Get all the relatives of the members From 5bc4a2bb9f07dc236c0816bb58a943a7aaa091f1 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 25 Sep 2018 12:10:26 +0200 Subject: [PATCH 051/121] Added check for out set --- .../plugins/maya/publish/validate_animation_content.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/colorbleed/plugins/maya/publish/validate_animation_content.py b/colorbleed/plugins/maya/publish/validate_animation_content.py index 0725281705..da1ec9e84a 100644 --- a/colorbleed/plugins/maya/publish/validate_animation_content.py +++ b/colorbleed/plugins/maya/publish/validate_animation_content.py @@ -20,6 +20,12 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin): def get_invalid(cls, instance): assert 'out_hierarchy' in instance.data, "Missing `out_hierarchy` data" + out_set = next((i for i in instance.data["setMembers"] if + i.endswith("out_SET")), None) + + assert out_set, ("Instance '%s' has no objectSet named: `OUT_set`" % + instance.name) + # All nodes in the `out_hierarchy` must be among the nodes that are # in the instance. The nodes in the instance are found from the top # group, as such this tests whether all nodes are under that top group. From 35923049263db00cef2023f23a4a0da8ce8b8de0 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 25 Sep 2018 12:12:42 +0200 Subject: [PATCH 052/121] Improved error message --- .../plugins/maya/publish/validate_animation_content.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/maya/publish/validate_animation_content.py b/colorbleed/plugins/maya/publish/validate_animation_content.py index da1ec9e84a..c5cc53ba2e 100644 --- a/colorbleed/plugins/maya/publish/validate_animation_content.py +++ b/colorbleed/plugins/maya/publish/validate_animation_content.py @@ -23,8 +23,10 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin): out_set = next((i for i in instance.data["setMembers"] if i.endswith("out_SET")), None) - assert out_set, ("Instance '%s' has no objectSet named: `OUT_set`" % - instance.name) + assert out_set, ("Instance '%s' has no objectSet named: `OUT_set`." + "If this instance is an unloaded reference, " + "please deactivate by toggling the 'Active' attribute" + % instance.name) # All nodes in the `out_hierarchy` must be among the nodes that are # in the instance. The nodes in the instance are found from the top From 43a9305984d95b0deaf294c53943e56c95857cb5 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 25 Sep 2018 12:18:00 +0200 Subject: [PATCH 053/121] Changed order, out_hierarchy is dependend on out_SET --- colorbleed/plugins/maya/publish/validate_animation_content.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/validate_animation_content.py b/colorbleed/plugins/maya/publish/validate_animation_content.py index c5cc53ba2e..cce7732803 100644 --- a/colorbleed/plugins/maya/publish/validate_animation_content.py +++ b/colorbleed/plugins/maya/publish/validate_animation_content.py @@ -18,7 +18,6 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin): @classmethod def get_invalid(cls, instance): - assert 'out_hierarchy' in instance.data, "Missing `out_hierarchy` data" out_set = next((i for i in instance.data["setMembers"] if i.endswith("out_SET")), None) @@ -28,6 +27,8 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin): "please deactivate by toggling the 'Active' attribute" % instance.name) + assert 'out_hierarchy' in instance.data, "Missing `out_hierarchy` data" + # All nodes in the `out_hierarchy` must be among the nodes that are # in the instance. The nodes in the instance are found from the top # group, as such this tests whether all nodes are under that top group. From 00dad9a4f5f3c4d6d5def065d8a4c5826d34107c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 25 Sep 2018 12:21:20 +0200 Subject: [PATCH 054/121] Fixed space in error message --- colorbleed/plugins/maya/publish/validate_animation_content.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/validate_animation_content.py b/colorbleed/plugins/maya/publish/validate_animation_content.py index cce7732803..efa7d07c8e 100644 --- a/colorbleed/plugins/maya/publish/validate_animation_content.py +++ b/colorbleed/plugins/maya/publish/validate_animation_content.py @@ -22,7 +22,7 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin): out_set = next((i for i in instance.data["setMembers"] if i.endswith("out_SET")), None) - assert out_set, ("Instance '%s' has no objectSet named: `OUT_set`." + assert out_set, ("Instance '%s' has no objectSet named: `OUT_set`. " "If this instance is an unloaded reference, " "please deactivate by toggling the 'Active' attribute" % instance.name) From 66e5471604db93bc66df4e117d4e74d504669041 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 27 Sep 2018 16:09:43 +0200 Subject: [PATCH 055/121] Optimize get_id (+/- 3x faster) and set_id (minor tweak) --- colorbleed/maya/lib.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index b1351cc19c..2ea618c178 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -11,6 +11,7 @@ import contextlib from collections import OrderedDict, defaultdict from maya import cmds, mel +import maya.api.OpenMaya as om from avalon import api, maya, io, pipeline from avalon.vendor.six import string_types @@ -764,10 +765,20 @@ def get_id(node): if node is None: return - if not cmds.attributeQuery("cbId", node=node, exists=True): + sel = om.MSelectionList() + sel.add(node) + + api_node = sel.getDependNode(0) + fn = om.MFnDependencyNode(api_node) + + if not fn.hasAttribute("cbId"): return - return cmds.getAttr("{}.cbId".format(node)) + try: + return fn.findPlug("cbId", False).asString() + except RuntimeError: + log.warning("Failed to retrieve cbId on %s", node) + return def generate_ids(nodes, asset_id=None): @@ -828,7 +839,6 @@ def set_id(node, unique_id, overwrite=False): """ - attr = "{0}.cbId".format(node) exists = cmds.attributeQuery("cbId", node=node, exists=True) # Add the attribute if it does not exist yet @@ -837,6 +847,7 @@ def set_id(node, unique_id, overwrite=False): # Set the value if not exists or overwrite: + attr = "{0}.cbId".format(node) cmds.setAttr(attr, unique_id, type="string") From ddefd09bc2cdf1d7ee87f761c073dcabe959888a Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 28 Sep 2018 10:45:40 +0200 Subject: [PATCH 056/121] added support for writing vertex colors --- colorbleed/plugins/maya/create/colorbleed_model.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/colorbleed/plugins/maya/create/colorbleed_model.py b/colorbleed/plugins/maya/create/colorbleed_model.py index b55b3dc3dd..d4fa5c5a3d 100644 --- a/colorbleed/plugins/maya/create/colorbleed_model.py +++ b/colorbleed/plugins/maya/create/colorbleed_model.py @@ -1,3 +1,5 @@ +from collections import OrderedDict + import avalon.maya @@ -8,3 +10,14 @@ class CreateModel(avalon.maya.Creator): label = "Model" family = "colorbleed.model" icon = "cube" + + def __init__(self, *args, **kwargs): + super(CreateModel, self).__init__(*args, **kwargs) + + # create an ordered dict with the existing data first + data = OrderedDict(**self.data) + + # Write vertex colors with the geometry. + data["writeColorSets"] = False + + self.data = data From 21bbcc200a7d03a63dc5d43e46b383d08dffb757 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 28 Sep 2018 10:47:48 +0200 Subject: [PATCH 057/121] Set default value to True --- colorbleed/plugins/maya/create/colorbleed_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/create/colorbleed_model.py b/colorbleed/plugins/maya/create/colorbleed_model.py index d4fa5c5a3d..c69e4bbf7b 100644 --- a/colorbleed/plugins/maya/create/colorbleed_model.py +++ b/colorbleed/plugins/maya/create/colorbleed_model.py @@ -18,6 +18,6 @@ class CreateModel(avalon.maya.Creator): data = OrderedDict(**self.data) # Write vertex colors with the geometry. - data["writeColorSets"] = False + data["writeColorSets"] = True self.data = data From 307c635e918acb91638b0fad81c78fa36aa03fd3 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 28 Sep 2018 10:51:34 +0200 Subject: [PATCH 058/121] disabled log, gave error on missing keys when exporting static frame --- colorbleed/plugins/houdini/publish/extract_vdb_cache.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/extract_vdb_cache.py b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py index ad85a5daf0..4721486131 100644 --- a/colorbleed/plugins/houdini/publish/extract_vdb_cache.py +++ b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py @@ -31,9 +31,9 @@ class ExtractVDBCache(colorbleed.api.Extractor): file_name.replace(frame_nr, "%04d") # We run the render - self.log.info( - "Starting render: {startFrame} - {endFrame}".format(**instance.data) - ) + #self.log.info( + # "Starting render: {startFrame} - {endFrame}".format(**instance.data) + #) ropnode.render() if "files" not in instance.data: From ebea792845475101ff993e2bf08e1c69e8705a5b Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 28 Sep 2018 10:53:51 +0200 Subject: [PATCH 059/121] cosmetic --- colorbleed/plugins/houdini/publish/collection_animation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/collection_animation.py b/colorbleed/plugins/houdini/publish/collection_animation.py index 835e52eb68..60204d0eec 100644 --- a/colorbleed/plugins/houdini/publish/collection_animation.py +++ b/colorbleed/plugins/houdini/publish/collection_animation.py @@ -25,7 +25,7 @@ class CollectAnimation(pyblish.api.InstancePlugin): "f2": "endFrame", "f3": "steps"} - data = {name: node.parm(par).eval() for par, name in + data = {name: node.evalParm(parm) for parm, name in parameters.items()} instance.data.update(data) From 48d01aea92cb03c913e77ca327286a56bac9b771 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 28 Sep 2018 15:18:31 +0200 Subject: [PATCH 060/121] Use $HIP/pyblsih as output directory --- colorbleed/plugins/houdini/create/create_alembic_camera.py | 2 +- colorbleed/plugins/houdini/create/create_pointcache.py | 2 +- colorbleed/plugins/houdini/create/create_vbd_cache.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_alembic_camera.py b/colorbleed/plugins/houdini/create/create_alembic_camera.py index 43213f46b0..aee47a55ad 100644 --- a/colorbleed/plugins/houdini/create/create_alembic_camera.py +++ b/colorbleed/plugins/houdini/create/create_alembic_camera.py @@ -27,7 +27,7 @@ class CreateAlembicCamera(houdini.Creator): parms = {"use_sop_path": True, "build_from_path": True, "path_attrib": "path", - "filename": "$HIP/%s.abc" % self.name} + "filename": "$HIP/pyblish/%s.abc" % self.name} if self.nodes: node = self.nodes[0] diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 25671c787e..03c7fc3deb 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -28,7 +28,7 @@ class CreatePointCache(houdini.Creator): parms = {"use_sop_path": True, "build_from_path": True, "path_attrib": "path", - "filename": "$HIP/%s.abc" % self.name} + "filename": "$HIP/pyblish/%s.abc" % self.name} if self.nodes: node = self.nodes[0] diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index b103a046fa..0c26e68053 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -25,7 +25,7 @@ class CreateVDBCache(houdini.Creator): def process(self): instance = super(CreateVDBCache, self).process() - parms = {"sopoutput": "$HIP/geo/%s.$F4.vdb" % self.name} + parms = {"sopoutput": "$HIP/pyblish/%s.$F4.vdb" % self.name} if self.nodes: parms.update({"soppath": self.nodes[0].path()}) From cb3f8fcad99b7efcfbc191254cdbf2da7fc513c5 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 28 Sep 2018 18:01:55 +0200 Subject: [PATCH 061/121] Added support for executeBackground --- .../houdini/create/create_vbd_cache.py | 13 +++----- .../houdini/publish/extract_vdb_cache.py | 31 ++++++++++++++----- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index 0c26e68053..325ec32cbe 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -1,5 +1,3 @@ -from collections import OrderedDict - from avalon import houdini @@ -14,13 +12,10 @@ class CreateVDBCache(houdini.Creator): def __init__(self, *args, **kwargs): super(CreateVDBCache, self).__init__(*args, **kwargs) - # create an ordered dict with the existing data first - data = OrderedDict(**self.data) - - # Set node type to create for output - data["node_type"] = "geometry" - - self.data = data + self.data.update({ + "node_type": "geometry", # Set node type to create for output + "executeBackground": True # Render node in background + }) def process(self): instance = super(CreateVDBCache, self).process() diff --git a/colorbleed/plugins/houdini/publish/extract_vdb_cache.py b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py index 4721486131..ed9e211f63 100644 --- a/colorbleed/plugins/houdini/publish/extract_vdb_cache.py +++ b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py @@ -4,6 +4,8 @@ import re import pyblish.api import colorbleed.api +import hou + class ExtractVDBCache(colorbleed.api.Extractor): @@ -17,9 +19,9 @@ class ExtractVDBCache(colorbleed.api.Extractor): ropnode = instance[0] # Get the filename from the filename parameter - # `.eval()` will make sure all tokens are resolved - output = ropnode.parm("sopoutput").eval() - staging_dir = os.path.dirname(output) + # `.evalParm(parameter)` will make sure all tokens are resolved + output = ropnode.evalParm("sopoutput") + staging_dir = os.path.normpath(os.path.dirname(output)) instance.data["stagingDir"] = staging_dir # Replace the 4 digits to match file sequence token '%04d' if we have @@ -31,10 +33,25 @@ class ExtractVDBCache(colorbleed.api.Extractor): file_name.replace(frame_nr, "%04d") # We run the render - #self.log.info( - # "Starting render: {startFrame} - {endFrame}".format(**instance.data) - #) - ropnode.render() + start_frame = instance.data.get("startFrame", None) + end_frame = instance.data.get("endFrame", None) + if all(f for f in [start_frame, end_frame]): + self.log.info( + "Starting render: {} - {}".format(start_frame, end_frame) + ) + + # Ensure output folder exists + if not os.path.isdir(staging_dir): + os.makedirs(staging_dir) + + assert os.path.exists(staging_dir) + + if instance.data.get("executeBackground", True): + self.log.info("Creating background task..") + ropnode.parm("executebackground").pressButton() + self.log.info("Finished") + else: + ropnode.render() if "files" not in instance.data: instance.data["files"] = [] From 2bad4947abe41deb341a2654f6ab1350a016b54e Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 1 Oct 2018 15:39:31 +0200 Subject: [PATCH 062/121] Added support for parameter callbacks --- colorbleed/houdini/lib.py | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/colorbleed/houdini/lib.py b/colorbleed/houdini/lib.py index cf526ba2b9..949d970841 100644 --- a/colorbleed/houdini/lib.py +++ b/colorbleed/houdini/lib.py @@ -95,6 +95,59 @@ def get_additional_data(container): return container +def set_parameter_callback(node, parameter, language, callback): + """Link a callback to a parameter of a node + + Args: + node(hou.Node): instance of the nodee + parameter(str): name of the parameter + language(str): name of the language, e.g.: python + callback(str): command which needs to be triggered + + Returns: + None + + """ + + template_grp = node.parmTemplateGroup() + template = template_grp.find(parameter) + if not template: + return + + script_language = (hou.scriptLanguage.Python if language == "python" else + hou.scriptLanguage.Hscript) + + template.setScriptCallbackLanguage(script_language) + template.setScriptCallback(callback) + + template.setTags({"script_callback": callback, + "script_callback_language": language.lower()}) + + # Replace the existing template with the adjusted one + template_grp.replace(parameter, template) + + node.setParmTemplateGroup(template_grp) + + +def set_parameter_callbacks(node, parameter_callbacks): + """Set callbacks for multiple parameters of a node + + Args: + node(hou.Node): instance of a hou.Node + parameter_callbacks(dict): collection of parameter and callback data + example: {"active" : + {"language": "python", + "callback": "print('hello world)'"} + } + Returns: + None + """ + for parameter, data in parameter_callbacks.items(): + language = data["language"] + callback = data["callback"] + + set_parameter_callback(node, parameter, language, callback) + @contextmanager def attribute_values(node, data): From 2856dbee8706569588271929ed224e4136bb3d4f Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 1 Oct 2018 16:07:21 +0200 Subject: [PATCH 063/121] Updated instances --- .../plugins/houdini/create/create_alembic_camera.py | 8 +++----- colorbleed/plugins/houdini/create/create_pointcache.py | 9 +++------ colorbleed/plugins/houdini/create/create_vbd_cache.py | 3 +++ 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_alembic_camera.py b/colorbleed/plugins/houdini/create/create_alembic_camera.py index aee47a55ad..6d35e3bd02 100644 --- a/colorbleed/plugins/houdini/create/create_alembic_camera.py +++ b/colorbleed/plugins/houdini/create/create_alembic_camera.py @@ -13,13 +13,11 @@ class CreateAlembicCamera(houdini.Creator): def __init__(self, *args, **kwargs): super(CreateAlembicCamera, self).__init__(*args, **kwargs) - # create an ordered dict with the existing data first - data = OrderedDict(**self.data) + # Remove the active, we are checking the bypass flag of the nodes + self.data.pop("active", None) # Set node type to create for output - data["node_type"] = "alembic" - - self.data = data + self.data.update({"node_type": "alembic"}) def process(self): instance = super(CreateAlembicCamera, self).process() diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 03c7fc3deb..fdc5ff3598 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -14,13 +14,10 @@ class CreatePointCache(houdini.Creator): def __init__(self, *args, **kwargs): super(CreatePointCache, self).__init__(*args, **kwargs) - # create an ordered dict with the existing data first - data = OrderedDict(**self.data) + # Remove the active, we are checking the bypass flag of the nodes + self.data.pop("active", None) - # Set node type to create for output - data["node_type"] = "alembic" - - self.data = data + self.data.update({"node_type": "alembic"}) def process(self): instance = super(CreatePointCache, self).process() diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index 325ec32cbe..cf84b10742 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -12,6 +12,9 @@ class CreateVDBCache(houdini.Creator): def __init__(self, *args, **kwargs): super(CreateVDBCache, self).__init__(*args, **kwargs) + # Remove the active, we are checking the bypass flag of the nodes + self.data.pop("active", None) + self.data.update({ "node_type": "geometry", # Set node type to create for output "executeBackground": True # Render node in background From 9ae437e2dfde03369ac61ecdcd987d41e40417fd Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 1 Oct 2018 16:10:30 +0200 Subject: [PATCH 064/121] Updated method of getting parameter values. Check active state with `not isBypassed` --- colorbleed/plugins/houdini/publish/collect_instances.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/collect_instances.py b/colorbleed/plugins/houdini/publish/collect_instances.py index effd6e392e..387c587b25 100644 --- a/colorbleed/plugins/houdini/publish/collect_instances.py +++ b/colorbleed/plugins/houdini/publish/collect_instances.py @@ -38,13 +38,15 @@ class CollectInstances(pyblish.api.ContextPlugin): if not node.parm("id"): continue - if node.parm("id").eval() != "pyblish.avalon.instance": + if node.evalParm("id") != "pyblish.avalon.instance": continue - has_family = node.parm("family").eval() + has_family = node.evalParm("family") assert has_family, "'%s' is missing 'family'" % node.name() data = lib.read(node) + # Check bypass state and reverse + data.update({"active": not node.isBypassed}) # temporarily translation of `active` to `publish` till issue has # been resolved, https://github.com/pyblish/pyblish-base/issues/307 From 381a955b3ffd30f2d9723fa5d0cd9385842969ea Mon Sep 17 00:00:00 2001 From: wikoreman Date: Mon, 1 Oct 2018 16:13:54 +0200 Subject: [PATCH 065/121] Added missing parentheses --- colorbleed/plugins/houdini/publish/collect_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/collect_instances.py b/colorbleed/plugins/houdini/publish/collect_instances.py index 387c587b25..61d4cdbe0b 100644 --- a/colorbleed/plugins/houdini/publish/collect_instances.py +++ b/colorbleed/plugins/houdini/publish/collect_instances.py @@ -46,7 +46,7 @@ class CollectInstances(pyblish.api.ContextPlugin): data = lib.read(node) # Check bypass state and reverse - data.update({"active": not node.isBypassed}) + data.update({"active": not node.isBypassed()}) # temporarily translation of `active` to `publish` till issue has # been resolved, https://github.com/pyblish/pyblish-base/issues/307 From 48ed86a1fb150267bbc8761fae30e2ff453ac3ce Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:44:58 +0200 Subject: [PATCH 066/121] Added validator for vdb animation settings --- .../publish/validate_animation_settings.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/validate_animation_settings.py diff --git a/colorbleed/plugins/houdini/publish/validate_animation_settings.py b/colorbleed/plugins/houdini/publish/validate_animation_settings.py new file mode 100644 index 0000000000..92955efbe8 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/validate_animation_settings.py @@ -0,0 +1,50 @@ +import pyblish.api + +from colorbleed.houdini import lib + + +class ValidateAnimationSettings(pyblish.api.InstancePlugin): + """Validate if the unexpanded string contains the frame ('$F') token + + This validator will only check the output parameter of the node if + the Valid Frame Range is not set to 'Render Current Frame' + + Rules: + If you render out a frame range it is mandatory to have the + frame token - '$F4' or similar - to ensure that each frame gets + written. If this is not the case you will override the same file + every time a frame is written out. + + Examples: + Good: 'my_vbd_cache.$F4.vdb' + Bad: 'my_vbd_cache.vdb' + + """ + + order = pyblish.api.ValidatorOrder + label = "Validate Frame Settings" + families = ["colorbleed.vdbcache"] + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Output settings do no match for '%s'" % + instance) + + @classmethod + def get_invalid(cls, instance): + + node = instance[0] + + # Check trange parm, 0 means Render Current Frame + frame_range = node.evalParm("trange") + if frame_range == 0: + return [] + + output_parm = lib.get_output_parameter(node) + unexpanded_str = output_parm.unexpandedString() + + if "$F" not in unexpanded_str: + cls.log.error("No frame token found in '%s'" % node.path()) + return [instance] From 455a7dbf60eebb76dcbc6fc83716ae7c660c4965 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:45:39 +0200 Subject: [PATCH 067/121] Added estamated file list for vdbcache --- .../plugins/houdini/publish/collect_frames.py | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 colorbleed/plugins/houdini/publish/collect_frames.py diff --git a/colorbleed/plugins/houdini/publish/collect_frames.py b/colorbleed/plugins/houdini/publish/collect_frames.py new file mode 100644 index 0000000000..d08162d332 --- /dev/null +++ b/colorbleed/plugins/houdini/publish/collect_frames.py @@ -0,0 +1,72 @@ +import os +import re + +import pyblish.api + + +class CollectFrames(pyblish.api.InstancePlugin): + """Collect all frames which would be a resukl""" + + order = pyblish.api.CollectorOrder + label = "Collect Frames" + families = ["colorbleed.vdbcache"] + + def process(self, instance): + + ropnode = instance[0] + + node_type = ropnode.type().name() + if node_type == "geometry": + output_parm = "sopoutput" + elif node_type == "alembic": + output_parm = "filename" + else: + raise TypeError("Node type '%s' not supported" % node_type) + + output = ropnode.evalParm(output_parm) + + file_name = os.path.basename(output) + match = re.match("(\w+)\.(\d+)\.vdb", file_name) + result = file_name + + start_frame = instance.data.get("startFrame", None) + end_frame = instance.data.get("endFrame", None) + + if match and start_frame is not None: + + # Check if frames are bigger than 1 (file collection) + # override the result + if end_frame - start_frame > 1: + result = self.create_file_list(match, + int(start_frame), + int(end_frame)) + + instance.data.update({"frames": result}) + + def create_file_list(self, match, start_frame, end_frame): + """Collect files based on frame range and regex.match + + Args: + match(re.match): match object + start_frame(int): start of the animation + end_frame(int): end of the animation + + Returns: + list + + """ + + result = [] + + padding = len(match.group(2)) + name = match.group(1) + padding_format = "{number:0{width}d}" + + count = start_frame + while count <= end_frame: + str_count = padding_format.format(number=count, width=padding) + file_name = "{}.{}.vdb".format(name, str_count) + result.append(file_name) + count += 1 + + return result From 01bf82484565d81a5790ebbaf44639bb024106c5 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:46:08 +0200 Subject: [PATCH 068/121] Added comments about settings, force format on creation --- colorbleed/plugins/houdini/create/create_pointcache.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index fdc5ff3598..698594796d 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -1,5 +1,3 @@ -from collections import OrderedDict - from avalon import houdini @@ -22,9 +20,10 @@ class CreatePointCache(houdini.Creator): def process(self): instance = super(CreatePointCache, self).process() - parms = {"use_sop_path": True, - "build_from_path": True, - "path_attrib": "path", + parms = {"use_sop_path": True, # Export single node from SOP Path + "build_from_path": True, # Direct path of primitive in output + "path_attrib": "path", # Pass path attribute for output + "format": 2, # Set format to Ogawa "filename": "$HIP/pyblish/%s.abc" % self.name} if self.nodes: From 447029a6a665de5f4de1b0fc57524e29fd8a5103 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:46:52 +0200 Subject: [PATCH 069/121] Moved chunk for file collection to collector --- .../houdini/publish/extract_vdb_cache.py | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/extract_vdb_cache.py b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py index ed9e211f63..a489eb2dbb 100644 --- a/colorbleed/plugins/houdini/publish/extract_vdb_cache.py +++ b/colorbleed/plugins/houdini/publish/extract_vdb_cache.py @@ -1,11 +1,8 @@ import os -import re import pyblish.api import colorbleed.api -import hou - class ExtractVDBCache(colorbleed.api.Extractor): @@ -20,32 +17,10 @@ class ExtractVDBCache(colorbleed.api.Extractor): # Get the filename from the filename parameter # `.evalParm(parameter)` will make sure all tokens are resolved - output = ropnode.evalParm("sopoutput") - staging_dir = os.path.normpath(os.path.dirname(output)) + sop_output = ropnode.evalParm("sopoutput") + staging_dir = os.path.normpath(os.path.dirname(sop_output)) instance.data["stagingDir"] = staging_dir - # Replace the 4 digits to match file sequence token '%04d' if we have - # a sequence of frames - file_name = os.path.basename(output) - has_frame = re.match("\w\.(d+)\.vdb", file_name) - if has_frame: - frame_nr = has_frame.group() - file_name.replace(frame_nr, "%04d") - - # We run the render - start_frame = instance.data.get("startFrame", None) - end_frame = instance.data.get("endFrame", None) - if all(f for f in [start_frame, end_frame]): - self.log.info( - "Starting render: {} - {}".format(start_frame, end_frame) - ) - - # Ensure output folder exists - if not os.path.isdir(staging_dir): - os.makedirs(staging_dir) - - assert os.path.exists(staging_dir) - if instance.data.get("executeBackground", True): self.log.info("Creating background task..") ropnode.parm("executebackground").pressButton() @@ -56,4 +31,6 @@ class ExtractVDBCache(colorbleed.api.Extractor): if "files" not in instance.data: instance.data["files"] = [] - instance.data["files"].append(file_name) + output = instance.data["frames"] + + instance.data["files"].append(output) From 32936a19ab36ed974427a9a2084e68fae668a722 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:47:11 +0200 Subject: [PATCH 070/121] Added get_output_parameter function --- colorbleed/houdini/lib.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/colorbleed/houdini/lib.py b/colorbleed/houdini/lib.py index 949d970841..78adbc5790 100644 --- a/colorbleed/houdini/lib.py +++ b/colorbleed/houdini/lib.py @@ -148,6 +148,35 @@ def set_parameter_callbacks(node, parameter_callbacks): set_parameter_callback(node, parameter, language, callback) + +def get_output_parameter(node): + """Return the render output parameter name of the given node + + Example: + root = hou.node("/obj") + my_alembic_node = root.createNode("alembic") + get_output_parameter(my_alembic_node) + # Result: "output" + + Args: + node(hou.Node): node instance + + Returns: + hou.Parm + + """ + + node_type = node.type().name() + if node_type == "geometry": + return node.parm("sopoutput") + + elif node_type == "alembic": + return node.parm("filename") + + else: + raise TypeError("Node type '%s' not supported" % node_type) + + @contextmanager def attribute_values(node, data): From b3de7e2ec5394a01e5fc102208e7a63c57b1c78f Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:48:56 +0200 Subject: [PATCH 071/121] Updated plugin --- colorbleed/plugins/houdini/publish/collect_frames.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/colorbleed/plugins/houdini/publish/collect_frames.py b/colorbleed/plugins/houdini/publish/collect_frames.py index d08162d332..49fa7378c5 100644 --- a/colorbleed/plugins/houdini/publish/collect_frames.py +++ b/colorbleed/plugins/houdini/publish/collect_frames.py @@ -2,6 +2,7 @@ import os import re import pyblish.api +from colorbleed.houdini import lib class CollectFrames(pyblish.api.InstancePlugin): @@ -15,15 +16,8 @@ class CollectFrames(pyblish.api.InstancePlugin): ropnode = instance[0] - node_type = ropnode.type().name() - if node_type == "geometry": - output_parm = "sopoutput" - elif node_type == "alembic": - output_parm = "filename" - else: - raise TypeError("Node type '%s' not supported" % node_type) - - output = ropnode.evalParm(output_parm) + output_parm = lib.get_output_parameter(ropnode) + output = output_parm.eval() file_name = os.path.basename(output) match = re.match("(\w+)\.(\d+)\.vdb", file_name) From 6ab87afd9c5c8e6679f7bbf3cda4f473c8bead4c Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:50:14 +0200 Subject: [PATCH 072/121] Fixed error message --- colorbleed/plugins/houdini/publish/validate_output_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/publish/validate_output_node.py b/colorbleed/plugins/houdini/publish/validate_output_node.py index 18fcb4898f..d3393e1c33 100644 --- a/colorbleed/plugins/houdini/publish/validate_output_node.py +++ b/colorbleed/plugins/houdini/publish/validate_output_node.py @@ -33,7 +33,7 @@ class ValidateOutputNode(pyblish.api.InstancePlugin): # Check if type is correct type_name = output_node.type().name() if type_name not in ["output", "cam"]: - cls.log.error("Output node `%s` is an accepted type `output` " + cls.log.error("Output node `%s` is not an accepted type `output` " "or `camera`" % output_node.path()) return [output_node.path()] From b751dd3a1585583a71668982c4549fb14e4885d3 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 10:51:22 +0200 Subject: [PATCH 073/121] Minor improvement --- colorbleed/plugins/houdini/load/load_alembic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/load/load_alembic.py b/colorbleed/plugins/houdini/load/load_alembic.py index ca5741ecc3..235d316c05 100644 --- a/colorbleed/plugins/houdini/load/load_alembic.py +++ b/colorbleed/plugins/houdini/load/load_alembic.py @@ -42,7 +42,7 @@ class AbcLoader(api.Loader): container = obj.createNode("geo", node_name=node_name) # Remove the file node, it only loads static meshes - file_node = hou.node("/obj/{}/file1".format(node_name)) + file_node = container.node("file1".format(node_name)) file_node.destroy() # Create an alembic node (supports animation) From ba482725bff60ce4cb871d21f9185287e8005b76 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 11:23:02 +0200 Subject: [PATCH 074/121] Toggle initsim at creation --- colorbleed/plugins/houdini/create/create_vbd_cache.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index cf84b10742..5a52f6da7f 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -23,7 +23,9 @@ class CreateVDBCache(houdini.Creator): def process(self): instance = super(CreateVDBCache, self).process() - parms = {"sopoutput": "$HIP/pyblish/%s.$F4.vdb" % self.name} + parms = {"sopoutput": "$HIP/pyblish/%s.$F4.vdb" % self.name, + "initsim": True} + if self.nodes: parms.update({"soppath": self.nodes[0].path()}) From 343799731e954a3dfff61159c0762ceb2f1eecf7 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Tue, 2 Oct 2018 11:23:57 +0200 Subject: [PATCH 075/121] Removed unused modules --- .../houdini/publish/collect_alembic_nodes.py | 9 ------ .../houdini/publish/collection_animation.py | 31 ------------------- 2 files changed, 40 deletions(-) delete mode 100644 colorbleed/plugins/houdini/publish/collect_alembic_nodes.py delete mode 100644 colorbleed/plugins/houdini/publish/collection_animation.py diff --git a/colorbleed/plugins/houdini/publish/collect_alembic_nodes.py b/colorbleed/plugins/houdini/publish/collect_alembic_nodes.py deleted file mode 100644 index dc0de42126..0000000000 --- a/colorbleed/plugins/houdini/publish/collect_alembic_nodes.py +++ /dev/null @@ -1,9 +0,0 @@ -import pyblish.api - - -class CollectAlembicNodes(pyblish.api.InstancePlugin): - - label = "Collect Alembic Nodes" - - def process(self, instance): - pass \ No newline at end of file diff --git a/colorbleed/plugins/houdini/publish/collection_animation.py b/colorbleed/plugins/houdini/publish/collection_animation.py deleted file mode 100644 index 60204d0eec..0000000000 --- a/colorbleed/plugins/houdini/publish/collection_animation.py +++ /dev/null @@ -1,31 +0,0 @@ -import pyblish.api - - -class CollectAnimation(pyblish.api.InstancePlugin): - """Collect the animation data for the data base - - Data collected: - - start frame - - end frame - - nr of steps - - """ - - order = pyblish.api.CollectorOrder - families = ["colorbleed.pointcache"] - hosts = ["houdini"] - label = "Collect Animation" - - def process(self, instance): - - node = instance[0] - - # Get animation parameters for data - parameters = {"f1": "startFrame", - "f2": "endFrame", - "f3": "steps"} - - data = {name: node.evalParm(parm) for parm, name in - parameters.items()} - - instance.data.update(data) From abf79377b56efc38d49516f117565eca07ce070c Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 2 Oct 2018 17:37:09 +0200 Subject: [PATCH 076/121] Implement draft for ValidateLookIdReferenceEdits (LKD-16) --- .../validate_look_id_reference_edits.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py diff --git a/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py b/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py new file mode 100644 index 0000000000..3b5615c9f4 --- /dev/null +++ b/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py @@ -0,0 +1,44 @@ +from maya import cmds + +import pyblish.api +import colorbleed.api + + +class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin): + """Validate nodes in look have no reference edits to cbId.""" + + order = colorbleed.api.ValidateContentsOrder + families = ['colorbleed.look'] + hosts = ['maya'] + label = 'Look Id Reference Edits' + actions = [colorbleed.api.SelectInvalidAction] + + def process(self, instance): + + invalid = self.get_invalid(instance) + + if invalid: + raise RuntimeError("Invalid nodes %s" % (invalid,)) + + @staticmethod + def get_invalid(instance): + + # Collect all referenced members + referenced_nodes = [] + relationships = instance.data["lookData"]["relationships"] + for relationship in relationships.values(): + for member in relationship['members']: + node = member["name"] + + if cmds.referenceQuery(node, isNodeReferenced=True): + referenced_nodes.append(node) + + # Validate whether any has changes to 'cbId' attribute + # TODO: optimize this query (instead of per node) + invalid = list() + for node in referenced_nodes: + edits = set(cmds.referenceQuery(node, editAttrs=True)) + if "cbId" in edits: + invalid.append(node) + + return invalid From 3296583c814483e9097b6dd4c69464983444cc16 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 3 Oct 2018 12:10:09 +0200 Subject: [PATCH 077/121] Improve query speed, add repair functionality --- .../validate_look_id_reference_edits.py | 73 ++++++++++++++++--- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py b/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py index 3b5615c9f4..b2608b1cdf 100644 --- a/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py +++ b/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py @@ -1,3 +1,4 @@ +from collections import defaultdict from maya import cmds import pyblish.api @@ -5,16 +6,23 @@ import colorbleed.api class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin): - """Validate nodes in look have no reference edits to cbId.""" + """Validate nodes in look have no reference edits to cbId. + + Note: + This only validates the cbId edits on the referenced nodes that are + used in the look. For example, a transform can have its cbId changed + without being invalidated when it is not used in the look's assignment. + + """ order = colorbleed.api.ValidateContentsOrder families = ['colorbleed.look'] hosts = ['maya'] label = 'Look Id Reference Edits' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.api.SelectInvalidAction, + colorbleed.api.RepairAction] def process(self, instance): - invalid = self.get_invalid(instance) if invalid: @@ -24,21 +32,66 @@ class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin): def get_invalid(instance): # Collect all referenced members - referenced_nodes = [] + references = defaultdict(set) relationships = instance.data["lookData"]["relationships"] for relationship in relationships.values(): for member in relationship['members']: node = member["name"] if cmds.referenceQuery(node, isNodeReferenced=True): - referenced_nodes.append(node) + ref = cmds.referenceQuery(node, referenceNode=True) + references[ref].add(node) # Validate whether any has changes to 'cbId' attribute - # TODO: optimize this query (instead of per node) invalid = list() - for node in referenced_nodes: - edits = set(cmds.referenceQuery(node, editAttrs=True)) - if "cbId" in edits: - invalid.append(node) + for ref, nodes in references.items(): + edits = cmds.referenceQuery(editAttrs=True, + editNodes=True, + showDagPath=True, + showNamespace=True, + onReferenceNode=ref) + for edit in edits: + + # Ensure it is an attribute ending with .cbId + # thus also ignore just node edits (like parenting) + if not edit.endswith(".cbId"): + continue + + # Ensure the attribute is 'cbId' (and not a nested attribute) + node, attr = edit.split(".", 1) + if attr != "cbId": + continue + + if node in nodes: + invalid.append(node) return invalid + + @classmethod + def repair(cls, instance): + + invalid = cls.get_invalid(instance) + + # Group invalid nodes by reference node + references = defaultdict(set) + for node in invalid: + ref = cmds.referenceQuery(node, referenceNode=True) + references[ref].add(node) + + # Remove the reference edits on the nodes per reference node + for ref, nodes in references.items(): + for node in nodes: + + # Somehow this only works if you run the the removal + # per edit command. + for command in ["addAttr", + "connectAttr", + "deleteAttr", + "disconnectAttr", + "setAttr"]: + cmds.referenceEdit("{}.cbId".format(node), + removeEdits=True, + successfulEdits=True, + failedEdits=True, + editCommand=command, + onReferenceNode=ref) From 792bae854bc04cb86121ae26c08165d71c309a47 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 3 Oct 2018 12:18:04 +0200 Subject: [PATCH 078/121] Removed out from the sop_path value --- colorbleed/plugins/houdini/create/create_pointcache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 698594796d..481404dcec 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -28,6 +28,6 @@ class CreatePointCache(houdini.Creator): if self.nodes: node = self.nodes[0] - parms.update({"sop_path": "%s/OUT" % node.path()}) + parms.update({"sop_path": node.path()}) instance.setParms(parms) From cfe8548bdb6d8b9904969ae92d7216b611718425 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 3 Oct 2018 12:18:46 +0200 Subject: [PATCH 079/121] Consistency update --- colorbleed/plugins/houdini/create/create_vbd_cache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/create/create_vbd_cache.py b/colorbleed/plugins/houdini/create/create_vbd_cache.py index 5a52f6da7f..aadd222d41 100644 --- a/colorbleed/plugins/houdini/create/create_vbd_cache.py +++ b/colorbleed/plugins/houdini/create/create_vbd_cache.py @@ -27,6 +27,7 @@ class CreateVDBCache(houdini.Creator): "initsim": True} if self.nodes: - parms.update({"soppath": self.nodes[0].path()}) + node = self.nodes[0] + parms.update({"sop_path": node.path()}) instance.setParms(parms) From 63ce0b8218f8747a3a15eddd532ce3b0eb8b134e Mon Sep 17 00:00:00 2001 From: wikoreman Date: Wed, 3 Oct 2018 17:16:39 +0200 Subject: [PATCH 080/121] fixed bug in assertion --- colorbleed/plugins/maya/publish/collect_yeti_rig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/collect_yeti_rig.py b/colorbleed/plugins/maya/publish/collect_yeti_rig.py index 3198120374..feb7a19199 100644 --- a/colorbleed/plugins/maya/publish/collect_yeti_rig.py +++ b/colorbleed/plugins/maya/publish/collect_yeti_rig.py @@ -26,7 +26,7 @@ class CollectYetiRig(pyblish.api.InstancePlugin): def process(self, instance): - assert "input_SET" in cmds.sets(instance.name, query=True), ( + assert "input_SET" in instance.data["setMembers"], ( "Yeti Rig must have an input_SET") # Get the input meshes information From 3b1cd4743eec308176108f4b1cd145f0ca5c812d Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 4 Oct 2018 10:17:48 +0200 Subject: [PATCH 081/121] ensure time slider state is maintained when updating FPS --- colorbleed/maya/lib.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 2ea618c178..73794b4462 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1404,9 +1404,19 @@ def set_scene_fps(fps, update=True): else: raise ValueError("Unsupported FPS value: `%s`" % fps) + # Get time slider current state + start_frame = cmds.playbackOptions(query=True, minTime=True) + end_frame = cmds.playbackOptions(query=True, maxTime=True) + current_frame = cmds.currentTime(query=True) + log.info("Updating FPS to '{}'".format(unit)) cmds.currentUnit(time=unit, updateAnimation=update) + # Set time slider data back to previous state + cmds.playbackOptions(edit=True, minTime=start_frame) + cmds.playbackOptions(edit=True, maxTime=end_frame) + cmds.currentTime(current_frame, edit=True, update=True) + # Force file stated to 'modified' cmds.file(modified=True) From 8b17f5b6f3a0472a03780b0516a5f165c6a8ac7d Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 4 Oct 2018 10:34:55 +0200 Subject: [PATCH 082/121] maintain animation start and end frames --- colorbleed/maya/lib.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 73794b4462..d4b0b8aa73 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1407,6 +1407,11 @@ def set_scene_fps(fps, update=True): # Get time slider current state start_frame = cmds.playbackOptions(query=True, minTime=True) end_frame = cmds.playbackOptions(query=True, maxTime=True) + + # Get animation data + animation_start = cmds.playbackOptions(query=True, animationStartTime=True) + animation_end = cmds.playbackOptions(query=True, animationEndTime=True) + current_frame = cmds.currentTime(query=True) log.info("Updating FPS to '{}'".format(unit)) @@ -1415,6 +1420,11 @@ def set_scene_fps(fps, update=True): # Set time slider data back to previous state cmds.playbackOptions(edit=True, minTime=start_frame) cmds.playbackOptions(edit=True, maxTime=end_frame) + + # Set animation data + cmds.playbackOptions(edit=True, animationStartTime=animation_start) + cmds.playbackOptions(edit=True, animationEndTime=animation_end) + cmds.currentTime(current_frame, edit=True, update=True) # Force file stated to 'modified' From 7114fcf6fc31ce564806e9d365a93e62503593e0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 4 Oct 2018 13:46:51 +0200 Subject: [PATCH 083/121] Implement MOD-10 validate default "map1" uv set --- .../maya/publish/validate_mesh_uv_set_map1.py | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py diff --git a/colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py b/colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py new file mode 100644 index 0000000000..878549b539 --- /dev/null +++ b/colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py @@ -0,0 +1,90 @@ +from maya import cmds + +import pyblish.api +import colorbleed.api + + +class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin): + """Validate model's default set exists and is named 'map1'. + + In Maya meshes by default have a uv set named "map1" that cannot be + deleted. It can be renamed however, introducing some issues with some + renderers. As such we ensure the first (default) UV set index is named + "map1". + + """ + + order = colorbleed.api.ValidateMeshOrder + hosts = ['maya'] + families = ['colorbleed.model'] + optional = True + label = "Mesh has map1 UV Set" + actions = [colorbleed.api.SelectInvalidAction, + colorbleed.api.RepairAction] + + @staticmethod + def get_invalid(instance): + + meshes = cmds.ls(instance, type='mesh', long=True) + + invalid = [] + for mesh in meshes: + + # Get existing mapping of uv sets by index + indices = cmds.polyUVSet(mesh, query=True, allUVSetsIndices=True) + maps = cmds.polyUVSet(mesh, query=True, allUVSets=True) + mapping = dict(zip(indices, maps)) + + # Get the uv set at index zero. + name = mapping[0] + if name != "map1": + invalid.append(mesh) + + return invalid + + def process(self, instance): + """Process all the nodes in the instance 'objectSet'""" + + invalid = self.get_invalid(instance) + if invalid: + raise ValueError("Meshes found without 'map1' " + "UV set: {0}".format(invalid)) + + @classmethod + def repair(cls, instance): + """Rename uv map at index zero to map1""" + + for mesh in cls.get_invalid(instance): + + # Get existing mapping of uv sets by index + indices = cmds.polyUVSet(mesh, query=True, allUVSetsIndices=True) + maps = cmds.polyUVSet(mesh, query=True, allUVSets=True) + mapping = dict(zip(indices, maps)) + + # Ensure there is no uv set named map1 to avoid + # a clash on renaming the "default uv set" to map1 + existing = set(maps) + if "map1" in existing: + + # Find a unique name index + i = 2 + while True: + name = "map{0}".format(i) + if name not in existing: + break + i += 1 + + cls.log.warning("Renaming clashing uv set name on mesh" + " %s to '%s'", mesh, name) + + cmds.polyUVSet(mesh, + rename=True, + uvSet="map1", + newUVSet=name) + + # Rename the initial index to map1 + original = mapping[0] + cmds.polyUVSet(mesh, + rename=True, + uvSet=original, + newUVSet="map1") From 3ec2bb4993ce4fb916437019b9d34db57e334f04 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 5 Oct 2018 09:57:49 +0200 Subject: [PATCH 084/121] Added validator for Yeti Rig Settings --- .../publish/validate_yeti_rig_settings.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 colorbleed/plugins/maya/publish/validate_yeti_rig_settings.py diff --git a/colorbleed/plugins/maya/publish/validate_yeti_rig_settings.py b/colorbleed/plugins/maya/publish/validate_yeti_rig_settings.py new file mode 100644 index 0000000000..8b6219d989 --- /dev/null +++ b/colorbleed/plugins/maya/publish/validate_yeti_rig_settings.py @@ -0,0 +1,38 @@ +import pyblish.api + + +class ValidateYetiRigSettings(pyblish.api.InstancePlugin): + order = pyblish.api.ValidatorOrder + label = "Validate Yeti Rig Settings" + families = ["colorbleed.yetiRig"] + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise RuntimeError("Detected invalid Yeti Rig data. " + "Tip: Save the scene") + + @classmethod + def get_invalid(cls, instance): + + rigsettings = instance.data.get("rigsettings", {}) + if not rigsettings: + cls.log.error("MAJOR ERROR: No rig settings found!") + return True + + # Get inputs + inputs = rigsettings.get("inputs", []) + for input in inputs: + source_id = input["sourceID"] + if source_id is None: + cls.log.error("Discovered source with 'None' as ID, please " + "check if the input shape has an cbId") + return True + + destination_id = input["destinationID"] + if destination_id is None: + cls.log.error("Discovered None as destination ID value") + return True + + return False From b4e22263013a938ca48db035c4ca5150f2f405c1 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 5 Oct 2018 10:05:03 +0200 Subject: [PATCH 085/121] Renamed function, improved code --- .../plugins/maya/publish/extract_yeti_rig.py | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/colorbleed/plugins/maya/publish/extract_yeti_rig.py b/colorbleed/plugins/maya/publish/extract_yeti_rig.py index 7806c0642f..6548bab60b 100644 --- a/colorbleed/plugins/maya/publish/extract_yeti_rig.py +++ b/colorbleed/plugins/maya/publish/extract_yeti_rig.py @@ -10,7 +10,7 @@ import colorbleed.maya.lib as maya @contextlib.contextmanager -def disconnected_attributes(settings, members): +def disconnect_plugs(settings, members): members = cmds.ls(members, long=True) original_connections = [] @@ -19,35 +19,32 @@ def disconnected_attributes(settings, members): # Get source shapes source_nodes = lib.lsattr("cbId", input["sourceID"]) - sources = [i for i in source_nodes if - not cmds.referenceQuery(i, isNodeReferenced=True) - and i in members] - try: - source = sources[0] - except IndexError: - print("source_id:", input["sourceID"]) + if not source_nodes: continue + source = next(s for s in source_nodes if s not in members) + # Get destination shapes (the shapes used as hook up) destination_nodes = lib.lsattr("cbId", input["destinationID"]) - destinations = [i for i in destination_nodes if i not in members - and i not in sources] - destination = destinations[0] + destination = next(i for i in destination_nodes if i in members) - # Break connection + # Create full connection connections = input["connections"] src_attribute = "%s.%s" % (source, connections[0]) dst_attribute = "%s.%s" % (destination, connections[1]) - # store connection pair + # Check if there is an actual connection if not cmds.isConnected(src_attribute, dst_attribute): + print("No connection between %s and %s" % ( + src_attribute, dst_attribute)) continue + # Break and store connection cmds.disconnectAttr(src_attribute, dst_attribute) original_connections.append([src_attribute, dst_attribute]) yield finally: - # restore connections + # Restore previous connections for connection in original_connections: try: cmds.connectAttr(connection[0], connection[1]) @@ -57,12 +54,7 @@ def disconnected_attributes(settings, members): class ExtractYetiRig(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. - - """ + """Extract the Yeti rig to a MayaAscii and write the Yeti rig data""" label = "Extract Yeti Rig" hosts = ["maya"] @@ -85,7 +77,7 @@ class ExtractYetiRig(colorbleed.api.Extractor): image_search_path = "" settings = instance.data.get("rigsettings", None) - if settings is not None: + if settings: # Create assumed destination folder for imageSearchPath assumed_temp_data = instance.data["assumedTemplateData"] @@ -100,18 +92,22 @@ class ExtractYetiRig(colorbleed.api.Extractor): with open(settings_path, "w") as fp: json.dump(settings, fp, ensure_ascii=False) + # Ensure the imageSearchPath is being remapped to the publish folder attr_value = {"%s.imageSearchPath" % n: str(image_search_path) for n in yeti_nodes} # Get input_SET members - input_set = [i for i in instance if i == "input_SET"] + input_set = next(i for i in instance if i == "input_SET") + # Get all items - set_members = cmds.sets(input_set[0], query=True) - members = cmds.listRelatives(set_members, ad=True, fullPath=True) or [] - members += cmds.ls(set_members, long=True) + set_members = cmds.sets(input_set, query=True) + set_members += cmds.listRelatives(set_members, + allDescendents=True, + fullPath=True) or [] + members = cmds.ls(set_members, long=True, objects=True) nodes = instance.data["setMembers"] - with disconnected_attributes(settings, members): + with disconnect_plugs(settings, members): with maya.attribute_values(attr_value): cmds.select(nodes, noExpand=True) cmds.file(maya_path, From f40f4a3bcbb019092ec69e2056b0bb67f7b1fe13 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 5 Oct 2018 10:06:30 +0200 Subject: [PATCH 086/121] Ensure to get all nodes --- .../plugins/maya/publish/collect_yeti_rig.py | 19 ++++++++++--------- .../plugins/maya/publish/validate_node_ids.py | 3 ++- .../maya/publish/validate_node_ids_unique.py | 3 ++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_yeti_rig.py b/colorbleed/plugins/maya/publish/collect_yeti_rig.py index feb7a19199..e63bad48ca 100644 --- a/colorbleed/plugins/maya/publish/collect_yeti_rig.py +++ b/colorbleed/plugins/maya/publish/collect_yeti_rig.py @@ -30,16 +30,18 @@ class CollectYetiRig(pyblish.api.InstancePlugin): "Yeti Rig must have an input_SET") # Get the input meshes information - input_content = cmds.sets("input_SET", query=True) - input_nodes = cmds.listRelatives(input_content, - allDescendents=True, - fullPath=True) or input_content + input_content = cmds.ls(cmds.sets("input_SET", query=True), long=True) - # Get all the shapes - input_shapes = cmds.ls(input_nodes, long=True, noIntermediate=True) + # Include children + input_content += cmds.listRelatives(input_content, + allDescendents=True, + fullPath=True) or [] + + # Ignore intermediate objects + input_content = cmds.ls(input_content, long=True, noIntermediate=True) # Store all connections - connections = cmds.listConnections(input_shapes, + connections = cmds.listConnections(input_content, source=True, destination=False, connections=True, @@ -150,7 +152,6 @@ class CollectYetiRig(pyblish.api.InstancePlugin): if re.match(re_pattern, f)] pattern = [clique.PATTERNS["frames"]] - collection, remainder = clique.assemble(files, - patterns=pattern) + collection, remainder = clique.assemble(files, patterns=pattern) return collection diff --git a/colorbleed/plugins/maya/publish/validate_node_ids.py b/colorbleed/plugins/maya/publish/validate_node_ids.py index 3bd4471788..22369ccb65 100644 --- a/colorbleed/plugins/maya/publish/validate_node_ids.py +++ b/colorbleed/plugins/maya/publish/validate_node_ids.py @@ -21,7 +21,8 @@ class ValidateNodeIDs(pyblish.api.InstancePlugin): "colorbleed.rig", "colorbleed.pointcache", "colorbleed.animation", - "colorbleed.setdress"] + "colorbleed.setdress", + "colorbleed.yetiRig"] actions = [colorbleed.api.SelectInvalidAction, colorbleed.api.GenerateUUIDsOnInvalidAction] diff --git a/colorbleed/plugins/maya/publish/validate_node_ids_unique.py b/colorbleed/plugins/maya/publish/validate_node_ids_unique.py index b8cbbd552d..61fed9faad 100644 --- a/colorbleed/plugins/maya/publish/validate_node_ids_unique.py +++ b/colorbleed/plugins/maya/publish/validate_node_ids_unique.py @@ -16,7 +16,8 @@ class ValidateNodeIdsUnique(pyblish.api.InstancePlugin): hosts = ['maya'] families = ["colorbleed.model", "colorbleed.look", - "colorbleed.rig"] + "colorbleed.rig", + "colorbleed.yetiRig"] actions = [colorbleed.api.SelectInvalidAction, colorbleed.api.GenerateUUIDsOnInvalidAction] From dcac192e9cc70ef81d997cb641889ae1179aeea0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 5 Oct 2018 10:35:25 +0200 Subject: [PATCH 087/121] Validate V-Ray proxy frame range with generic frame range validator --- colorbleed/plugins/maya/publish/validate_frame_range.py | 3 ++- colorbleed/plugins/maya/publish/validate_vrayproxy.py | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/colorbleed/plugins/maya/publish/validate_frame_range.py b/colorbleed/plugins/maya/publish/validate_frame_range.py index 010422df38..05b64ccf17 100644 --- a/colorbleed/plugins/maya/publish/validate_frame_range.py +++ b/colorbleed/plugins/maya/publish/validate_frame_range.py @@ -20,7 +20,8 @@ class ValidateFrameRange(pyblish.api.InstancePlugin): families = ["colorbleed.animation", "colorbleed.pointcache", "colorbleed.camera", - "colorbleed.renderlayer"] + "colorbleed.renderlayer", + "oolorbleed.vrayproxy"] def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_vrayproxy.py b/colorbleed/plugins/maya/publish/validate_vrayproxy.py index 40c45094db..f722dc6129 100644 --- a/colorbleed/plugins/maya/publish/validate_vrayproxy.py +++ b/colorbleed/plugins/maya/publish/validate_vrayproxy.py @@ -21,7 +21,3 @@ class ValidateVrayProxy(pyblish.api.InstancePlugin): if not data["setMembers"]: cls.log.error("'%s' is empty! This is a bug" % instance.name) - - if data["animation"]: - if data["endFrame"] < data["startFrame"]: - cls.log.error("End frame is smaller than start frame") From f8f8bd2ebf2852eb51f26c9ceff6a0b994878d3a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 5 Oct 2018 12:20:57 +0200 Subject: [PATCH 088/121] Keep a reference to the instance objectSet upon collection --- colorbleed/plugins/maya/publish/collect_instances.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/colorbleed/plugins/maya/publish/collect_instances.py b/colorbleed/plugins/maya/publish/collect_instances.py index f23f0aa396..8ed6242036 100644 --- a/colorbleed/plugins/maya/publish/collect_instances.py +++ b/colorbleed/plugins/maya/publish/collect_instances.py @@ -115,7 +115,13 @@ class CollectInstances(pyblish.api.ContextPlugin): # Create the instance instance = context.create_instance(label) instance[:] = members_hierarchy + + # Store a reference to the object set of this instance + instance.data["objectSet"] = objset + + # Store the exact members of the object set instance.data["setMembers"] = members + instance.data.update(data) # Produce diagnostic message for any graphical From e0d7a61f7f555adfdc59ed598640279a9a165a03 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 5 Oct 2018 14:20:26 +0200 Subject: [PATCH 089/121] Fix Validate VRay Proxy actually returning a value and invalidating something --- .../publish/validate_vrayproxy_members.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 colorbleed/plugins/maya/publish/validate_vrayproxy_members.py diff --git a/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py b/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py new file mode 100644 index 0000000000..84a29fbc12 --- /dev/null +++ b/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py @@ -0,0 +1,37 @@ +import pyblish.api +import colorbleed.api + +from maya import cmds + + +class ValidateVrayProxyMembers(pyblish.api.InstancePlugin): + """Validate whether the V-Ray Proxy instance has shape members""" + + order = pyblish.api.ValidatorOrder + label = 'VRay Proxy Members' + hosts = ['maya'] + families = ['colorbleed.vrayproxy'] + actions = [colorbleed.api.SelectInvalidAction] + + def process(self, instance): + + invalid = self.get_invalid(instance) + + if invalid: + raise RuntimeError("'%s' is invalid VRay Proxy for " + "export!" % instance.name) + + @classmethod + def get_invalid(cls, instance): + + shapes = cmds.ls(instance, + shapes=True, + noIntermediate=True, + long=True) + + if not shapes: + cls.log.error("'%s' contains no shapes." % instance.name) + + # Return the instance itself + return [instance.data["objectSet"]] + From b1c6035d0a6240ee83190974ba7fe029bb2f3054 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 5 Oct 2018 14:27:17 +0200 Subject: [PATCH 090/121] Label the instances correctly instead of revising name --- .../plugins/maya/publish/collect_instances.py | 17 ++++++++--------- .../maya/publish/collect_renderlayers.py | 3 ++- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_instances.py b/colorbleed/plugins/maya/publish/collect_instances.py index 8ed6242036..807b57c710 100644 --- a/colorbleed/plugins/maya/publish/collect_instances.py +++ b/colorbleed/plugins/maya/publish/collect_instances.py @@ -102,6 +102,13 @@ class CollectInstances(pyblish.api.ContextPlugin): parents = self.get_all_parents(members) members_hierarchy = list(set(members + children + parents)) + # Create the instance + instance = context.create_instance(objset) + instance[:] = members_hierarchy + + # Store the exact members of the object set + instance.data["setMembers"] = members + # Define nice label name = cmds.ls(objset, long=False)[0] # use short name label = "{0} ({1})".format(name, @@ -112,15 +119,7 @@ class CollectInstances(pyblish.api.ContextPlugin): label += " [{0}-{1}]".format(int(data["startFrame"]), int(data["endFrame"])) - # Create the instance - instance = context.create_instance(label) - instance[:] = members_hierarchy - - # Store a reference to the object set of this instance - instance.data["objectSet"] = objset - - # Store the exact members of the object set - instance.data["setMembers"] = members + instance.data["label"] = label instance.data.update(data) diff --git a/colorbleed/plugins/maya/publish/collect_renderlayers.py b/colorbleed/plugins/maya/publish/collect_renderlayers.py index 48f7fe113c..03a879d17b 100644 --- a/colorbleed/plugins/maya/publish/collect_renderlayers.py +++ b/colorbleed/plugins/maya/publish/collect_renderlayers.py @@ -108,7 +108,8 @@ class CollectMayaRenderlayers(pyblish.api.ContextPlugin): label += " [{0}-{1}]".format(int(data["startFrame"]), int(data["endFrame"])) - instance = context.create_instance(label) + instance = context.create_instance(layername) + instance.data["label"] = label instance.data.update(data) def get_render_attribute(self, attr): From 608edcb056bf59c0a94f3ef5b7e2bb06f0b76ad5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 5 Oct 2018 14:48:19 +0200 Subject: [PATCH 091/121] Fix validate V-Ray proxy members --- .../maya/publish/validate_vrayproxy.py | 23 ------------------- .../publish/validate_vrayproxy_members.py | 2 +- 2 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 colorbleed/plugins/maya/publish/validate_vrayproxy.py diff --git a/colorbleed/plugins/maya/publish/validate_vrayproxy.py b/colorbleed/plugins/maya/publish/validate_vrayproxy.py deleted file mode 100644 index f722dc6129..0000000000 --- a/colorbleed/plugins/maya/publish/validate_vrayproxy.py +++ /dev/null @@ -1,23 +0,0 @@ -import pyblish.api - - -class ValidateVrayProxy(pyblish.api.InstancePlugin): - - order = pyblish.api.ValidatorOrder - label = 'VRay Proxy Settings' - hosts = ['maya'] - families = ['colorbleed.vrayproxy'] - - def process(self, instance): - - invalid = self.get_invalid(instance) - if invalid: - raise RuntimeError("'%s' has invalid settings for VRay Proxy " - "export!" % instance.name) - - @classmethod - def get_invalid(cls, instance): - data = instance.data - - if not data["setMembers"]: - cls.log.error("'%s' is empty! This is a bug" % instance.name) diff --git a/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py b/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py index 84a29fbc12..0ffbf3bd80 100644 --- a/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py +++ b/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py @@ -33,5 +33,5 @@ class ValidateVrayProxyMembers(pyblish.api.InstancePlugin): cls.log.error("'%s' contains no shapes." % instance.name) # Return the instance itself - return [instance.data["objectSet"]] + return [instance.name] From b97b784255199baf15344b3509ec57ac652d4c15 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Fri, 5 Oct 2018 16:18:27 +0200 Subject: [PATCH 092/121] Disallow GenerateUUIDsOnInvalidAction to regenerate ids on referenced nodes --- colorbleed/action.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/colorbleed/action.py b/colorbleed/action.py index b8e3c21d49..ba017354f0 100644 --- a/colorbleed/action.py +++ b/colorbleed/action.py @@ -41,7 +41,7 @@ def get_errored_plugins_from_data(context): class RepairAction(pyblish.api.Action): """Repairs the action - To process the repairing this requires a static `repair(instance)` method + To process the repairing this requires a static `repair(instance)` method is available on the plugin. """ @@ -67,7 +67,7 @@ class RepairAction(pyblish.api.Action): class RepairContextAction(pyblish.api.Action): """Repairs the action - To process the repairing this requires a static `repair(instance)` method + To process the repairing this requires a static `repair(instance)` method is available on the plugin. """ @@ -153,6 +153,8 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): def process(self, context, plugin): + from maya import cmds + self.log.info("Finding bad nodes..") # Get the errored instances @@ -170,6 +172,18 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): all_invalid = [] for instance in instances: invalid = plugin.get_invalid(instance) + + # Don't allow referenced nodes to get their ids regenerated to + # avoid loaded content getting messed up with reference edits + if invalid: + referenced = {node for node in invalid if + cmds.referenceQuery(node, isNodeReferenced=True)} + if referenced: + self.log.warning("Skipping UUID generation on referenced " + "nodes: {}".format(list(referenced))) + invalid = [node for node in invalid + if node not in referenced] + if invalid: self.log.info("Fixing instance {}".format(instance.name)) From 55dd74ddd583f5fb465f62a3091135aa5da24d05 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 5 Oct 2018 17:14:03 +0200 Subject: [PATCH 093/121] moved plugin to global --- colorbleed/plugins/{maya => global}/publish/validate_resources.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename colorbleed/plugins/{maya => global}/publish/validate_resources.py (100%) diff --git a/colorbleed/plugins/maya/publish/validate_resources.py b/colorbleed/plugins/global/publish/validate_resources.py similarity index 100% rename from colorbleed/plugins/maya/publish/validate_resources.py rename to colorbleed/plugins/global/publish/validate_resources.py From d8030186cd097be18479ddd7bcf1f795f58eb568 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 5 Oct 2018 17:15:21 +0200 Subject: [PATCH 094/121] collect reference files, extended docstrings --- .../plugins/maya/publish/collect_yeti_rig.py | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/colorbleed/plugins/maya/publish/collect_yeti_rig.py b/colorbleed/plugins/maya/publish/collect_yeti_rig.py index e63bad48ca..c0d7cba2d2 100644 --- a/colorbleed/plugins/maya/publish/collect_yeti_rig.py +++ b/colorbleed/plugins/maya/publish/collect_yeti_rig.py @@ -64,10 +64,9 @@ class CollectYetiRig(pyblish.api.InstancePlugin): # Collect any textures if used yeti_resources = [] - yeti_nodes = cmds.ls(instance[:], type="pgYetiMaya") + yeti_nodes = cmds.ls(instance[:], type="pgYetiMaya", long=True) for node in yeti_nodes: # Get Yeti resources (textures) - # TODO: referenced files in Yeti Graph resources = self.get_yeti_resources(node) yeti_resources.extend(resources) @@ -80,11 +79,16 @@ class CollectYetiRig(pyblish.api.InstancePlugin): instance.data["endFrame"] = 1 def get_yeti_resources(self, node): - """Get all texture file paths + """Get all resource file paths If a texture is a sequence it gathers all sibling files to ensure the texture sequence is complete. + References can be used in the Yeti graph, this means that it is + possible to load previously caches files. The information will need + to be stored and, if the file not publish, copied to the resource + folder. + Args: node (str): node name of the pgYetiMaya node @@ -93,15 +97,25 @@ class CollectYetiRig(pyblish.api.InstancePlugin): """ resources = [] image_search_path = cmds.getAttr("{}.imageSearchPath".format(node)) + + # List all related textures texture_filenames = cmds.pgYetiCommand(node, listTextures=True) + self.log.info("Found %i texture(s)" % len(texture_filenames)) + + # Get all reference nodes + reference_nodes = cmds.pgYetiGraph(node, + listNodes=True, + type="reference") + self.log.info("Found %i reference node(s)" % len(reference_nodes)) if texture_filenames and not image_search_path: raise ValueError("pgYetiMaya node '%s' is missing the path to the " "files in the 'imageSearchPath " "atttribute'" % node) + # Collect all texture files for texture in texture_filenames: - node_resources = {"files": [], "source": texture, "node": node} + item = {"files": [], "source": texture, "node": node} texture_filepath = os.path.join(image_search_path, texture) if len(texture.split(".")) > 2: @@ -109,20 +123,46 @@ class CollectYetiRig(pyblish.api.InstancePlugin): if "" in texture: sequences = self.get_sequence(texture_filepath, pattern="") - node_resources["files"].extend(sequences) + item["files"].extend(sequences) # Based textures (animated masks f.e) elif "%04d" in texture: sequences = self.get_sequence(texture_filepath, pattern="%04d") - node_resources["files"].extend(sequences) + item["files"].extend(sequences) # Assuming it is a fixed name else: - node_resources["files"].append(texture_filepath) + item["files"].append(texture_filepath) else: - node_resources["files"].append(texture_filepath) + item["files"].append(texture_filepath) - resources.append(node_resources) + resources.append(item) + + # Collect all referenced files + for reference_node in reference_nodes: + ref_file = cmds.pgYetiGraph(node, + node=reference_node, + param="reference_file", + getParamValue=True) + + if not os.path.isfile(ref_file): + raise RuntimeError("Reference file must be a full file path!") + + # Create resource dict + item = {"files": [], + "source": ref_file, + "node": node, + "graphnode": reference_node, + "param": "reference_file"} + + ref_file_name = os.path.basename(ref_file) + if "%04d" in ref_file_name: + ref_files = self.get_sequence(ref_file) + item["files"].extend(ref_files) + else: + item["files"].append(ref_file) + + resources.append(item) return resources @@ -141,7 +181,6 @@ class CollectYetiRig(pyblish.api.InstancePlugin): list: file sequence. """ - from avalon.vendor import clique escaped = re.escape(filename) From 85a32ba4879d8845ef3ed9518de499579eb4adf9 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Fri, 5 Oct 2018 17:16:31 +0200 Subject: [PATCH 095/121] added contextmanager to map graph node attributes --- .../plugins/maya/publish/extract_yeti_rig.py | 78 ++++++++++++++----- 1 file changed, 57 insertions(+), 21 deletions(-) diff --git a/colorbleed/plugins/maya/publish/extract_yeti_rig.py b/colorbleed/plugins/maya/publish/extract_yeti_rig.py index 6548bab60b..68fe9b118d 100644 --- a/colorbleed/plugins/maya/publish/extract_yeti_rig.py +++ b/colorbleed/plugins/maya/publish/extract_yeti_rig.py @@ -53,6 +53,41 @@ def disconnect_plugs(settings, members): continue +@contextlib.contextmanager +def yetigraph_attribute_values(assumed_destination, resources): + + try: + for resource in resources: + if "graphnode" not in resource: + continue + + fname = os.path.basename(resource["source"]) + new_fpath = os.path.join(assumed_destination, fname) + new_fpath = new_fpath.replace("\\", "/") + + try: + cmds.pgYetiGraph(resource["node"], + node=resource["graphnode"], + param=resource["param"], + setParamValueString=new_fpath) + except Exception as exc: + print(">>> Exception:", exc) + yield + + finally: + for resource in resources: + if "graphnode" not in resources: + continue + + try: + cmds.pgYetiGraph(resource["node"], + node=resource["graphnode"], + param=resource["param"], + setParamValue=resource["source"]) + except RuntimeError: + pass + + class ExtractYetiRig(colorbleed.api.Extractor): """Extract the Yeti rig to a MayaAscii and write the Yeti rig data""" @@ -75,19 +110,18 @@ class ExtractYetiRig(colorbleed.api.Extractor): self.log.info("Writing metadata file") - image_search_path = "" + # Create assumed destination folder for imageSearchPath + assumed_temp_data = instance.data["assumedTemplateData"] + template = instance.data["template"] + template_formatted = template.format(**assumed_temp_data) + + destination_folder = os.path.dirname(template_formatted) + + image_search_path = os.path.join(destination_folder, "resources") + image_search_path = os.path.normpath(image_search_path) + settings = instance.data.get("rigsettings", None) if settings: - - # Create assumed destination folder for imageSearchPath - assumed_temp_data = instance.data["assumedTemplateData"] - template = instance.data["template"] - template_formatted = template.format(**assumed_temp_data) - - destination_folder = os.path.dirname(template_formatted) - image_search_path = os.path.join(destination_folder, "resources") - image_search_path = os.path.normpath(image_search_path) - settings["imageSearchPath"] = image_search_path with open(settings_path, "w") as fp: json.dump(settings, fp, ensure_ascii=False) @@ -104,19 +138,21 @@ class ExtractYetiRig(colorbleed.api.Extractor): set_members += cmds.listRelatives(set_members, allDescendents=True, fullPath=True) or [] - members = cmds.ls(set_members, long=True, objects=True) + members = cmds.ls(set_members, long=True) nodes = instance.data["setMembers"] + resources = instance.data.get("resources", {}) with disconnect_plugs(settings, members): - with maya.attribute_values(attr_value): - cmds.select(nodes, noExpand=True) - cmds.file(maya_path, - force=True, - exportSelected=True, - typ="mayaAscii", - preserveReferences=False, - constructionHistory=True, - shader=False) + with yetigraph_attribute_values(destination_folder, resources): + with maya.attribute_values(attr_value): + cmds.select(nodes, noExpand=True) + cmds.file(maya_path, + force=True, + exportSelected=True, + typ="mayaAscii", + preserveReferences=False, + constructionHistory=True, + shader=False) # Ensure files can be stored if "files" not in instance.data: From b48541f9ea743b2fa4c6b7e1cf85bcdda6457313 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 8 Oct 2018 16:43:43 +0200 Subject: [PATCH 096/121] Fix PLN-178 --- .../maya/publish/extract_camera_mayaAscii.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py index c2c1f32b01..c29aaed4fe 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py +++ b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py @@ -1,4 +1,5 @@ import os +from itertools import izip_longest from maya import cmds @@ -35,6 +36,49 @@ def massage_ma_file(path): f.close() +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks + + Examples: + grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx + + """ + + args = [iter(iterable)] * n + return izip_longest(fillvalue=fillvalue, *args) + + +def unlock(plug): + """Unlocks attribute and disconnects inputs for a plug. + + This will also recursively unlock the attribute + upwards to any parent attributes for compound + attributes, to ensure it's fully unlocked and free + to change the value. + + """ + node, attr = plug.rsplit(".", 1) + + # Unlock attribute + cmds.setAttr(plug, lock=False) + + # Also unlock any parent attribute (if compound) + parents = cmds.attributeQuery(attr, node=node, listParent=True) + if parents: + for parent in parents: + unlock("{0}.{1}".format(node, parent)) + + # Break incoming connections + connections = cmds.listConnections(plug, + source=True, + destination=False, + plugs=True, + connections=True) + if connections: + for destination, source in grouper(connections, 2): + cmds.disconnectAttr(source, destination) + + class ExtractCameraMayaAscii(colorbleed.api.Extractor): """Extract a Camera as Maya Ascii. @@ -107,6 +151,17 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): shapes=True, long=True) + # Fix PLN-178: Don't allow background color to be non-black + for cam in baked_shapes: + attrs = {"backgroundColorR": 0.0, + "backgroundColorG": 0.0, + "backgroundColorB": 0.0, + "overscan": 1.0} + for attr, value in attrs.items(): + plug = "{0}.{1}".format(cam, attr) + unlock(plug) + cmds.setAttr(plug, value) + self.log.info("Performing extraction..") cmds.select(baked_shapes, noExpand=True) cmds.file(path, From a39dc850837764cd70af9004fc4602fbb8da89c2 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 8 Oct 2018 18:00:16 +0200 Subject: [PATCH 097/121] Simplify logic by using `get_errored_instances_from_context` function --- colorbleed/action.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/colorbleed/action.py b/colorbleed/action.py index ba017354f0..534f34751d 100644 --- a/colorbleed/action.py +++ b/colorbleed/action.py @@ -157,13 +157,7 @@ class GenerateUUIDsOnInvalidAction(pyblish.api.Action): self.log.info("Finding bad nodes..") - # Get the errored instances - errored_instances = [] - for result in context.data["results"]: - if result["error"] is not None and result["instance"] is not None: - if result["error"]: - instance = result["instance"] - errored_instances.append(instance) + errored_instances = get_errored_instances_from_context(context) # Apply pyblish logic to get the instances for the plug-in instances = pyblish.api.instances_by_plugin(errored_instances, plugin) From 4d16d20b6d55f1e9c659f2cfaf862f0a1233b436 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 8 Oct 2018 18:05:00 +0200 Subject: [PATCH 098/121] Move Maya Pyblish actions to colorbleed.maya module --- colorbleed/action.py | 120 +--------------- colorbleed/api.py | 5 - colorbleed/maya/action.py | 128 ++++++++++++++++++ .../publish/validate_animation_content.py | 3 +- ...date_animation_out_set_related_node_ids.py | 3 +- .../publish/validate_camera_attributes.py | 3 +- .../maya/publish/validate_camera_contents.py | 5 +- .../publish/validate_instance_has_members.py | 3 +- .../maya/publish/validate_joints_hidden.py | 3 +- .../maya/publish/validate_look_contents.py | 3 +- .../validate_look_id_reference_edits.py | 3 +- .../publish/validate_look_members_unique.py | 5 +- .../validate_look_no_default_shaders.py | 3 +- .../maya/publish/validate_look_sets.py | 3 +- .../publish/validate_look_single_shader.py | 3 +- .../maya/publish/validate_mesh_has_uv.py | 5 +- .../publish/validate_mesh_lamina_faces.py | 3 +- .../validate_mesh_no_negative_scale.py | 3 +- .../publish/validate_mesh_non_manifold.py | 3 +- .../publish/validate_mesh_non_zero_edge.py | 3 +- .../publish/validate_mesh_normals_unlocked.py | 3 +- .../validate_mesh_shader_connections.py | 3 +- .../publish/validate_mesh_single_uv_set.py | 3 +- .../maya/publish/validate_mesh_uv_set_map1.py | 3 +- .../validate_mesh_vertices_have_edges.py | 3 +- .../maya/publish/validate_model_content.py | 3 +- .../maya/publish/validate_no_animation.py | 3 +- .../publish/validate_no_default_camera.py | 3 +- .../maya/publish/validate_no_namespace.py | 3 +- .../publish/validate_no_null_transforms.py | 4 +- .../maya/publish/validate_no_unknown_nodes.py | 3 +- .../plugins/maya/publish/validate_node_ids.py | 11 +- .../validate_node_ids_deformed_shapes.py | 3 +- .../publish/validate_node_ids_in_database.py | 3 +- .../maya/publish/validate_node_ids_related.py | 5 +- .../maya/publish/validate_node_ids_unique.py | 5 +- .../maya/publish/validate_node_no_ghosting.py | 3 +- .../validate_render_no_default_cameras.py | 3 +- .../publish/validate_render_single_camera.py | 3 +- .../maya/publish/validate_renderlayer_aovs.py | 3 +- .../maya/publish/validate_rig_controllers.py | 4 +- ...idate_rig_controllers_arnold_attributes.py | 4 +- .../publish/validate_rig_out_set_node_ids.py | 3 +- .../publish/validate_setdress_namespaces.py | 3 +- .../publish/validate_setdress_transforms.py | 4 +- .../publish/validate_shape_default_names.py | 3 +- .../publish/validate_shape_render_stats.py | 4 +- .../maya/publish/validate_step_size.py | 3 +- .../validate_transform_naming_suffix.py | 4 +- .../maya/publish/validate_transform_zero.py | 3 +- .../publish/validate_vrayproxy_members.py | 4 +- .../validate_yeti_rig_input_in_instance.py | 3 +- .../publish/validate_yetirig_cache_state.py | 4 +- 53 files changed, 245 insertions(+), 184 deletions(-) create mode 100644 colorbleed/maya/action.py diff --git a/colorbleed/action.py b/colorbleed/action.py index 534f34751d..ea1f5e13f4 100644 --- a/colorbleed/action.py +++ b/colorbleed/action.py @@ -1,5 +1,6 @@ # absolute_import is needed to counter the `module has no cmds error` in Maya from __future__ import absolute_import + import pyblish.api @@ -89,122 +90,3 @@ class RepairContextAction(pyblish.api.Action): plugin.repair() -class SelectInvalidAction(pyblish.api.Action): - """Select invalid nodes in Maya when plug-in failed. - - To retrieve the invalid nodes this assumes a static `get_invalid()` - method is available on the plugin. - - """ - label = "Select invalid" - on = "failed" # This action is only available on a failed plug-in - icon = "search" # Icon from Awesome Icon - - def process(self, context, plugin): - - try: - from maya import cmds - except ImportError: - raise ImportError("Current host is not Maya") - - errored_instances = get_errored_instances_from_context(context) - - # Apply pyblish.logic to get the instances for the plug-in - instances = pyblish.api.instances_by_plugin(errored_instances, plugin) - - # Get the invalid nodes for the plug-ins - self.log.info("Finding invalid nodes..") - invalid = list() - for instance in instances: - invalid_nodes = plugin.get_invalid(instance) - if invalid_nodes: - if isinstance(invalid_nodes, (list, tuple)): - invalid.extend(invalid_nodes) - else: - self.log.warning("Plug-in returned to be invalid, " - "but has no selectable nodes.") - - # Ensure unique (process each node only once) - invalid = list(set(invalid)) - - if invalid: - self.log.info("Selecting invalid nodes: %s" % ", ".join(invalid)) - cmds.select(invalid, replace=True, noExpand=True) - else: - self.log.info("No invalid nodes found.") - cmds.select(deselect=True) - - -class GenerateUUIDsOnInvalidAction(pyblish.api.Action): - """Generate UUIDs on the invalid nodes in the instance. - - Invalid nodes are those returned by the plugin's `get_invalid` method. - As such it is the plug-in's responsibility to ensure the nodes that - receive new UUIDs are actually invalid. - - Requires: - - instance.data["asset"] - - """ - - label = "Regenerate UUIDs" - on = "failed" # This action is only available on a failed plug-in - icon = "wrench" # Icon from Awesome Icon - - def process(self, context, plugin): - - from maya import cmds - - self.log.info("Finding bad nodes..") - - errored_instances = get_errored_instances_from_context(context) - - # Apply pyblish logic to get the instances for the plug-in - instances = pyblish.api.instances_by_plugin(errored_instances, plugin) - - # Get the nodes from the all instances that ran through this plug-in - all_invalid = [] - for instance in instances: - invalid = plugin.get_invalid(instance) - - # Don't allow referenced nodes to get their ids regenerated to - # avoid loaded content getting messed up with reference edits - if invalid: - referenced = {node for node in invalid if - cmds.referenceQuery(node, isNodeReferenced=True)} - if referenced: - self.log.warning("Skipping UUID generation on referenced " - "nodes: {}".format(list(referenced))) - invalid = [node for node in invalid - if node not in referenced] - - if 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 - - all_invalid = list(set(all_invalid)) - self.log.info("Generated ids on nodes: {0}".format(all_invalid)) - - def _update_id_attribute(self, instance, nodes): - """Delete the id attribute - - Args: - 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, _id in lib.generate_ids(nodes, asset_id=asset_id): - lib.set_id(node, _id, overwrite=True) diff --git a/colorbleed/api.py b/colorbleed/api.py index 531a63a50d..c332a98391 100644 --- a/colorbleed/api.py +++ b/colorbleed/api.py @@ -12,10 +12,7 @@ from .plugin import ( # temporary fix, might from .action import ( - get_errored_instances_from_context, - SelectInvalidAction, - GenerateUUIDsOnInvalidAction, RepairAction, RepairContextAction ) @@ -30,7 +27,5 @@ all = [ "ValidateMeshOrder", # action "get_errored_instances_from_context", - "SelectInvalidAction", - "GenerateUUIDsOnInvalidAction", "RepairAction" ] diff --git a/colorbleed/maya/action.py b/colorbleed/maya/action.py new file mode 100644 index 0000000000..f0f94516c0 --- /dev/null +++ b/colorbleed/maya/action.py @@ -0,0 +1,128 @@ +# absolute_import is needed to counter the `module has no cmds error` in Maya +from __future__ import absolute_import + +import pyblish.api + + +from ..action import get_errored_instances_from_context + + +class GenerateUUIDsOnInvalidAction(pyblish.api.Action): + """Generate UUIDs on the invalid nodes in the instance. + + Invalid nodes are those returned by the plugin's `get_invalid` method. + As such it is the plug-in's responsibility to ensure the nodes that + receive new UUIDs are actually invalid. + + Requires: + - instance.data["asset"] + + """ + + label = "Regenerate UUIDs" + on = "failed" # This action is only available on a failed plug-in + icon = "wrench" # Icon from Awesome Icon + + def process(self, context, plugin): + + from maya import cmds + + self.log.info("Finding bad nodes..") + + errored_instances = get_errored_instances_from_context(context) + + # Apply pyblish logic to get the instances for the plug-in + instances = pyblish.api.instances_by_plugin(errored_instances, plugin) + + # Get the nodes from the all instances that ran through this plug-in + all_invalid = [] + for instance in instances: + invalid = plugin.get_invalid(instance) + + # Don't allow referenced nodes to get their ids regenerated to + # avoid loaded content getting messed up with reference edits + if invalid: + referenced = {node for node in invalid if + cmds.referenceQuery(node, isNodeReferenced=True)} + if referenced: + self.log.warning("Skipping UUID generation on referenced " + "nodes: {}".format(list(referenced))) + invalid = [node for node in invalid + if node not in referenced] + + if 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 + + all_invalid = list(set(all_invalid)) + self.log.info("Generated ids on nodes: {0}".format(all_invalid)) + + def _update_id_attribute(self, instance, nodes): + """Delete the id attribute + + Args: + 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, _id in lib.generate_ids(nodes, asset_id=asset_id): + lib.set_id(node, _id, overwrite=True) + + +class SelectInvalidAction(pyblish.api.Action): + """Select invalid nodes in Maya when plug-in failed. + + To retrieve the invalid nodes this assumes a static `get_invalid()` + method is available on the plugin. + + """ + label = "Select invalid" + on = "failed" # This action is only available on a failed plug-in + icon = "search" # Icon from Awesome Icon + + def process(self, context, plugin): + + try: + from maya import cmds + except ImportError: + raise ImportError("Current host is not Maya") + + errored_instances = get_errored_instances_from_context(context) + + # Apply pyblish.logic to get the instances for the plug-in + instances = pyblish.api.instances_by_plugin(errored_instances, plugin) + + # Get the invalid nodes for the plug-ins + self.log.info("Finding invalid nodes..") + invalid = list() + for instance in instances: + invalid_nodes = plugin.get_invalid(instance) + if invalid_nodes: + if isinstance(invalid_nodes, (list, tuple)): + invalid.extend(invalid_nodes) + else: + self.log.warning("Plug-in returned to be invalid, " + "but has no selectable nodes.") + + # Ensure unique (process each node only once) + invalid = list(set(invalid)) + + if invalid: + self.log.info("Selecting invalid nodes: %s" % ", ".join(invalid)) + cmds.select(invalid, replace=True, noExpand=True) + else: + self.log.info("No invalid nodes found.") + cmds.select(deselect=True) \ No newline at end of file diff --git a/colorbleed/plugins/maya/publish/validate_animation_content.py b/colorbleed/plugins/maya/publish/validate_animation_content.py index 0725281705..dc25f7391e 100644 --- a/colorbleed/plugins/maya/publish/validate_animation_content.py +++ b/colorbleed/plugins/maya/publish/validate_animation_content.py @@ -1,5 +1,6 @@ import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateAnimationContent(pyblish.api.InstancePlugin): @@ -14,7 +15,7 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["colorbleed.animation"] label = "Animation Content" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @classmethod def get_invalid(cls, instance): diff --git a/colorbleed/plugins/maya/publish/validate_animation_out_set_related_node_ids.py b/colorbleed/plugins/maya/publish/validate_animation_out_set_related_node_ids.py index 63705dd3ba..0c05d211ca 100644 --- a/colorbleed/plugins/maya/publish/validate_animation_out_set_related_node_ids.py +++ b/colorbleed/plugins/maya/publish/validate_animation_out_set_related_node_ids.py @@ -2,6 +2,7 @@ import maya.cmds as cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -19,7 +20,7 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin): families = ['colorbleed.animation', "colorbleed.pointcache"] hosts = ['maya'] label = 'Animation Out Set Related Node Ids' - actions = [colorbleed.api.SelectInvalidAction, colorbleed.api.RepairAction] + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] def process(self, instance): """Process all meshes""" diff --git a/colorbleed/plugins/maya/publish/validate_camera_attributes.py b/colorbleed/plugins/maya/publish/validate_camera_attributes.py index 46b9de7ecd..9c43e9e6ed 100644 --- a/colorbleed/plugins/maya/publish/validate_camera_attributes.py +++ b/colorbleed/plugins/maya/publish/validate_camera_attributes.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateCameraAttributes(pyblish.api.InstancePlugin): @@ -17,7 +18,7 @@ class ValidateCameraAttributes(pyblish.api.InstancePlugin): families = ['colorbleed.camera'] hosts = ['maya'] label = 'Camera Attributes' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] DEFAULTS = [ ("filmFitOffset", 0.0), diff --git a/colorbleed/plugins/maya/publish/validate_camera_contents.py b/colorbleed/plugins/maya/publish/validate_camera_contents.py index 09f5d5392b..0b7ba9735d 100644 --- a/colorbleed/plugins/maya/publish/validate_camera_contents.py +++ b/colorbleed/plugins/maya/publish/validate_camera_contents.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateCameraContents(pyblish.api.InstancePlugin): @@ -9,7 +10,7 @@ class ValidateCameraContents(pyblish.api.InstancePlugin): A Camera instance may only hold a SINGLE camera's transform, nothing else. - It may hold a "locator" as shape, but different shapes are down the + It may hold a "locator" as shape, but different shapes are down the hierarchy. """ @@ -18,7 +19,7 @@ class ValidateCameraContents(pyblish.api.InstancePlugin): families = ['colorbleed.camera'] hosts = ['maya'] label = 'Camera Contents' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @classmethod def get_invalid(cls, instance): diff --git a/colorbleed/plugins/maya/publish/validate_instance_has_members.py b/colorbleed/plugins/maya/publish/validate_instance_has_members.py index d209505378..e0ea93b85c 100644 --- a/colorbleed/plugins/maya/publish/validate_instance_has_members.py +++ b/colorbleed/plugins/maya/publish/validate_instance_has_members.py @@ -1,5 +1,6 @@ import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateInstanceHasMembers(pyblish.api.InstancePlugin): @@ -8,7 +9,7 @@ class ValidateInstanceHasMembers(pyblish.api.InstancePlugin): order = colorbleed.api.ValidateContentsOrder hosts = ["maya"] label = 'Instance has members' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @classmethod def get_invalid(cls, instance): diff --git a/colorbleed/plugins/maya/publish/validate_joints_hidden.py b/colorbleed/plugins/maya/publish/validate_joints_hidden.py index 7772372ad4..1c8e44e843 100644 --- a/colorbleed/plugins/maya/publish/validate_joints_hidden.py +++ b/colorbleed/plugins/maya/publish/validate_joints_hidden.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -22,7 +23,7 @@ class ValidateJointsHidden(pyblish.api.InstancePlugin): category = 'rig' version = (0, 1, 0) label = "Joints Hidden" - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] @staticmethod diff --git a/colorbleed/plugins/maya/publish/validate_look_contents.py b/colorbleed/plugins/maya/publish/validate_look_contents.py index 5f47ea39cc..2f515e970c 100644 --- a/colorbleed/plugins/maya/publish/validate_look_contents.py +++ b/colorbleed/plugins/maya/publish/validate_look_contents.py @@ -1,5 +1,6 @@ import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateLookContents(pyblish.api.InstancePlugin): @@ -20,7 +21,7 @@ class ValidateLookContents(pyblish.api.InstancePlugin): families = ['colorbleed.look'] hosts = ['maya'] label = 'Look Data Contents' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): """Process all the nodes in the instance""" diff --git a/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py b/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py index b2608b1cdf..454320b00c 100644 --- a/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py +++ b/colorbleed/plugins/maya/publish/validate_look_id_reference_edits.py @@ -3,6 +3,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin): @@ -19,7 +20,7 @@ class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin): families = ['colorbleed.look'] hosts = ['maya'] label = 'Look Id Reference Edits' - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_look_members_unique.py b/colorbleed/plugins/maya/publish/validate_look_members_unique.py index a42410c123..b611bdbb33 100644 --- a/colorbleed/plugins/maya/publish/validate_look_members_unique.py +++ b/colorbleed/plugins/maya/publish/validate_look_members_unique.py @@ -2,6 +2,7 @@ from collections import defaultdict import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateUniqueRelationshipMembers(pyblish.api.InstancePlugin): @@ -24,8 +25,8 @@ class ValidateUniqueRelationshipMembers(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['colorbleed.look'] - actions = [colorbleed.api.SelectInvalidAction, - colorbleed.api.GenerateUUIDsOnInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction, + colorbleed.maya.action.GenerateUUIDsOnInvalidAction] def process(self, instance): """Process all meshes""" diff --git a/colorbleed/plugins/maya/publish/validate_look_no_default_shaders.py b/colorbleed/plugins/maya/publish/validate_look_no_default_shaders.py index 9315359184..7485fc3cc1 100644 --- a/colorbleed/plugins/maya/publish/validate_look_no_default_shaders.py +++ b/colorbleed/plugins/maya/publish/validate_look_no_default_shaders.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin): @@ -26,7 +27,7 @@ class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin): families = ['colorbleed.look'] hosts = ['maya'] label = 'Look No Default Shaders' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] DEFAULT_SHADERS = {"lambert1", "initialShadingGroup", "initialParticleSE", "particleCloud1"} diff --git a/colorbleed/plugins/maya/publish/validate_look_sets.py b/colorbleed/plugins/maya/publish/validate_look_sets.py index bf0567cc88..31884ab48f 100644 --- a/colorbleed/plugins/maya/publish/validate_look_sets.py +++ b/colorbleed/plugins/maya/publish/validate_look_sets.py @@ -1,3 +1,4 @@ +import colorbleed.maya.action from colorbleed.maya import lib import pyblish.api @@ -35,7 +36,7 @@ class ValidateLookSets(pyblish.api.InstancePlugin): families = ['colorbleed.look'] hosts = ['maya'] label = 'Look Sets' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): """Process all the nodes in the instance""" diff --git a/colorbleed/plugins/maya/publish/validate_look_single_shader.py b/colorbleed/plugins/maya/publish/validate_look_single_shader.py index f56f8eb64e..687dd2b84f 100644 --- a/colorbleed/plugins/maya/publish/validate_look_single_shader.py +++ b/colorbleed/plugins/maya/publish/validate_look_single_shader.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateSingleShader(pyblish.api.InstancePlugin): @@ -15,7 +16,7 @@ class ValidateSingleShader(pyblish.api.InstancePlugin): families = ['colorbleed.look'] hosts = ['maya'] label = 'Look Single Shader Per Shape' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] # The default connections to check def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_mesh_has_uv.py b/colorbleed/plugins/maya/publish/validate_mesh_has_uv.py index bc7c19e5ad..4233674ddd 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_has_uv.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_has_uv.py @@ -4,6 +4,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action def len_flattened(components): @@ -13,7 +14,7 @@ def len_flattened(components): when requesting with `maya.cmds.ls` without the `flatten` flag. Though enabling `flatten` on a large list (e.g. millions) will result in a slow result. This command will return the amount - of entries in a non-flattened list by parsing the result with + of entries in a non-flattened list by parsing the result with regex. Args: @@ -49,7 +50,7 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin): families = ['colorbleed.model'] category = 'geometry' label = 'Mesh Has UVs' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] optional = True @classmethod diff --git a/colorbleed/plugins/maya/publish/validate_mesh_lamina_faces.py b/colorbleed/plugins/maya/publish/validate_mesh_lamina_faces.py index 80a6968e55..ca54648108 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_lamina_faces.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_lamina_faces.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin): @@ -17,7 +18,7 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin): category = 'geometry' version = (0, 1, 0) label = 'Mesh Lamina Faces' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_mesh_no_negative_scale.py b/colorbleed/plugins/maya/publish/validate_mesh_no_negative_scale.py index 91fc720cbe..de60300931 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_no_negative_scale.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_no_negative_scale.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateMeshNoNegativeScale(pyblish.api.Validator): @@ -20,7 +21,7 @@ class ValidateMeshNoNegativeScale(pyblish.api.Validator): hosts = ['maya'] families = ['colorbleed.model'] label = 'Mesh No Negative Scale' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_mesh_non_manifold.py b/colorbleed/plugins/maya/publish/validate_mesh_non_manifold.py index c1185cf587..1c5ce731fa 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_non_manifold.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_non_manifold.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateMeshNonManifold(pyblish.api.Validator): @@ -16,7 +17,7 @@ class ValidateMeshNonManifold(pyblish.api.Validator): hosts = ['maya'] families = ['colorbleed.model'] label = 'Mesh Non-Manifold Vertices/Edges' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_mesh_non_zero_edge.py b/colorbleed/plugins/maya/publish/validate_mesh_non_zero_edge.py index 02019da523..6f35f512a0 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_non_zero_edge.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_non_zero_edge.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -21,7 +22,7 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin): category = 'geometry' version = (0, 1, 0) label = 'Mesh Edge Length Non Zero' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] __tolerance = 1e-5 diff --git a/colorbleed/plugins/maya/publish/validate_mesh_normals_unlocked.py b/colorbleed/plugins/maya/publish/validate_mesh_normals_unlocked.py index 68049fd60a..09fe2b4e4b 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_normals_unlocked.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_normals_unlocked.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateMeshNormalsUnlocked(pyblish.api.Validator): @@ -18,7 +19,7 @@ class ValidateMeshNormalsUnlocked(pyblish.api.Validator): category = 'geometry' version = (0, 1, 0) label = 'Mesh Normals Unlocked' - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] optional = True diff --git a/colorbleed/plugins/maya/publish/validate_mesh_shader_connections.py b/colorbleed/plugins/maya/publish/validate_mesh_shader_connections.py index 5f44bf1f9f..2d9d82eaa5 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_shader_connections.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_shader_connections.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action def pairs(iterable): @@ -76,7 +77,7 @@ class ValidateMeshShaderConnections(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['colorbleed.model'] label = "Mesh Shader Connections" - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_mesh_single_uv_set.py b/colorbleed/plugins/maya/publish/validate_mesh_single_uv_set.py index 77ec1a0661..774c7899d2 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_single_uv_set.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_single_uv_set.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -21,7 +22,7 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin): optional = True version = (0, 1, 0) label = "Mesh Single UV Set" - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] @staticmethod diff --git a/colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py b/colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py index 878549b539..00c9d800cf 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_uv_set_map1.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin): @@ -19,7 +20,7 @@ class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin): families = ['colorbleed.model'] optional = True label = "Mesh has map1 UV Set" - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] @staticmethod diff --git a/colorbleed/plugins/maya/publish/validate_mesh_vertices_have_edges.py b/colorbleed/plugins/maya/publish/validate_mesh_vertices_have_edges.py index 90eca6e6f4..41efd27f89 100644 --- a/colorbleed/plugins/maya/publish/validate_mesh_vertices_have_edges.py +++ b/colorbleed/plugins/maya/publish/validate_mesh_vertices_have_edges.py @@ -4,6 +4,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action def len_flattened(components): @@ -61,7 +62,7 @@ class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin): families = ['colorbleed.model'] category = 'geometry' label = 'Mesh Vertices Have Edges' - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] @classmethod diff --git a/colorbleed/plugins/maya/publish/validate_model_content.py b/colorbleed/plugins/maya/publish/validate_model_content.py index 1e43754c6d..1ea26047ee 100644 --- a/colorbleed/plugins/maya/publish/validate_model_content.py +++ b/colorbleed/plugins/maya/publish/validate_model_content.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -17,7 +18,7 @@ class ValidateModelContent(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["colorbleed.model"] label = "Model Content" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @classmethod def get_invalid(cls, instance): diff --git a/colorbleed/plugins/maya/publish/validate_no_animation.py b/colorbleed/plugins/maya/publish/validate_no_animation.py index 8b0da47d89..bde5775fe7 100644 --- a/colorbleed/plugins/maya/publish/validate_no_animation.py +++ b/colorbleed/plugins/maya/publish/validate_no_animation.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateNoAnimation(pyblish.api.Validator): @@ -18,7 +19,7 @@ class ValidateNoAnimation(pyblish.api.Validator): hosts = ["maya"] families = ["colorbleed.model"] optional = True - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_no_default_camera.py b/colorbleed/plugins/maya/publish/validate_no_default_camera.py index 6bdb830f0b..e41d2eea99 100644 --- a/colorbleed/plugins/maya/publish/validate_no_default_camera.py +++ b/colorbleed/plugins/maya/publish/validate_no_default_camera.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateNoDefaultCameras(pyblish.api.InstancePlugin): @@ -17,7 +18,7 @@ class ValidateNoDefaultCameras(pyblish.api.InstancePlugin): families = ['colorbleed.camera'] version = (0, 1, 0) label = "No Default Cameras" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_no_namespace.py b/colorbleed/plugins/maya/publish/validate_no_namespace.py index 0f0bbad1c1..46f1dbe49c 100644 --- a/colorbleed/plugins/maya/publish/validate_no_namespace.py +++ b/colorbleed/plugins/maya/publish/validate_no_namespace.py @@ -3,6 +3,7 @@ import maya.cmds as cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action def get_namespace(node_name): @@ -21,7 +22,7 @@ class ValidateNoNamespace(pyblish.api.InstancePlugin): category = 'cleanup' version = (0, 1, 0) label = 'No Namespaces' - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] @staticmethod diff --git a/colorbleed/plugins/maya/publish/validate_no_null_transforms.py b/colorbleed/plugins/maya/publish/validate_no_null_transforms.py index d94938493b..d9dff290b8 100644 --- a/colorbleed/plugins/maya/publish/validate_no_null_transforms.py +++ b/colorbleed/plugins/maya/publish/validate_no_null_transforms.py @@ -2,6 +2,7 @@ import maya.cmds as cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action def has_shape_children(node): @@ -42,7 +43,8 @@ class ValidateNoNullTransforms(pyblish.api.InstancePlugin): category = 'cleanup' version = (0, 1, 0) label = 'No Empty/Null Transforms' - actions = [colorbleed.api.RepairAction, colorbleed.api.SelectInvalidAction] + actions = [colorbleed.api.RepairAction, + colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_no_unknown_nodes.py b/colorbleed/plugins/maya/publish/validate_no_unknown_nodes.py index 221e8f8b61..3fdd087b3d 100644 --- a/colorbleed/plugins/maya/publish/validate_no_unknown_nodes.py +++ b/colorbleed/plugins/maya/publish/validate_no_unknown_nodes.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateNoUnknownNodes(pyblish.api.InstancePlugin): @@ -20,7 +21,7 @@ class ValidateNoUnknownNodes(pyblish.api.InstancePlugin): families = ['colorbleed.model', 'colorbleed.rig'] optional = True label = "Unknown Nodes" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_node_ids.py b/colorbleed/plugins/maya/publish/validate_node_ids.py index 3bd4471788..f2d21b6158 100644 --- a/colorbleed/plugins/maya/publish/validate_node_ids.py +++ b/colorbleed/plugins/maya/publish/validate_node_ids.py @@ -1,14 +1,15 @@ import pyblish.api import colorbleed.api +import colorbleed.maya.action from colorbleed.maya import lib class ValidateNodeIDs(pyblish.api.InstancePlugin): """Validate nodes have a Colorbleed Id. - - When IDs are missing from nodes *save your scene* and they should be - automatically generated because IDs are created on non-referenced nodes + + When IDs are missing from nodes *save your scene* and they should be + automatically generated because IDs are created on non-referenced nodes in Maya upon scene save. """ @@ -23,8 +24,8 @@ class ValidateNodeIDs(pyblish.api.InstancePlugin): "colorbleed.animation", "colorbleed.setdress"] - actions = [colorbleed.api.SelectInvalidAction, - colorbleed.api.GenerateUUIDsOnInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction, + colorbleed.maya.action.GenerateUUIDsOnInvalidAction] def process(self, instance): """Process all meshes""" diff --git a/colorbleed/plugins/maya/publish/validate_node_ids_deformed_shapes.py b/colorbleed/plugins/maya/publish/validate_node_ids_deformed_shapes.py index 4513acb6e1..99df6a1b63 100644 --- a/colorbleed/plugins/maya/publish/validate_node_ids_deformed_shapes.py +++ b/colorbleed/plugins/maya/publish/validate_node_ids_deformed_shapes.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -19,7 +20,7 @@ class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin): families = ['colorbleed.look'] hosts = ['maya'] label = 'Deformed shape ids' - actions = [colorbleed.api.SelectInvalidAction, colorbleed.api.RepairAction] + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] def process(self, instance): """Process all the nodes in the instance""" diff --git a/colorbleed/plugins/maya/publish/validate_node_ids_in_database.py b/colorbleed/plugins/maya/publish/validate_node_ids_in_database.py index 0493779da2..7b0fddee30 100644 --- a/colorbleed/plugins/maya/publish/validate_node_ids_in_database.py +++ b/colorbleed/plugins/maya/publish/validate_node_ids_in_database.py @@ -3,6 +3,7 @@ import pyblish.api import avalon.io as io import colorbleed.api +import colorbleed.maya.action from colorbleed.maya import lib @@ -22,7 +23,7 @@ class ValidateNodeIdsInDatabase(pyblish.api.InstancePlugin): hosts = ['maya'] families = ["*"] - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): invalid = self.get_invalid(instance) diff --git a/colorbleed/plugins/maya/publish/validate_node_ids_related.py b/colorbleed/plugins/maya/publish/validate_node_ids_related.py index 985bd54dda..c9185b4e02 100644 --- a/colorbleed/plugins/maya/publish/validate_node_ids_related.py +++ b/colorbleed/plugins/maya/publish/validate_node_ids_related.py @@ -2,6 +2,7 @@ import pyblish.api import colorbleed.api import avalon.io as io +import colorbleed.maya.action from colorbleed.maya import lib @@ -19,8 +20,8 @@ class ValidateNodeIDsRelated(pyblish.api.InstancePlugin): "colorbleed.rig"] optional = True - actions = [colorbleed.api.SelectInvalidAction, - colorbleed.api.GenerateUUIDsOnInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction, + colorbleed.maya.action.GenerateUUIDsOnInvalidAction] def process(self, instance): """Process all nodes in instance (including hierarchy)""" diff --git a/colorbleed/plugins/maya/publish/validate_node_ids_unique.py b/colorbleed/plugins/maya/publish/validate_node_ids_unique.py index b8cbbd552d..09551ef2ef 100644 --- a/colorbleed/plugins/maya/publish/validate_node_ids_unique.py +++ b/colorbleed/plugins/maya/publish/validate_node_ids_unique.py @@ -2,6 +2,7 @@ from collections import defaultdict import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -18,8 +19,8 @@ class ValidateNodeIdsUnique(pyblish.api.InstancePlugin): "colorbleed.look", "colorbleed.rig"] - actions = [colorbleed.api.SelectInvalidAction, - colorbleed.api.GenerateUUIDsOnInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction, + colorbleed.maya.action.GenerateUUIDsOnInvalidAction] def process(self, instance): """Process all meshes""" diff --git a/colorbleed/plugins/maya/publish/validate_node_no_ghosting.py b/colorbleed/plugins/maya/publish/validate_node_no_ghosting.py index ca5c4a1edc..360c3cdce8 100644 --- a/colorbleed/plugins/maya/publish/validate_node_no_ghosting.py +++ b/colorbleed/plugins/maya/publish/validate_node_no_ghosting.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateNodeNoGhosting(pyblish.api.InstancePlugin): @@ -20,7 +21,7 @@ class ValidateNodeNoGhosting(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['colorbleed.model', 'colorbleed.rig'] label = "No Ghosting" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] _attributes = {'ghosting': 0} diff --git a/colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py b/colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py index 4f55670a8c..588c66eb39 100644 --- a/colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py +++ b/colorbleed/plugins/maya/publish/validate_render_no_default_cameras.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -12,7 +13,7 @@ class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['colorbleed.renderlayer'] label = "No Default Cameras Renderable" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_render_single_camera.py b/colorbleed/plugins/maya/publish/validate_render_single_camera.py index e45f5f186c..94448d1585 100644 --- a/colorbleed/plugins/maya/publish/validate_render_single_camera.py +++ b/colorbleed/plugins/maya/publish/validate_render_single_camera.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -20,7 +21,7 @@ class ValidateRenderSingleCamera(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['colorbleed.renderlayer'] label = "Render Single Camera" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def get_invalid(instance): diff --git a/colorbleed/plugins/maya/publish/validate_renderlayer_aovs.py b/colorbleed/plugins/maya/publish/validate_renderlayer_aovs.py index 387f4b4881..06e0c68184 100644 --- a/colorbleed/plugins/maya/publish/validate_renderlayer_aovs.py +++ b/colorbleed/plugins/maya/publish/validate_renderlayer_aovs.py @@ -1,5 +1,6 @@ import pyblish.api +import colorbleed.maya.action from avalon import io import colorbleed.api @@ -24,7 +25,7 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): label = "Render Passes / AOVs Are Registered" hosts = ["maya"] families = ["colorbleed.renderlayer"] - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): invalid = self.get_invalid(instance) diff --git a/colorbleed/plugins/maya/publish/validate_rig_controllers.py b/colorbleed/plugins/maya/publish/validate_rig_controllers.py index 2ae799fa71..6117f46597 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_controllers.py +++ b/colorbleed/plugins/maya/publish/validate_rig_controllers.py @@ -4,6 +4,8 @@ import pyblish.api import colorbleed.api from cb.utils.maya.context import undo_chunk +import colorbleed.maya.action + class ValidateRigControllers(pyblish.api.InstancePlugin): """Validate rig controllers. @@ -28,7 +30,7 @@ class ValidateRigControllers(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["colorbleed.rig"] actions = [colorbleed.api.RepairAction, - colorbleed.api.SelectInvalidAction] + colorbleed.maya.action.SelectInvalidAction] # Default controller values CONTROLLER_DEFAULTS = { diff --git a/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py b/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py index b6a1191006..6655e59a67 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py +++ b/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py @@ -4,6 +4,8 @@ import pyblish.api import colorbleed.api from cb.utils.maya.context import undo_chunk +import colorbleed.maya.action + class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin): """Validate rig control curves have no keyable arnold attributes. @@ -29,7 +31,7 @@ class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["colorbleed.rig"] actions = [colorbleed.api.RepairAction, - colorbleed.api.SelectInvalidAction] + colorbleed.maya.action.SelectInvalidAction] attributes = [ "rcurve", diff --git a/colorbleed/plugins/maya/publish/validate_rig_out_set_node_ids.py b/colorbleed/plugins/maya/publish/validate_rig_out_set_node_ids.py index 6b8fc9d28c..72e0db311c 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_out_set_node_ids.py +++ b/colorbleed/plugins/maya/publish/validate_rig_out_set_node_ids.py @@ -2,6 +2,7 @@ import maya.cmds as cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action import colorbleed.maya.lib as lib @@ -19,7 +20,7 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): families = ["colorbleed.rig"] hosts = ['maya'] label = 'Rig Out Set Node Ids' - actions = [colorbleed.api.SelectInvalidAction, colorbleed.api.RepairAction] + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] def process(self, instance): """Process all meshes""" diff --git a/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py b/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py index 1eda02cf74..a8a28a10ca 100644 --- a/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py +++ b/colorbleed/plugins/maya/publish/validate_setdress_namespaces.py @@ -1,5 +1,6 @@ import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateSetdressNamespaces(pyblish.api.InstancePlugin): @@ -17,7 +18,7 @@ class ValidateSetdressNamespaces(pyblish.api.InstancePlugin): label = "Validate Setdress Namespaces" order = pyblish.api.ValidatorOrder families = ["colorbleed.setdress"] - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_setdress_transforms.py b/colorbleed/plugins/maya/publish/validate_setdress_transforms.py index c3547f963e..8ed4a81b5f 100644 --- a/colorbleed/plugins/maya/publish/validate_setdress_transforms.py +++ b/colorbleed/plugins/maya/publish/validate_setdress_transforms.py @@ -3,6 +3,8 @@ import colorbleed.api from maya import cmds +import colorbleed.maya.action + class ValidateSetDressModelTransforms(pyblish.api.InstancePlugin): """Verify only root nodes of the loaded asset have transformations. @@ -26,7 +28,7 @@ class ValidateSetDressModelTransforms(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder + 0.49 label = "Setdress Model Transforms" families = ["colorbleed.setdress"] - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] prompt_message = ("You are about to reset the matrix to the default values." diff --git a/colorbleed/plugins/maya/publish/validate_shape_default_names.py b/colorbleed/plugins/maya/publish/validate_shape_default_names.py index 75fd8f3f1e..b4249d69aa 100644 --- a/colorbleed/plugins/maya/publish/validate_shape_default_names.py +++ b/colorbleed/plugins/maya/publish/validate_shape_default_names.py @@ -4,6 +4,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action def short_name(node): @@ -37,7 +38,7 @@ class ValidateShapeDefaultNames(pyblish.api.InstancePlugin): optional = True version = (0, 1, 0) label = "Shape Default Naming" - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] @staticmethod diff --git a/colorbleed/plugins/maya/publish/validate_shape_render_stats.py b/colorbleed/plugins/maya/publish/validate_shape_render_stats.py index 9a3067badb..544470c3a7 100644 --- a/colorbleed/plugins/maya/publish/validate_shape_render_stats.py +++ b/colorbleed/plugins/maya/publish/validate_shape_render_stats.py @@ -3,6 +3,8 @@ import colorbleed.api from maya import cmds +import colorbleed.maya.action + class ValidateShapeRenderStats(pyblish.api.Validator): """Ensure all render stats are set to the default values.""" @@ -11,7 +13,7 @@ class ValidateShapeRenderStats(pyblish.api.Validator): hosts = ['maya'] families = ['colorbleed.model'] label = 'Shape Default Render Stats' - actions = [colorbleed.api.SelectInvalidAction, + actions = [colorbleed.maya.action.SelectInvalidAction, colorbleed.api.RepairAction] defaults = {'castsShadows': 1, diff --git a/colorbleed/plugins/maya/publish/validate_step_size.py b/colorbleed/plugins/maya/publish/validate_step_size.py index 7267d99a35..07d61ec933 100644 --- a/colorbleed/plugins/maya/publish/validate_step_size.py +++ b/colorbleed/plugins/maya/publish/validate_step_size.py @@ -1,5 +1,6 @@ import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateStepSize(pyblish.api.InstancePlugin): @@ -14,7 +15,7 @@ class ValidateStepSize(pyblish.api.InstancePlugin): families = ['colorbleed.camera', 'colorbleed.pointcache', 'colorbleed.animation'] - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] MIN = 0.01 MAX = 1.0 diff --git a/colorbleed/plugins/maya/publish/validate_transform_naming_suffix.py b/colorbleed/plugins/maya/publish/validate_transform_naming_suffix.py index 51d63ad505..0c100126ce 100644 --- a/colorbleed/plugins/maya/publish/validate_transform_naming_suffix.py +++ b/colorbleed/plugins/maya/publish/validate_transform_naming_suffix.py @@ -2,7 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api - +import colorbleed.maya.action SUFFIX_NAMING_TABLE = {'mesh': ["_GEO", "_GES", "_GEP", "_OSD"], 'nurbsCurve': ["_CRV"], @@ -38,7 +38,7 @@ class ValidateTransformNamingSuffix(pyblish.api.InstancePlugin): optional = True version = (0, 1, 0) label = 'Suffix Naming Conventions' - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] @staticmethod def is_valid_name(node_name, shape_type): diff --git a/colorbleed/plugins/maya/publish/validate_transform_zero.py b/colorbleed/plugins/maya/publish/validate_transform_zero.py index fa87539d96..895248933c 100644 --- a/colorbleed/plugins/maya/publish/validate_transform_zero.py +++ b/colorbleed/plugins/maya/publish/validate_transform_zero.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateTransformZero(pyblish.api.Validator): @@ -19,7 +20,7 @@ class ValidateTransformZero(pyblish.api.Validator): category = "geometry" version = (0, 1, 0) label = "Transform Zero (Freeze)" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] _identity = [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, diff --git a/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py b/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py index 0ffbf3bd80..7d9ecdaac5 100644 --- a/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py +++ b/colorbleed/plugins/maya/publish/validate_vrayproxy_members.py @@ -3,6 +3,8 @@ import colorbleed.api from maya import cmds +import colorbleed.maya.action + class ValidateVrayProxyMembers(pyblish.api.InstancePlugin): """Validate whether the V-Ray Proxy instance has shape members""" @@ -11,7 +13,7 @@ class ValidateVrayProxyMembers(pyblish.api.InstancePlugin): label = 'VRay Proxy Members' hosts = ['maya'] families = ['colorbleed.vrayproxy'] - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_yeti_rig_input_in_instance.py b/colorbleed/plugins/maya/publish/validate_yeti_rig_input_in_instance.py index 58d9834617..423cc18360 100644 --- a/colorbleed/plugins/maya/publish/validate_yeti_rig_input_in_instance.py +++ b/colorbleed/plugins/maya/publish/validate_yeti_rig_input_in_instance.py @@ -2,6 +2,7 @@ from maya import cmds import pyblish.api import colorbleed.api +import colorbleed.maya.action class ValidateYetiRigInputShapesInInstance(pyblish.api.Validator): @@ -11,7 +12,7 @@ class ValidateYetiRigInputShapesInInstance(pyblish.api.Validator): hosts = ["maya"] families = ["colorbleed.yetiRig"] label = "Yeti Rig Input Shapes In Instance" - actions = [colorbleed.api.SelectInvalidAction] + actions = [colorbleed.maya.action.SelectInvalidAction] def process(self, instance): diff --git a/colorbleed/plugins/maya/publish/validate_yetirig_cache_state.py b/colorbleed/plugins/maya/publish/validate_yetirig_cache_state.py index 2c4d4dbc72..94a46d2821 100644 --- a/colorbleed/plugins/maya/publish/validate_yetirig_cache_state.py +++ b/colorbleed/plugins/maya/publish/validate_yetirig_cache_state.py @@ -4,6 +4,8 @@ import colorbleed.action import maya.cmds as cmds +import colorbleed.maya.action + class ValidateYetiRigCacheState(pyblish.api.InstancePlugin): """Validate the I/O attributes of the node @@ -19,7 +21,7 @@ class ValidateYetiRigCacheState(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["colorbleed.yetiRig"] actions = [colorbleed.action.RepairAction, - colorbleed.action.SelectInvalidAction] + colorbleed.maya.action.SelectInvalidAction] def process(self, instance): invalid = self.get_invalid(instance) From e3fc962add0a7dc0338ce074001ce28f9ef034b4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 8 Oct 2018 18:07:47 +0200 Subject: [PATCH 099/121] Fix all to __all__ --- colorbleed/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/api.py b/colorbleed/api.py index c332a98391..8e49c17309 100644 --- a/colorbleed/api.py +++ b/colorbleed/api.py @@ -17,7 +17,7 @@ from .action import ( RepairContextAction ) -all = [ +__all__ = [ # plugin classes "Extractor", # ordering From e1eaef8a69a7a7dca5964ebafe416f621fa3133a Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 8 Oct 2018 18:08:10 +0200 Subject: [PATCH 100/121] Remove OrderedDict from api (since it's Python built-in) --- colorbleed/api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/colorbleed/api.py b/colorbleed/api.py index 8e49c17309..e665d40535 100644 --- a/colorbleed/api.py +++ b/colorbleed/api.py @@ -1,5 +1,3 @@ -from collections import OrderedDict - from .plugin import ( Extractor, From 128b2856543078ba8a25824ad6895e4ca98bc4ff Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 12:57:50 +0200 Subject: [PATCH 101/121] Embed all internal Colorbleed library (cb) code into config --- colorbleed/lib.py | 19 + colorbleed/maya/lib.py | 493 ++++++++++++++++++ .../plugins/maya/create/colorbleed_camera.py | 2 +- .../plugins/maya/publish/collect_look.py | 142 ++++- .../maya/publish/extract_camera_alembic.py | 10 +- .../maya/publish/extract_camera_mayaAscii.py | 36 +- .../plugins/maya/publish/extract_look.py | 11 +- .../plugins/maya/publish/extract_model.py | 21 +- .../maya/publish/validate_look_sets.py | 4 +- .../maya/publish/validate_rig_controllers.py | 4 +- ...idate_rig_controllers_arnold_attributes.py | 4 +- 11 files changed, 689 insertions(+), 57 deletions(-) diff --git a/colorbleed/lib.py b/colorbleed/lib.py index bc98cf0cc5..edd911a461 100644 --- a/colorbleed/lib.py +++ b/colorbleed/lib.py @@ -2,6 +2,7 @@ import os import re import logging import importlib +import itertools from .vendor import pather from .vendor.pather.error import ParseError @@ -12,6 +13,24 @@ import avalon.api log = logging.getLogger(__name__) +def pairwise(iterable): + """s -> (s0,s1), (s2,s3), (s4, s5), ...""" + a = iter(iterable) + return itertools.izip(a, a) + + +def grouper(iterable, n, fillvalue=None): + """Collect data into fixed-length chunks or blocks + + Examples: + grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx + + """ + + args = [iter(iterable)] * n + return itertools.izip_longest(fillvalue=fillvalue, *args) + + def is_latest(representation): """Return whether the representation is from latest version diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index d4b0b8aa73..dcb9a91620 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -94,6 +94,11 @@ INT_FPS = {15, 24, 25, 30, 48, 50, 60, 44100, 48000} FLOAT_FPS = {23.976, 29.97, 47.952, 59.94} +def _get_mel_global(name): + """Return the value of a mel global variable""" + return mel.eval("$%s = $%s;" % (name, name)) + + def matrix_equals(a, b, tolerance=1e-10): """ Compares two matrices with an imperfection tolerance @@ -306,6 +311,33 @@ def attribute_values(attr_values): cmds.setAttr(attr, value) +@contextlib.contextmanager +def keytangent_default(in_tangent_type='auto', + out_tangent_type='auto'): + """Set the default keyTangent for new keys during this context""" + + original_itt = cmds.keyTangent(query=True, g=True, itt=True)[0] + original_ott = cmds.keyTangent(query=True, g=True, ott=True)[0] + cmds.keyTangent(g=True, itt=in_tangent_type) + cmds.keyTangent(g=True, ott=out_tangent_type) + try: + yield + finally: + cmds.keyTangent(g=True, itt=original_itt) + cmds.keyTangent(g=True, ott=original_ott) + + +@contextlib.contextmanager +def undo_chunk(): + """Open a undo chunk during context.""" + + try: + cmds.undoInfo(openChunk=True) + yield + finally: + cmds.undoInfo(closeChunk=True) + + @contextlib.contextmanager def renderlayer(layer): """Set the renderlayer during the context""" @@ -339,6 +371,126 @@ def evaluation(mode="off"): cmds.evaluationManager(mode=original) +@contextlib.contextmanager +def no_refresh(): + """Temporarily disables Maya's UI updates + + Note: + This only disabled the main pane and will sometimes still + trigger updates in torn off panels. + + """ + + pane = _get_mel_global('gMainPane') + state = cmds.paneLayout(pane, query=True, manage=True) + cmds.paneLayout(pane, edit=True, manage=False) + + try: + yield + finally: + cmds.paneLayout(pane, edit=True, manage=state) + + +@contextlib.contextmanager +def empty_sets(sets, force=False): + """Remove all members of the sets during the context""" + + assert isinstance(sets, (list, tuple)) + + original = dict() + original_connections = [] + + # Store original state + for obj_set in sets: + members = cmds.sets(obj_set, query=True) + original[obj_set] = members + + try: + for obj_set in sets: + cmds.sets(clear=obj_set) + if force: + # Break all connections if force is enabled, this way we + # prevent Maya from exporting any reference nodes which are + # connected with placeHolder[x] attributes + plug = "%s.dagSetMembers" % obj_set + connections = cmds.listConnections(plug, + source=True, + destination=False, + plugs=True, + connections=True) or [] + original_connections.extend(connections) + for dest, src in pairwise(connections): + cmds.disconnectAttr(src, dest) + yield + finally: + + for dest, src in pairwise(original_connections): + cmds.connectAttr(src, dest) + + # Restore original members + for origin_set, members in original.iteritems(): + cmds.sets(members, forceElement=origin_set) + + +@contextlib.contextmanager +def renderlayer(layer): + """Set the renderlayer during the context + + Arguments: + layer (str): Name of layer to switch to. + + """ + + original = cmds.editRenderLayerGlobals(query=True, + currentRenderLayer=True) + + try: + cmds.editRenderLayerGlobals(currentRenderLayer=layer) + yield + finally: + cmds.editRenderLayerGlobals(currentRenderLayer=original) + + +class delete_after(object): + """Context Manager that will delete collected nodes after exit. + + This allows to ensure the nodes added to the context are deleted + afterwards. This is useful if you want to ensure nodes are deleted + even if an error is raised. + + Examples: + with delete_after() as delete_bin: + cube = maya.cmds.polyCube() + delete_bin.extend(cube) + # cube exists + # cube deleted + + """ + + def __init__(self, nodes=None): + + self._nodes = list() + + if nodes: + self.extend(nodes) + + def append(self, node): + self._nodes.append(node) + + def extend(self, nodes): + self._nodes.extend(nodes) + + def __iter__(self): + return iter(self._nodes) + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + if self._nodes: + cmds.delete(self._nodes) + + def get_renderer(layer): with renderlayer(layer): return cmds.getAttr("defaultRenderGlobals.currentRenderer") @@ -367,6 +519,157 @@ def no_undo(flush=False): cmds.undoInfo(**{keyword: original}) +def get_shader_assignments_from_shapes(shapes): + """Return the shape assignment per related shading engines. + + Returns a dictionary where the keys are shadingGroups and the values are + lists of assigned shapes or shape-components. + + For the 'shapes' this will return a dictionary like: + { + "shadingEngineX": ["nodeX", "nodeY"], + "shadingEngineY": ["nodeA", "nodeB"] + } + + Args: + shapes (list): The shapes to collect the assignments for. + + Returns: + dict: The {shadingEngine: shapes} relationships + + """ + + shapes = cmds.ls(shapes, + long=True, + selection=True, + shapes=True, + objectsOnly=True) + if not shapes: + return {} + + # Collect shading engines and their shapes + assignments = defaultdict(list) + for shape in shapes: + + # Get unique shading groups for the shape + shading_groups = cmds.listConnections(shape, + type="shadingEngine") or [] + shading_groups = list(set(shading_groups)) + for shading_group in shading_groups: + assignments[shading_group].add(shape) + + return dict(assignments) + + +@contextlib.contextmanager +def shader(nodes, shadingEngine="initialShadingGroup"): + """Assign a shader to nodes during the context""" + + shapes = cmds.ls(nodes, dag=1, o=1, shapes=1, long=1) + original = get_shader_assignments_from_shapes(shapes) + + try: + # Assign override shader + if shapes: + cmds.sets(shapes, edit=True, forceElement=shadingEngine) + yield + finally: + + # Assign original shaders + for sg, members in original.items(): + if members: + cmds.sets(shapes, edit=True, forceElement=shadingEngine) + + +@contextlib.contextmanager +def displaySmoothness(nodes, + divisionsU=0, + divisionsV=0, + pointsWire=4, + pointsShaded=1, + polygonObject=1): + """Set the displaySmoothness during the context""" + + # Ensure only non-intermediate shapes + nodes = cmds.ls(nodes, + dag=1, + shapes=1, + long=1, + noIntermediate=True) + + def parse(node): + """Parse the current state of a node""" + state = {} + for key in ["divisionsU", + "divisionsV", + "pointsWire", + "pointsShaded", + "polygonObject"]: + value = cmds.displaySmoothness(node, query=1, **{key: True}) + if value is not None: + state[key] = value[0] + return state + + originals = dict((node, parse(node)) for node in nodes) + + try: + # Apply current state + cmds.displaySmoothness(nodes, + divisionsU=divisionsU, + divisionsV=divisionsV, + pointsWire=pointsWire, + pointsShaded=pointsShaded, + polygonObject=polygonObject) + yield + finally: + # Revert state + for node, state in originals.iteritems(): + if state: + cmds.displaySmoothness(node, **state) + + +@contextlib.contextmanager +def no_display_layers(nodes): + """Ensure nodes are not in a displayLayer during context. + + Arguments: + nodes (list): The nodes to remove from any display layer. + + """ + + # Ensure long names + nodes = cmds.ls(nodes, long=True) + + # Get the original state + lookup = set(nodes) + original = {} + for layer in cmds.ls(type='displayLayer'): + + # Skip default layer + if layer == "defaultLayer": + continue + + members = cmds.editDisplayLayerMembers(layer, + query=True, + fullNames=True) + if not members: + continue + members = set(members) + + included = lookup.intersection(members) + if included: + original[layer] = list(included) + + try: + # Add all nodes to default layer + cmds.editDisplayLayerMembers("defaultLayer", nodes, noRecurse=True) + yield + finally: + # Restore original members + for layer, members in original.iteritems(): + cmds.editDisplayLayerMembers(layer, members, noRecurse=True) + + @contextlib.contextmanager def namespaced(namespace, new=True): """Work inside namespace during context @@ -1534,3 +1837,193 @@ def validate_fps(): return False return True + + +def bake(nodes, + frame_range=None, + step=1.0, + simulation=True, + preserve_outside_keys=False, + disable_implicit_control=True, + shape=True): + """Bake the given nodes over the time range. + + This will bake all attributes of the node, including custom attributes. + + Args: + nodes (list): Names of transform nodes, eg. camera, light. + frame_range (tuple): frame range with start and end frame. + or if None then takes timeSliderRange + simulation (bool): Whether to perform a full simulation of the + attributes over time. + preserve_outside_keys (bool): Keep keys that are outside of the baked + range. + disable_implicit_control (bool): When True will disable any + constraints to the object. + shape (bool): When True also bake attributes on the children shapes. + step (float): The step size to sample by. + + Returns: + None + + """ + + # Parse inputs + if not nodes: + return + + assert isinstance(nodes, (list, tuple)), "Nodes must be a list or tuple" + + # If frame range is None fall back to time slider range + if frame_range is None: + frame_range = getTimeSliderRange() + + # If frame range is single frame bake one frame more, + # otherwise maya.cmds.bakeResults gets confused + if frame_range[1] == frame_range[0]: + frame_range[1] += 1 + + # Bake it + with keytangent_default(in_tangent_type='auto', + out_tangent_type='auto'): + cmds.bakeResults(nodes, + simulation=simulation, + preserveOutsideKeys=preserve_outside_keys, + disableImplicitControl=disable_implicit_control, + shape=shape, + sampleBy=step, + time=(frame_range[0], frame_range[1])) + + +def bake_to_world_space(nodes, + frameRange=None, + simulation=True, + preserveOutsideKeys=False, + disableImplicitControl=True, + shape=True, + step=1.0): + """Bake the nodes to world space transformation (incl. other attributes) + + Bakes the transforms to world space (while maintaining all its animated + attributes and settings) by duplicating the node. Then parents it to world + and constrains to the original. + + Other attributes are also baked by connecting all attributes directly. + Baking is then done using Maya's bakeResults command. + + See `bake` for the argument documentation. + + Returns: + list: The newly created and baked node names. + + """ + + def _get_attrs(node): + """Workaround for buggy shape attribute listing with listAttr""" + attrs = cmds.listAttr(node, + write=True, + scalar=True, + settable=True, + connectable=True, + keyable=True, + shortNames=True) or [] + valid_attrs = [] + for attr in attrs: + node_attr = '{0}.{1}'.format(node, attr) + + # Sometimes Maya returns 'non-existent' attributes for shapes + # so we filter those out + if not cmds.attributeQuery(attr, node=node, exists=True): + continue + + # We only need those that have a connection, just to be safe + # that it's actually keyable/connectable anyway. + if cmds.connectionInfo(node_attr, + isDestination=True): + valid_attrs.append(attr) + + return valid_attrs + + transform_attrs = set(["t", "r", "s", + "tx", "ty", "tz", + "rx", "ry", "rz", + "sx", "sy", "sz"]) + + world_space_nodes = [] + with delete_after() as delete_bin: + + # Create the duplicate nodes that are in world-space connected to + # the originals + for node in nodes: + + # Duplicate the node + short_name = node.rsplit("|", 1)[-1] + new_name = "{0}_baked".format(short_name) + new_node = cmds.duplicate(node, + name=new_name, + renameChildren=True)[0] + + # Connect all attributes on the node except for transform + # attributes + attrs = _get_attrs(node) + attrs = set(attrs) - transform_attrs if attrs else [] + + for attr in attrs: + orig_node_attr = '{0}.{1}'.format(node, attr) + new_node_attr = '{0}.{1}'.format(new_node, attr) + + # unlock to avoid connection errors + cmds.setAttr(new_node_attr, lock=False) + + cmds.connectAttr(orig_node_attr, + new_node_attr, + force=True) + + # If shapes are also baked then connect those keyable attributes + if shape: + children_shapes = cmds.listRelatives(new_node, + children=True, + fullPath=True, + shapes=True) + if children_shapes: + orig_children_shapes = cmds.listRelatives(node, + children=True, + fullPath=True, + shapes=True) + for orig_shape, new_shape in zip(orig_children_shapes, + children_shapes): + attrs = _get_attrs(orig_shape) + for attr in attrs: + orig_node_attr = '{0}.{1}'.format(orig_shape, attr) + new_node_attr = '{0}.{1}'.format(new_shape, attr) + + # unlock to avoid connection errors + cmds.setAttr(new_node_attr, lock=False) + + cmds.connectAttr(orig_node_attr, + new_node_attr, + force=True) + + # Parent to world + if cmds.listRelatives(new_node, parent=True): + new_node = cmds.parent(new_node, world=True)[0] + + # Unlock transform attributes so constraint can be created + for attr in transform_attrs: + cmds.setAttr('{0}.{1}'.format(new_node, attr), lock=False) + + # Constraints + delete_bin.extend(cmds.parentConstraint(node, new_node, mo=False)) + delete_bin.extend(cmds.scaleConstraint(node, new_node, mo=False)) + + world_space_nodes.append(new_node) + + bake(world_space_nodes, + frame_range=frameRange, + step=step, + simulation=simulation, + preserve_outside_keys=preserveOutsideKeys, + disable_implicit_control=disableImplicitControl, + shape=shape) + + return world_space_nodes diff --git a/colorbleed/plugins/maya/create/colorbleed_camera.py b/colorbleed/plugins/maya/create/colorbleed_camera.py index 94c1a82225..1a87744d38 100644 --- a/colorbleed/plugins/maya/create/colorbleed_camera.py +++ b/colorbleed/plugins/maya/create/colorbleed_camera.py @@ -22,6 +22,6 @@ class CreateCamera(avalon.maya.Creator): # Bake to world space by default, when this is False it will also # include the parent hierarchy in the baked results - data['bakeToWorldSpace'] = True + data['bake_to_world_space'] = True self.data = data diff --git a/colorbleed/plugins/maya/publish/collect_look.py b/colorbleed/plugins/maya/publish/collect_look.py index 93080b5312..1decca6e2e 100644 --- a/colorbleed/plugins/maya/publish/collect_look.py +++ b/colorbleed/plugins/maya/publish/collect_look.py @@ -1,7 +1,10 @@ +import re +import os +import glob + from maya import cmds import pyblish.api import colorbleed.maya.lib as lib -from cb.utils.maya import context, shaders SHAPE_ATTRS = ["castsShadows", "receiveShadows", @@ -48,6 +51,139 @@ def get_look_attrs(node): return result +def node_uses_image_sequence(node): + """Return whether file node uses an image sequence or single image. + + Determine if a node uses an image sequence or just a single image, + not always obvious from its file path alone. + + Args: + node (str): Name of the Maya node + + Returns: + bool: True if node uses an image sequence + + """ + + # useFrameExtension indicates an explicit image sequence + node_path = get_file_node_path(node).lower() + + # The following tokens imply a sequence + patterns = ["", "", "", "u_v", ".tif will return as /path/to/texture.*.tif. + + Args: + path (str): the image sequence path + + Returns: + str: Return glob string that matches the filename pattern. + + """ + + if path is None: + return path + + # If any of the patterns, convert the pattern + patterns = { + "": "", + "": "", + "": "", + "#": "#", + "u_v": "|", + "", + "": "" + } + + lower = path.lower() + has_pattern = False + for pattern, regex_pattern in patterns.items(): + if pattern in lower: + path = re.sub(regex_pattern, "*", path, flags=re.IGNORECASE) + has_pattern = True + + if has_pattern: + return path + + base = os.path.basename(path) + matches = list(re.finditer(r'\d+', base)) + if matches: + match = matches[-1] + new_base = '{0}*{1}'.format(base[:match.start()], + base[match.end():]) + head = os.path.dirname(path) + return os.path.join(head, new_base) + else: + return path + + +def get_file_node_path(node): + """Get the file path used by a Maya file node. + + Args: + node (str): Name of the Maya file node + + Returns: + str: the file path in use + + """ + # if the path appears to be sequence, use computedFileTextureNamePattern, + # this preserves the <> tag + if cmds.attributeQuery('computedFileTextureNamePattern', + node=node, + exists=True): + plug = '{0}.computedFileTextureNamePattern'.format(node) + texture_pattern = cmds.getAttr(plug) + + patterns = ["", + "", + "u_v", + "", + ""] + lower = texture_pattern.lower() + if any(pattern in lower for pattern in patterns): + return texture_pattern + + # otherwise use fileTextureName + return cmds.getAttr('{0}.fileTextureName'.format(node)) + + +def get_file_node_files(node): + """Return the file paths related to the file node + + Note: + Will only return existing files. Returns an empty list + if not valid existing files are linked. + + Returns: + list: List of full file paths. + + """ + + path = get_file_node_path(node) + path = cmds.workspace(expandName=path) + if node_uses_image_sequence(node): + glob_pattern = seq_to_glob(path) + return glob.glob(glob_pattern) + elif os.path.exists(path): + return [path] + else: + return [] + + class CollectLook(pyblish.api.InstancePlugin): """Collect look data for instance. @@ -74,7 +210,7 @@ class CollectLook(pyblish.api.InstancePlugin): def process(self, instance): """Collect the Look in the instance with the correct layer settings""" - with context.renderlayer(instance.data["renderlayer"]): + with lib.renderlayer(instance.data["renderlayer"]): self.collect(instance) def collect(self, instance): @@ -268,7 +404,7 @@ class CollectLook(pyblish.api.InstancePlugin): # paths as the computed patterns source = source.replace("\\", "/") - files = shaders.get_file_node_files(node) + files = get_file_node_files(node) if len(files) == 0: self.log.error("No valid files found from node `%s`" % node) diff --git a/colorbleed/plugins/maya/publish/extract_camera_alembic.py b/colorbleed/plugins/maya/publish/extract_camera_alembic.py index 2b18ced96f..efacf7b528 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_alembic.py +++ b/colorbleed/plugins/maya/publish/extract_camera_alembic.py @@ -5,14 +5,14 @@ from maya import cmds import avalon.maya import colorbleed.api -import cb.utils.maya.context as context +import colorbleed.maya.lib as lib class ExtractCameraAlembic(colorbleed.api.Extractor): """Extract a Camera as Alembic. The cameras gets baked to world space by default. Only when the instance's - `bakeToWorldSpace` is set to False it will include its full hierarchy. + `bake_to_world_space` is set to False it will include its full hierarchy. """ @@ -27,7 +27,7 @@ class ExtractCameraAlembic(colorbleed.api.Extractor): instance.data.get("endFrame", 1)] handles = instance.data.get("handles", 0) step = instance.data.get("step", 1.0) - bake_to_worldspace = instance.data("bakeToWorldSpace", True) + bake_to_worldspace = instance.data("bake_to_world_space", True) # get cameras members = instance.data['setMembers'] @@ -66,8 +66,8 @@ class ExtractCameraAlembic(colorbleed.api.Extractor): job_str += ' -file "{0}"'.format(path) - with context.evaluation("off"): - with context.no_refresh(): + with lib.evaluation("off"): + with lib.no_refresh(): cmds.AbcExport(j=job_str, verbose=False) if "files" not in instance.data: diff --git a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py index c29aaed4fe..bd0eeba05f 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py +++ b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py @@ -1,13 +1,11 @@ import os -from itertools import izip_longest from maya import cmds import avalon.maya import colorbleed.api - -import cb.utils.maya.context as context -from cb.utils.maya.animation import bakeToWorldSpace +from colorbleed.lib import grouper +from colorbleed.maya import lib def massage_ma_file(path): @@ -36,18 +34,6 @@ def massage_ma_file(path): f.close() -def grouper(iterable, n, fillvalue=None): - """Collect data into fixed-length chunks or blocks - - Examples: - grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx - - """ - - args = [iter(iterable)] * n - return izip_longest(fillvalue=fillvalue, *args) - - def unlock(plug): """Unlocks attribute and disconnects inputs for a plug. @@ -87,7 +73,7 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): will be published. The cameras gets baked to world space by default. Only when the instance's - `bakeToWorldSpace` is set to False it will include its full hierarchy. + `bake_to_world_space` is set to False it will include its full hierarchy. Note: The extracted Maya ascii file gets "massaged" removing the uuid values @@ -106,12 +92,12 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): instance.data.get("endFrame", 1)] handles = instance.data.get("handles", 0) step = instance.data.get("step", 1.0) - bake_to_worldspace = instance.data("bakeToWorldSpace", True) + bake_to_worldspace = instance.data("bake_to_world_space", True) # TODO: Implement a bake to non-world space # Currently it will always bake the resulting camera to world-space # and it does not allow to include the parent hierarchy, even though - # with `bakeToWorldSpace` set to False it should include its hierarchy + # with `bake_to_world_space` set to False it should include its hierarchy # to be correct with the family implementation. if not bake_to_worldspace: self.log.warning("Camera (Maya Ascii) export only supports world" @@ -140,11 +126,13 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): # Perform extraction self.log.info("Performing camera bakes for: {0}".format(transform)) with avalon.maya.maintained_selection(): - with context.evaluation("off"): - with context.no_refresh(): - baked = bakeToWorldSpace(transform, - frameRange=range_with_handles, - step=step) + with lib.evaluation("off"): + with lib.no_refresh(): + baked = lib.bake_to_worldspace( + transform, + frameRange=range_with_handles, + step=step + ) baked_shapes = cmds.ls(baked, type="camera", dag=True, diff --git a/colorbleed/plugins/maya/publish/extract_look.py b/colorbleed/plugins/maya/publish/extract_look.py index 7e8fc2b436..c359fc10b5 100644 --- a/colorbleed/plugins/maya/publish/extract_look.py +++ b/colorbleed/plugins/maya/publish/extract_look.py @@ -6,10 +6,9 @@ from maya import cmds import pyblish.api import avalon.maya -import colorbleed.api -import colorbleed.maya.lib as maya -from cb.utils.maya import context +import colorbleed.api +import colorbleed.maya.lib as lib class ExtractLook(colorbleed.api.Extractor): @@ -63,10 +62,10 @@ class ExtractLook(colorbleed.api.Extractor): # Extract in correct render layer layer = instance.data.get("renderlayer", "defaultRenderLayer") - with context.renderlayer(layer): + with lib.renderlayer(layer): # TODO: Ensure membership edits don't become renderlayer overrides - with context.empty_sets(sets, force=True): - with maya.attribute_values(remap): + with lib.empty_sets(sets, force=True): + with lib.attribute_values(remap): with avalon.maya.maintained_selection(): cmds.select(sets, noExpand=True) cmds.file(maya_path, diff --git a/colorbleed/plugins/maya/publish/extract_model.py b/colorbleed/plugins/maya/publish/extract_model.py index e1be53d59a..466ee73174 100644 --- a/colorbleed/plugins/maya/publish/extract_model.py +++ b/colorbleed/plugins/maya/publish/extract_model.py @@ -4,8 +4,7 @@ from maya import cmds import avalon.maya import colorbleed.api - -from cb.utils.maya import context +import colorbleed.maya.lib as lib class ExtractModel(colorbleed.api.Extractor): @@ -47,15 +46,15 @@ class ExtractModel(colorbleed.api.Extractor): noIntermediate=True, long=True) - with context.no_display_layers(instance): - with context.displaySmoothness(members, - divisionsU=0, - divisionsV=0, - pointsWire=4, - pointsShaded=1, - polygonObject=1): - with context.shader(members, - shadingEngine="initialShadingGroup"): + with lib.no_display_layers(instance): + with lib.displaySmoothness(members, + divisionsU=0, + divisionsV=0, + pointsWire=4, + pointsShaded=1, + polygonObject=1): + with lib.shader(members, + shadingEngine="initialShadingGroup"): with avalon.maya.maintained_selection(): cmds.select(members, noExpand=True) cmds.file(path, diff --git a/colorbleed/plugins/maya/publish/validate_look_sets.py b/colorbleed/plugins/maya/publish/validate_look_sets.py index 31884ab48f..aeec58674a 100644 --- a/colorbleed/plugins/maya/publish/validate_look_sets.py +++ b/colorbleed/plugins/maya/publish/validate_look_sets.py @@ -4,8 +4,6 @@ from colorbleed.maya import lib import pyblish.api import colorbleed.api -from cb.utils.maya import context - class ValidateLookSets(pyblish.api.InstancePlugin): """Validate if any sets are missing from the instance and look data @@ -57,7 +55,7 @@ class ValidateLookSets(pyblish.api.InstancePlugin): invalid = [] renderlayer = instance.data.get("renderlayer", "defaultRenderLayer") - with context.renderlayer(renderlayer): + with lib.renderlayer(renderlayer): for node in instance: # get the connected objectSets of the node sets = lib.get_related_sets(node) diff --git a/colorbleed/plugins/maya/publish/validate_rig_controllers.py b/colorbleed/plugins/maya/publish/validate_rig_controllers.py index 6117f46597..ecb3b687d1 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_controllers.py +++ b/colorbleed/plugins/maya/publish/validate_rig_controllers.py @@ -1,10 +1,10 @@ from maya import cmds import pyblish.api -import colorbleed.api -from cb.utils.maya.context import undo_chunk +import colorbleed.api import colorbleed.maya.action +from colorbleed.maya.lib import undo_chunk class ValidateRigControllers(pyblish.api.InstancePlugin): diff --git a/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py b/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py index 6655e59a67..43bfde918f 100644 --- a/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py +++ b/colorbleed/plugins/maya/publish/validate_rig_controllers_arnold_attributes.py @@ -2,8 +2,8 @@ from maya import cmds import pyblish.api import colorbleed.api -from cb.utils.maya.context import undo_chunk +import colorbleed.maya.lib as lib import colorbleed.maya.action @@ -83,7 +83,7 @@ class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin): def repair(cls, instance): invalid = cls.get_invalid(instance) - with undo_chunk(): + with lib.undo_chunk(): for node in invalid: for attribute in cls.attributes: if cmds.attributeQuery(attribute, node=node, exists=True): From 959e35b3b4028b0d7d7d43e6441289a07118b3f0 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 13:02:27 +0200 Subject: [PATCH 102/121] Fix missing pairwise function --- 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 dcb9a91620..9727666765 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -419,12 +419,12 @@ def empty_sets(sets, force=False): plugs=True, connections=True) or [] original_connections.extend(connections) - for dest, src in pairwise(connections): + for dest, src in lib.pairwise(connections): cmds.disconnectAttr(src, dest) yield finally: - for dest, src in pairwise(original_connections): + for dest, src in lib.pairwise(original_connections): cmds.connectAttr(src, dest) # Restore original members From 4e760c593d2547d3b8772f3dc630fc1823ea1bc7 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 13:02:49 +0200 Subject: [PATCH 103/121] Make listConnections call for getting shaders more explicit --- colorbleed/maya/lib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 9727666765..3e6e7cb9d0 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -553,6 +553,10 @@ def get_shader_assignments_from_shapes(shapes): # Get unique shading groups for the shape shading_groups = cmds.listConnections(shape, + source=False, + destination=True, + plugs=False, + connections=False, type="shadingEngine") or [] shading_groups = list(set(shading_groups)) for shading_group in shading_groups: From 3de333ef67215978fc3c2a59ee79ce265653b4a5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 13:12:52 +0200 Subject: [PATCH 104/121] Fix baking to world space --- colorbleed/maya/lib.py | 7 ++++--- .../plugins/maya/publish/extract_camera_mayaAscii.py | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index 3e6e7cb9d0..fe2ac48035 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1856,7 +1856,7 @@ def bake(nodes, Args: nodes (list): Names of transform nodes, eg. camera, light. - frame_range (tuple): frame range with start and end frame. + frame_range (list): frame range with start and end frame. or if None then takes timeSliderRange simulation (bool): Whether to perform a full simulation of the attributes over time. @@ -1878,9 +1878,10 @@ def bake(nodes, assert isinstance(nodes, (list, tuple)), "Nodes must be a list or tuple" - # If frame range is None fall back to time slider range + # If frame range is None fall back to time slider playback time range if frame_range is None: - frame_range = getTimeSliderRange() + frame_range = [cmds.playbackOptions(query=True, minTime=True), + cmds.playbackOptions(query=True, maxTime=True)] # If frame range is single frame bake one frame more, # otherwise maya.cmds.bakeResults gets confused diff --git a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py index bd0eeba05f..47eef59221 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py +++ b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py @@ -97,8 +97,8 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): # TODO: Implement a bake to non-world space # Currently it will always bake the resulting camera to world-space # and it does not allow to include the parent hierarchy, even though - # with `bake_to_world_space` set to False it should include its hierarchy - # to be correct with the family implementation. + # with `bake_to_world_space` set to False it should include its + # hierarchy to be correct with the family implementation. if not bake_to_worldspace: self.log.warning("Camera (Maya Ascii) export only supports world" "space baked camera extractions. The disabled " @@ -128,7 +128,7 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): with avalon.maya.maintained_selection(): with lib.evaluation("off"): with lib.no_refresh(): - baked = lib.bake_to_worldspace( + baked = lib.bake_to_world_space( transform, frameRange=range_with_handles, step=step From 4a5c4dafd1c684704be53c96e48b647371c2e1c4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 13:15:31 +0200 Subject: [PATCH 105/121] Cosmetics / PEP08 / consistency with 'bake' function --- colorbleed/maya/lib.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/colorbleed/maya/lib.py b/colorbleed/maya/lib.py index fe2ac48035..5a6aed8d01 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1901,10 +1901,10 @@ def bake(nodes, def bake_to_world_space(nodes, - frameRange=None, + frame_range=None, simulation=True, - preserveOutsideKeys=False, - disableImplicitControl=True, + preserve_outside_keys=False, + disable_implicit_control=True, shape=True, step=1.0): """Bake the nodes to world space transformation (incl. other attributes) @@ -2024,11 +2024,11 @@ def bake_to_world_space(nodes, world_space_nodes.append(new_node) bake(world_space_nodes, - frame_range=frameRange, + frame_range=frame_range, step=step, simulation=simulation, - preserve_outside_keys=preserveOutsideKeys, - disable_implicit_control=disableImplicitControl, + preserve_outside_keys=preserve_outside_keys, + disable_implicit_control=disable_implicit_control, shape=shape) return world_space_nodes From 25365e872756dad2855559a0b38b01e225c45053 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 13:56:02 +0200 Subject: [PATCH 106/121] Cosmetics / PEP08 / consistency with 'bake' function --- colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py index 47eef59221..a600c6c5a0 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py +++ b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py @@ -130,7 +130,7 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): with lib.no_refresh(): baked = lib.bake_to_world_space( transform, - frameRange=range_with_handles, + frame_range=range_with_handles, step=step ) baked_shapes = cmds.ls(baked, From cc1ca111cf5a509ffad1bce9c3a32c52af5694bc Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 13:57:49 +0200 Subject: [PATCH 107/121] Revert back key change on "bakeToWorldSpace" in instance --- colorbleed/plugins/maya/create/colorbleed_camera.py | 2 +- colorbleed/plugins/maya/publish/extract_camera_alembic.py | 4 ++-- colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/colorbleed/plugins/maya/create/colorbleed_camera.py b/colorbleed/plugins/maya/create/colorbleed_camera.py index 1a87744d38..94c1a82225 100644 --- a/colorbleed/plugins/maya/create/colorbleed_camera.py +++ b/colorbleed/plugins/maya/create/colorbleed_camera.py @@ -22,6 +22,6 @@ class CreateCamera(avalon.maya.Creator): # Bake to world space by default, when this is False it will also # include the parent hierarchy in the baked results - data['bake_to_world_space'] = True + data['bakeToWorldSpace'] = True self.data = data diff --git a/colorbleed/plugins/maya/publish/extract_camera_alembic.py b/colorbleed/plugins/maya/publish/extract_camera_alembic.py index efacf7b528..0b315a8d6b 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_alembic.py +++ b/colorbleed/plugins/maya/publish/extract_camera_alembic.py @@ -12,7 +12,7 @@ class ExtractCameraAlembic(colorbleed.api.Extractor): """Extract a Camera as Alembic. The cameras gets baked to world space by default. Only when the instance's - `bake_to_world_space` is set to False it will include its full hierarchy. + `bakeToWorldSpace` is set to False it will include its full hierarchy. """ @@ -27,7 +27,7 @@ class ExtractCameraAlembic(colorbleed.api.Extractor): instance.data.get("endFrame", 1)] handles = instance.data.get("handles", 0) step = instance.data.get("step", 1.0) - bake_to_worldspace = instance.data("bake_to_world_space", True) + bake_to_worldspace = instance.data("bakeToWorldSpace", True) # get cameras members = instance.data['setMembers'] diff --git a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py index a600c6c5a0..09e1d535e4 100644 --- a/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py +++ b/colorbleed/plugins/maya/publish/extract_camera_mayaAscii.py @@ -73,7 +73,7 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): will be published. The cameras gets baked to world space by default. Only when the instance's - `bake_to_world_space` is set to False it will include its full hierarchy. + `bakeToWorldSpace` is set to False it will include its full hierarchy. Note: The extracted Maya ascii file gets "massaged" removing the uuid values @@ -92,12 +92,12 @@ class ExtractCameraMayaAscii(colorbleed.api.Extractor): instance.data.get("endFrame", 1)] handles = instance.data.get("handles", 0) step = instance.data.get("step", 1.0) - bake_to_worldspace = instance.data("bake_to_world_space", True) + bake_to_worldspace = instance.data("bakeToWorldSpace", True) # TODO: Implement a bake to non-world space # Currently it will always bake the resulting camera to world-space # and it does not allow to include the parent hierarchy, even though - # with `bake_to_world_space` set to False it should include its + # with `bakeToWorldSpace` set to False it should include its # hierarchy to be correct with the family implementation. if not bake_to_worldspace: self.log.warning("Camera (Maya Ascii) export only supports world" From cf524eac6a4627beadf38efacad2d93b0dab4190 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 15:57:00 +0200 Subject: [PATCH 108/121] Fix PLN-149 for Deadline renders, avoid Redshift creating .lock files --- colorbleed/plugins/maya/publish/submit_deadline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/colorbleed/plugins/maya/publish/submit_deadline.py b/colorbleed/plugins/maya/publish/submit_deadline.py index dbb987a603..45ef683757 100644 --- a/colorbleed/plugins/maya/publish/submit_deadline.py +++ b/colorbleed/plugins/maya/publish/submit_deadline.py @@ -207,6 +207,7 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): # todo: This is a temporary fix for yeti variables "PEREGRINEL_LICENSE", "REDSHIFT_MAYAEXTENSIONSPATH", + "REDSHIFT_DISABLEOUTPUTLOCKFILES" "VRAY_FOR_MAYA2018_PLUGINS_X64", "VRAY_PLUGINS_X64", "VRAY_USE_THREAD_AFFINITY", From 729617989c52bba297c6ff235a8c07c97eb5ed79 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Tue, 9 Oct 2018 18:56:39 +0200 Subject: [PATCH 109/121] Remove redundant repair method (dead code) and docstring tweak --- .../maya/publish/validate_look_sets.py | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/colorbleed/plugins/maya/publish/validate_look_sets.py b/colorbleed/plugins/maya/publish/validate_look_sets.py index aeec58674a..1e7645bfaf 100644 --- a/colorbleed/plugins/maya/publish/validate_look_sets.py +++ b/colorbleed/plugins/maya/publish/validate_look_sets.py @@ -8,25 +8,31 @@ import colorbleed.api class ValidateLookSets(pyblish.api.InstancePlugin): """Validate if any sets are missing from the instance and look data - A node might have a relationship with a shader but has no Colorbleed ID. + A shader can be assigned to a node that is missing a Colorbleed ID. Because it is missing the ID it has not been collected in the instance. + This validator ensures no relationships and thus considers it invalid + if a relationship was not collected. + When the relationship needs to be maintained the artist might need to create a different* relationship or ensure the node has the Colorbleed ID. - * The relationship might be too broad (assigned to top node if hierarchy). + *The relationship might be too broad (assigned to top node of hierarchy). This can be countered by creating the relationship on the shape or its - transform. - In essence, ensure item the shader is assigned to has the Colorbleed ID! + transform. In essence, ensure item the shader is assigned to has the + Colorbleed ID! - Displacement shaders: - Ensure all geometry is added to the displacement objectSet. - It is best practice to add the transform group of the shape to the - displacement objectSet - Example content: - [asset_GRP|geometry_GRP|body_GES, - asset_GRP|geometry_GRP|L_eye_GES, - asset_GRP|geometry_GRP|R_eye_GES, - asset_GRP|geometry_GRP|wings_GEO] + Examples: + + - Displacement objectSets (like V-Ray): + + It is best practice to add the transform group of the shape to the + displacement objectSet. + + Example content: + [asset_GRP|geometry_GRP|body_GES, + asset_GRP|geometry_GRP|L_eye_GES, + asset_GRP|geometry_GRP|R_eye_GES, + asset_GRP|geometry_GRP|wings_GEO] """ @@ -90,7 +96,3 @@ class ValidateLookSets(pyblish.api.InstancePlugin): continue return invalid - - @classmethod - def repair(cls, context, instance): - pass From 51995c6246a7e1aac60bd9dd52e2e3f7601bd084 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 10 Oct 2018 10:58:26 +0200 Subject: [PATCH 110/121] REN-55: Avoid scene open/save/new callbacks during Maya batch rendering. --- colorbleed/maya/__init__.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/colorbleed/maya/__init__.py b/colorbleed/maya/__init__.py index 3f75ffd872..86106ee593 100644 --- a/colorbleed/maya/__init__.py +++ b/colorbleed/maya/__init__.py @@ -5,6 +5,7 @@ import weakref from maya import utils, cmds, mel from avalon import api as avalon, pipeline, maya +from avalon.maya.pipeline import IS_HEADLESS from pyblish import api as pyblish from ..lib import ( @@ -34,12 +35,18 @@ def install(): log.info("Installing callbacks ... ") avalon.on("init", on_init) + + # Callbacks below are not required for headless mode, the `init` however + # is important to load referenced Alembics correctly at rendertime. + if IS_HEADLESS: + log.info("Running in headless mode, skipping Colorbleed Maya " + "save/open/new callback installation..") + return + avalon.on("save", on_save) avalon.on("open", on_open) - - avalon.before("save", on_before_save) - avalon.on("new", on_new) + avalon.before("save", on_before_save) log.info("Overriding existing event 'taskChanged'") override_event("taskChanged", on_task_changed) From 437a9c37f3b7184f33bb24695e17fac7b30300c4 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 11 Oct 2018 08:54:45 +0200 Subject: [PATCH 111/121] More consistent logging message (cosmetics) --- 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 5a6aed8d01..2ca874dd7d 100644 --- a/colorbleed/maya/lib.py +++ b/colorbleed/maya/lib.py @@ -1721,7 +1721,7 @@ def set_scene_fps(fps, update=True): current_frame = cmds.currentTime(query=True) - log.info("Updating FPS to '{}'".format(unit)) + log.info("Setting scene FPS to: '{}'".format(unit)) cmds.currentUnit(time=unit, updateAnimation=update) # Set time slider data back to previous state @@ -1762,7 +1762,7 @@ def set_scene_resolution(width, height): log.error("Can't set VRay resolution because there is no node " "named: `%s`" % vray_node) - log.info("Setting project resolution to: %s x %s" % (width, height)) + log.info("Setting scene resolution to: %s x %s" % (width, height)) cmds.setAttr("%s.width" % control_node, width) cmds.setAttr("%s.height" % control_node, height) From fe73f55a5d506b65919f19b7390b3124c80127b3 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 09:26:24 +0200 Subject: [PATCH 112/121] updated tools --- colorbleed/maya/menu.json | 3726 ++++++++++++++++++------------------- 1 file changed, 1767 insertions(+), 1959 deletions(-) diff --git a/colorbleed/maya/menu.json b/colorbleed/maya/menu.json index d394f5dba1..0c620d3e4c 100644 --- a/colorbleed/maya/menu.json +++ b/colorbleed/maya/menu.json @@ -1,1959 +1,1767 @@ -[ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\save_scene_incremental.py", - "sourcetype": "file", - "title": "Version Up", - "tooltip": "Incremental save with a specific format" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\show_current_scene_in_explorer.py", - "sourcetype": "file", - "title": "Explore current scene..", - "tooltip": "Show current scene in Explorer" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\avalon\\launch_manager.py", - "sourcetype": "file", - "title": "Project Manager", - "tooltip": "Add assets to the project" - }, - { - "type": "separator" - }, - { - "type": "menu", - "title": "Modeling", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\duplicate_normalized.py", - "sourcetype": "file", - "tags": [ - "modeling", - "duplicate", - "normalized" - ], - "title": "Duplicate Normalized", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\transferUVs.py", - "sourcetype": "file", - "tags": [ - "modeling", - "transfer", - "uv" - ], - "title": "Transfer UVs", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\mirrorSymmetry.py", - "sourcetype": "file", - "tags": [ - "modeling", - "mirror", - "symmetry" - ], - "title": "Mirror Symmetry", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\selectOutlineUI.py", - "sourcetype": "file", - "tags": [ - "modeling", - "select", - "outline", - "ui" - ], - "title": "Select Outline UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\polyDeleteOtherUVSets.py", - "sourcetype": "file", - "tags": [ - "modeling", - "polygon", - "uvset", - "delete" - ], - "title": "Polygon Delete Other UV Sets", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\polyCombineQuick.py", - "sourcetype": "file", - "tags": [ - "modeling", - "combine", - "polygon", - "quick" - ], - "title": "Polygon Combine Quick", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\separateMeshPerShader.py", - "sourcetype": "file", - "tags": [ - "modeling", - "separateMeshPerShader" - ], - "title": "Separate Mesh Per Shader", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\polyDetachSeparate.py", - "sourcetype": "file", - "tags": [ - "modeling", - "poly", - "detach", - "separate" - ], - "title": "Polygon Detach and Separate", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\polyRelaxVerts.py", - "sourcetype": "file", - "tags": [ - "modeling", - "relax", - "verts" - ], - "title": "Polygon Relax Vertices", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\polySelectEveryNthEdgeUI.py", - "sourcetype": "file", - "tags": [ - "modeling", - "select", - "nth", - "edge", - "ui" - ], - "title": "Select Every Nth Edge" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\modeling\\djPFXUVs.py", - "sourcetype": "file", - "tags": [ - "modeling", - "djPFX", - "UVs" - ], - "title": "dj PFX UVs", - "tooltip": "" - } - ] - }, - { - "type": "menu", - "title": "Rigging", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\addCurveBetween.py", - "sourcetype": "file", - "tags": [ - "rigging", - "addCurveBetween", - "file" - ], - "title": "Add Curve Between" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\averageSkinWeights.py", - "sourcetype": "file", - "tags": [ - "rigging", - "average", - "skin weights", - "file" - ], - "title": "Average Skin Weights" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\cbSmoothSkinWeightUI.py", - "sourcetype": "file", - "tags": [ - "rigging", - "cbSmoothSkinWeightUI", - "file" - ], - "title": "CB Smooth Skin Weight UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\channelBoxManagerUI.py", - "sourcetype": "file", - "tags": [ - "rigging", - "channelBoxManagerUI", - "file" - ], - "title": "Channel Box Manager UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\characterAutorigger.py", - "sourcetype": "file", - "tags": [ - "rigging", - "characterAutorigger", - "file" - ], - "title": "Character Auto Rigger" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\connectUI.py", - "sourcetype": "file", - "tags": [ - "rigging", - "connectUI", - "file" - ], - "title": "Connect UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\copySkinWeightsLocal.py", - "sourcetype": "file", - "tags": [ - "rigging", - "copySkinWeightsLocal", - "file" - ], - "title": "Copy Skin Weights Local" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\createCenterLocator.py", - "sourcetype": "file", - "tags": [ - "rigging", - "createCenterLocator", - "file" - ], - "title": "Create Center Locator" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\freezeTransformToGroup.py", - "sourcetype": "file", - "tags": [ - "rigging", - "freezeTransformToGroup", - "file" - ], - "title": "Freeze Transform To Group" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\groupSelected.py", - "sourcetype": "file", - "tags": [ - "rigging", - "groupSelected", - "file" - ], - "title": "Group Selected" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\ikHandlePoleVectorLocator.py", - "sourcetype": "file", - "tags": [ - "rigging", - "ikHandlePoleVectorLocator", - "file" - ], - "title": "IK Handle Pole Vector Locator" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\jointOrientUI.py", - "sourcetype": "file", - "tags": [ - "rigging", - "jointOrientUI", - "file" - ], - "title": "Joint Orient UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\jointsOnCurve.py", - "sourcetype": "file", - "tags": [ - "rigging", - "jointsOnCurve", - "file" - ], - "title": "Joints On Curve" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\resetBindSelectedSkinJoints.py", - "sourcetype": "file", - "tags": [ - "rigging", - "resetBindSelectedSkinJoints", - "file" - ], - "title": "Reset Bind Selected Skin Joints" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\selectSkinclusterJointsFromSelectedComponents.py", - "sourcetype": "file", - "tags": [ - "rigging", - "selectSkinclusterJointsFromSelectedComponents", - "file" - ], - "title": "Select Skincluster Joints From Selected Components" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\selectSkinclusterJointsFromSelectedMesh.py", - "sourcetype": "file", - "tags": [ - "rigging", - "selectSkinclusterJointsFromSelectedMesh", - "file" - ], - "title": "Select Skincluster Joints From Selected Mesh" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\setJointLabels.py", - "sourcetype": "file", - "tags": [ - "rigging", - "setJointLabels", - "file" - ], - "title": "Set Joint Labels" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\setJointOrientationFromCurrentRotation.py", - "sourcetype": "file", - "tags": [ - "rigging", - "setJointOrientationFromCurrentRotation", - "file" - ], - "title": "Set Joint Orientation From Current Rotation" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\setSelectedJointsOrientationZero.py", - "sourcetype": "file", - "tags": [ - "rigging", - "setSelectedJointsOrientationZero", - "file" - ], - "title": "Set Selected Joints Orientation Zero" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\mirrorCurveShape.py", - "sourcetype": "file", - "tags": [ - "rigging", - "mirrorCurveShape", - "file" - ], - "title": "Mirror Curve Shape" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\setRotationOrderUI.py", - "sourcetype": "file", - "tags": [ - "rigging", - "setRotationOrderUI", - "file" - ], - "title": "Set Rotation Order UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\paintItNowUI.py", - "sourcetype": "file", - "tags": [ - "rigging", - "paintItNowUI", - "file" - ], - "title": "Paint It Now UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\parentScaleConstraint.py", - "sourcetype": "file", - "tags": [ - "rigging", - "parentScaleConstraint", - "file" - ], - "title": "Parent Scale Constraint" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\quickSetWeightsUI.py", - "sourcetype": "file", - "tags": [ - "rigging", - "quickSetWeightsUI", - "file" - ], - "title": "Quick Set Weights UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\rapidRig.py", - "sourcetype": "file", - "tags": [ - "rigging", - "rapidRig", - "file" - ], - "title": "Rapid Rig" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\regenerate_blendshape_targets.py", - "sourcetype": "file", - "tags": [ - "rigging", - "regenerate_blendshape_targets", - "file" - ], - "title": "Regenerate Blendshape Targets" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\removeRotationAxis.py", - "sourcetype": "file", - "tags": [ - "rigging", - "removeRotationAxis", - "file" - ], - "title": "Remove Rotation Axis" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\resetBindSelectedMeshes.py", - "sourcetype": "file", - "tags": [ - "rigging", - "resetBindSelectedMeshes", - "file" - ], - "title": "Reset Bind Selected Meshes" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\simpleControllerOnSelection.py", - "sourcetype": "file", - "tags": [ - "rigging", - "simpleControllerOnSelection", - "file" - ], - "title": "Simple Controller On Selection" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\simpleControllerOnSelectionHierarchy.py", - "sourcetype": "file", - "tags": [ - "rigging", - "simpleControllerOnSelectionHierarchy", - "file" - ], - "title": "Simple Controller On Selection Hierarchy" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\superRelativeCluster.py", - "sourcetype": "file", - "tags": [ - "rigging", - "superRelativeCluster", - "file" - ], - "title": "Super Relative Cluster" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\tfSmoothSkinWeight.py", - "sourcetype": "file", - "tags": [ - "rigging", - "tfSmoothSkinWeight", - "file" - ], - "title": "TF Smooth Skin Weight" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\toggleIntermediates.py", - "sourcetype": "file", - "tags": [ - "rigging", - "toggleIntermediates", - "file" - ], - "title": "Toggle Intermediates" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\toggleSegmentScaleCompensate.py", - "sourcetype": "file", - "tags": [ - "rigging", - "toggleSegmentScaleCompensate", - "file" - ], - "title": "Toggle Segment Scale Compensate" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\rigging\\toggleSkinclusterDeformNormals.py", - "sourcetype": "file", - "tags": [ - "rigging", - "toggleSkinclusterDeformNormals", - "file" - ], - "title": "Toggle Skincluster Deform Normals" - } - ] - }, - { - "type": "menu", - "title": "Shading", - "items": [ - { - "type": "menu", - "title": "VRay", - "items": [ - { - "type": "action", - "title": "Import Proxies", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayImportProxies.py", - "sourcetype": "file", - "tags": ["shading", "vray", "import", "proxies"], - "tooltip": "" - }, - { - "type": "separator" - }, - { - "type": "action", - "title": "Select All GES", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectAllGES.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "select All GES" - ] - }, - { - "type": "action", - "title": "Select All GES Under Selection", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectAllGESUnderSelection.py", - "sourcetype": "file", - "tooltip": "", - "tags": ["shading", "vray", "select", "all", "GES"] - }, - { - "type": "separator" - }, - { - "type": "action", - "title": "Selection To VRay Mesh", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectionToVrayMesh.py", - "sourcetype": "file", - "tooltip": "", - "tags": ["shading", "vray", "selection", "vraymesh"] - }, - { - "type": "action", - "title": "Add VRay Round Edges Attribute", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayRoundEdgesAttribute.py", - "sourcetype": "file", - "tooltip": "", - "tags": ["shading", "vray", "round edges", "attribute"] - }, - { - "type": "action", - "title": "Add Gamma", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayAddGamma.py", - "sourcetype": "file", - "tooltip": "", - "tags": ["shading", "vray", "add gamma"] - }, - { - "type": "separator" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\select_vraymesh_materials_with_unconnected_shader_slots.py", - "sourcetype": "file", - "title": "Select Unconnected Shader Materials", - "tags": [ - "shading", - "vray", - "select", - "vraymesh", - "materials", - "unconnected shader slots" - ], - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayMergeSimilarVRayMeshMaterials.py", - "sourcetype": "file", - "title": "Merge Similar VRay Mesh Materials", - "tags": [ - "shading", - "vray", - "Merge", - "VRayMesh", - "Materials" - ], - "tooltip": "" - }, - { - "type": "action", - "title": "Create Two Sided Material", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayCreate2SidedMtlForSelectedMtlRenamed.py", - "sourcetype": "file", - "tooltip": "Creates two sided material for selected material and renames it", - "tags": [ - "shading", - "vray", - "two sided", - "material" - ] - }, - { - "type": "action", - "title": "Create Two Sided Material For Selected", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayCreate2SidedMtlForSelectedMtl.py", - "sourcetype": "file", - "tooltip": "Select material to create a two sided version from it", - "tags": [ - "shading", - "vray", - "Create2SidedMtlForSelectedMtl.py" - ] - }, - { - "type": "separator" - }, - { - "type": "action", - "title": "Add OpenSubdiv Attribute", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayOpenSubdivAttribute.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "add", - "open subdiv", - "attribute" - ] - }, - { - "type": "action", - "title": "Remove OpenSubdiv Attribute", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\removeVrayOpenSubdivAttribute.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "remove", - "opensubdiv", - "attributee" - ] - }, - { - "type": "separator" - }, - { - "type": "action", - "title": "Add Subdivision Attribute", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVraySubdivisionAttribute.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "addVraySubdivisionAttribute" - ] - }, - { - "type": "action", - "title": "Remove Subdivision Attribute.py", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\removeVraySubdivisionAttribute.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "remove", - "subdivision", - "attribute" - ] - }, - { - "type": "separator" - }, - { - "type": "action", - "title": "Add Vray Object Ids", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayObjectIds.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "add", - "object id" - ] - }, - { - "type": "action", - "title": "Add Vray Material Ids", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayMaterialIds.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "addVrayMaterialIds.py" - ] - }, - { - "type": "separator" - }, - { - "type": "action", - "title": "Set Physical DOF Depth", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayPhysicalDOFSetDepth.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "physical", - "DOF ", - "Depth" - ] - }, - { - "type": "action", - "title": "Magic Vray Proxy UI", - "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\magicVrayProxyUI.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "magicVrayProxyUI" - ] - } - ] - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\pyblish\\lighting\\set_filename_prefix.py", - "sourcetype": "file", - "tags": [ - "shading", - "lookdev", - "assign", - "shaders", - "prefix", - "filename", - "render" - ], - "title": "Set filename prefix", - "tooltip": "Set the render file name prefix." - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\look_manager_ui.py", - "sourcetype": "file", - "tags": [ - "shading", - "look", - "assign", - "shaders", - "auto" - ], - "title": "Look Manager", - "tooltip": "Open the Look Manager UI for look assignment" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\LightLinkUi.py", - "sourcetype": "file", - "tags": [ - "shading", - "light", - "link", - "ui" - ], - "title": "Light Link UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\vdviewer_ui.py", - "sourcetype": "file", - "tags": [ - "shading", - "look", - "vray", - "displacement", - "shaders", - "auto" - ], - "title": "VRay Displ Viewer", - "tooltip": "Open the VRay Displacement Viewer, select and control the content of the set" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\setTexturePreviewToCLRImage.py", - "sourcetype": "file", - "tags": [ - "shading", - "CLRImage", - "textures", - "preview" - ], - "title": "Set Texture Preview To CLRImage", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\fixDefaultShaderSetBehavior.py", - "sourcetype": "file", - "tags": [ - "shading", - "fix", - "DefaultShaderSet", - "Behavior" - ], - "title": "Fix Default Shader Set Behavior", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\fixSelectedShapesReferenceAssignments.py", - "sourcetype": "file", - "tags": [ - "shading", - "fix", - "Selected", - "Shapes", - "Reference", - "Assignments" - ], - "title": "Fix Shapes Reference Assignments", - "tooltip": "Select shapes to fix the reference assignments" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\selectLambert1Members.py", - "sourcetype": "file", - "tags": [ - "shading", - "selectLambert1Members" - ], - "title": "Select Lambert1 Members", - "tooltip": "Selects all objects which have the Lambert1 shader assigned" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\selectShapesWithoutShader.py", - "sourcetype": "file", - "tags": [ - "shading", - "selectShapesWithoutShader" - ], - "title": "Select Shapes Without Shader", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\fixRenderLayerOutAdjustmentErrors.py", - "sourcetype": "file", - "tags": [ - "shading", - "fixRenderLayerOutAdjustmentErrors" - ], - "title": "Fix RenderLayer Out Adjustment Errors", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\shading\\fix_renderlayer_missing_node_override.py", - "sourcetype": "file", - "tags": [ - "shading", - "renderlayer", - "missing", - "reference", - "switch", - "layer" - ], - "title": "Fix RenderLayer Missing Referenced Nodes Overrides", - "tooltip": "" - }, - { - "type": "action", - "title": "Image 2 Tiled EXR", - "command": "$COLORBLEED_SCRIPTS\\shading\\open_img2exr.py", - "sourcetype": "file", - "tooltip": "", - "tags": [ - "shading", - "vray", - "exr" - ] - } - ] - }, - { - "type": "menu", - "title": "Rendering", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\pyblish\\open_deadline_submission_settings.py", - "sourcetype": "file", - "tags": [ - "settings", - "deadline", - "globals", - "render" - ], - "title": "DL Submission Settings UI", - "tooltip": "Open the Deadline Submission Settings UI" - } - ] - }, - { - "type": "menu", - "title": "Animation", - "items": [ - { - "type": "menu", - "title": "Attributes", - "tooltip": "", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyValues.py", - "sourcetype": "file", - "tags": [ - "animation", - "copy", - "attributes" - ], - "title": "Copy Values", - "tooltip": "Copy attribute values" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyInConnections.py", - "sourcetype": "file", - "tags": [ - "animation", - "copy", - "attributes", - "connections", - "incoming" - ], - "title": "Copy In Connections", - "tooltip": "Copy incoming connections" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyOutConnections.py", - "sourcetype": "file", - "tags": [ - "animation", - "copy", - "attributes", - "connections", - "out" - ], - "title": "Copy Out Connections", - "tooltip": "Copy outcoming connections" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformLocal.py", - "sourcetype": "file", - "tags": [ - "animation", - "copy", - "attributes", - "transforms", - "local" - ], - "title": "Copy Local Transfroms", - "tooltip": "Copy local transfroms" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformMatrix.py", - "sourcetype": "file", - "tags": [ - "animation", - "copy", - "attributes", - "transforms", - "matrix" - ], - "title": "Copy Matrix Transfroms", - "tooltip": "Copy Matrix transfroms" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformUI.py", - "sourcetype": "file", - "tags": [ - "animation", - "copy", - "attributes", - "transforms", - "UI" - ], - "title": "Copy Transforms UI", - "tooltip": "Open the Copy Transforms UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\simpleCopyUI.py", - "sourcetype": "file", - "tags": [ - "animation", - "copy", - "attributes", - "transforms", - "UI", - "simple" - ], - "title": "Simple Copy UI", - "tooltip": "Open the simple Copy Transforms UI" - } - ] - }, - { - "type": "menu", - "title": "Optimize", - "tooltip": "Optimization scripts", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\optimize\\toggleFreezeHierarchy.py", - "sourcetype": "file", - "tags": [ - "animation", - "hierarchy", - "toggle", - "freeze" - ], - "title": "Toggle Freeze Hierarchy", - "tooltip": "Freeze and unfreeze hierarchy" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\animation\\optimize\\toggleParallelNucleus.py", - "sourcetype": "file", - "tags": [ - "animation", - "nucleus", - "toggle", - "parallel" - ], - "title": "Toggle Parallel Nucleus", - "tooltip": "Toggle parallel nucleus" - } - ] - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\bakeSelectedToWorldSpace.py", - "tags": ["animation", "bake","selection", "worldspace.py"], - "title": "Bake Selected To Worldspace", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\timeStepper.py", - "tags": ["animation", "time","stepper"], - "title": "Time Stepper", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\capture_ui.py", - "tags": ["animation", "capture", "ui", "screen", "movie", "image"], - "title": "Capture UI", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\simplePlayblastUI.py", - "tags": ["animation", "simple", "playblast", "ui"], - "title": "Simple Playblast UI", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\tweenMachineUI.py", - "tags": ["animation", "tween", "machine"], - "title": "Tween Machine UI", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\selectAllAnimationCurves.py", - "tags": ["animation", "select", "curves"], - "title": "Select All Animation Curves", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\pathAnimation.py", - "tags": ["animation", "path", "along"], - "title": "Path Animation", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\offsetSelectedObjectsUI.py", - "tags": [ - "animation", - "offsetSelectedObjectsUI.py" - ], - "title": "Offset Selected Objects UI", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\key_amplifier_ui.py", - "tags": [ - "animation", - "key", "amplifier" - ], - "title": "Key Amplifier UI", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\anim_scene_optimizer.py", - "tags": [ - "animation", - "anim_scene_optimizer.py" - ], - "title": "Anim_Scene_Optimizer", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\zvParentMaster.py", - "tags": [ - "animation", - "zvParentMaster.py" - ], - "title": "ZV Parent Master", - "type": "action" - }, - { - "sourcetype": "file", - "command": "$COLORBLEED_SCRIPTS\\animation\\poseLibrary.py", - "tags": [ - "animation", - "poseLibrary.py" - ], - "title": "Pose Library", - "type": "action" - } - ] - }, - { - "type": "menu", - "title": "Layout", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\alignDistributeUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "align", - "Distribute", - "UI" - ], - "title": "Align Distribute UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\alignSimpleUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "align", - "UI", - "Simple" - ], - "title": "Align Simple UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\center_locator.py", - "sourcetype": "file", - "tags": [ - "layout", - "center", - "locator" - ], - "title": "Center Locator", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\average_locator.py", - "sourcetype": "file", - "tags": [ - "layout", - "average", - "locator" - ], - "title": "Average Locator", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\selectWithinProximityUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "select", - "proximity", - "ui" - ], - "title": "Select Within Proximity UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\dupCurveUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "Duplicate", - "Curve", - "UI" - ], - "title": "Duplicate Curve UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\randomDeselectUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "random", - "Deselect", - "UI" - ], - "title": "Random Deselect UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\multiReferencerUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "multi", - "reference" - ], - "title": "Multi Referencer UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\duplicateOffsetUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "duplicate", - "offset", - "UI" - ], - "title": "Duplicate Offset UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\spPaint3d.py", - "sourcetype": "file", - "tags": [ - "layout", - "spPaint3d", - "paint", - "tool" - ], - "title": "SP Paint 3d", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\randomizeUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "randomize", - "UI" - ], - "title": "Randomize UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\layout\\distributeWithinObjectUI.py", - "sourcetype": "file", - "tags": [ - "layout", - "distribute", - "ObjectUI", - "within" - ], - "title": "Distribute Within Object UI", - "tooltip": "" - } - ] - }, - { - "type": "menu", - "title": "Particles", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjects.py", - "sourcetype": "file", - "tags": [ - "particles", - "instancerToObjects" - ], - "title": "Instancer To Objects", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjectsInstances.py", - "sourcetype": "file", - "tags": [ - "particles", - "instancerToObjectsInstances" - ], - "title": "Instancer To Objects Instances", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\objectsToParticlesAndInstancerCleanSource.py", - "sourcetype": "file", - "tags": ["particles", "objects", "Particles", "Instancer", "Clean", "Source"], - "title": "Objects To Particles & Instancer - Clean Source", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\particleComponentsToLocators.py", - "sourcetype": "file", - "tags": ["particles", "components", "locators"], - "title": "Particle Components To Locators", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\objectsToParticlesAndInstancer.py", - "sourcetype": "file", - "tags": [ - "particles", "objects", "particles", "instancer"], - "title": "Objects To Particles And Instancer", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\spawnParticlesOnMesh.py", - "sourcetype": "file", - "tags": ["particles", "spawn","on","mesh"], - "title": "Spawn Particles On Mesh", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjectsInstancesWithAnimation.py", - "sourcetype": "file", - "tags": [ - "particles", - "instancerToObjectsInstancesWithAnimation" - ], - "title": "Instancer To Objects Instances With Animation", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\objectsToParticles.py", - "sourcetype": "file", - "tags": [ - "particles", - "objectsToParticles" - ], - "title": "Objects To Particles", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\add_particle_cacheFile_attrs.py", - "sourcetype": "file", - "tags": [ - "particles", - "add_particle_cacheFile_attrs" - ], - "title": "Add Particle CacheFile Attributes", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\mergeParticleSystems.py", - "sourcetype": "file", - "tags": [ - "particles", - "mergeParticleSystems" - ], - "title": "Merge Particle Systems", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\particlesToLocators.py", - "sourcetype": "file", - "tags": [ - "particles", - "particlesToLocators" - ], - "title": "Particles To Locators", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjectsWithAnimation.py", - "sourcetype": "file", - "tags": [ - "particles", - "instancerToObjectsWithAnimation" - ], - "title": "Instancer To Objects With Animation", - "tooltip": "" - }, - {"type": "separator"}, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\mayaReplicateHoudiniTool.py", - "sourcetype": "file", - "tags": [ - "particles", - "houdini", "houdiniTool", "houdiniEngine" - ], - "title": "Replicate Houdini Tool", - "tooltip": "" - }, - {"type": "separator"}, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\clearInitialState.py", - "sourcetype": "file", - "tags": [ - "particles", - "clearInitialState" - ], - "title": "Clear Initial State", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\particles\\killSelectedParticles.py", - "sourcetype": "file", - "tags": [ - "particles", - "killSelectedParticles" - ], - "title": "Kill Selected Particles", - "tooltip": "" - } - ] - }, - { - "type": "menu", - "title": "Cleanup", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\repair_faulty_containers.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "repair", "containers" - ], - "title": "Find and Repair Containers", - "tooltip": "" - },{ - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\selectByType.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "selectByType" - ], - "title": "Select By Type", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\selectIntermediateObjects.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "selectIntermediateObjects" - ], - "title": "Select Intermediate Objects", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\selectNonUniqueNames.py", - "sourcetype": "file", - "tags": ["cleanup", "select", "non unique", "names"], - "title": "Select Non Unique Names", - "tooltip": "" - }, - {"type": "separator"}, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeNamespaces.py", - "sourcetype": "file", - "tags": ["cleanup", "remove", "namespaces"], - "title": "Remove Namespaces", - "tooltip": "Remove all namespaces" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\remove_user_defined_attributes.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "remove_user_defined_attributes" - ], - "title": "Remove User Defined Attributes", - "tooltip": "Remove all user-defined attributs from all nodes" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeUnknownNodes.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "removeUnknownNodes" - ], - "title": "Remove Unknown Nodes", - "tooltip": "Remove all unknown nodes" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeUnloadedReferences.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "removeUnloadedReferences" - ], - "title": "Remove Unloaded References", - "tooltip": "Remove all unloaded references" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeReferencesFailedEdits.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "removeReferencesFailedEdits" - ], - "title": "Remove References Failed Edits", - "tooltip": "Remove failed edits for all references" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\remove_unused_looks.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "removeUnusedLooks" - ], - "title": "Remove Unused Looks", - "tooltip": "Remove all loaded yet unused Avalon look containers" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\deleteGhostIntermediateObjects.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "deleteGhostIntermediateObjects" - ], - "title": "Delete Ghost Intermediate Objects", - "tooltip": "" - }, - {"type": "separator"}, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\resetViewportCache.py", - "sourcetype": "file", - "tags": ["cleanup", "reset","viewport", "cache"], - "title": "Reset Viewport Cache", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\uniqifyNodeNames.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "uniqifyNodeNames" - ], - "title": "Uniqify Node Names", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\autoRenameFileNodes.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "auto", "rename","filenodes" - ], - "title": "Auto Rename File Nodes", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\update_asset_id.py", - "sourcetype": "file", - "tags":["cleanup", "update", "database", "asset", "id"], - "title": "Update Asset ID", - "tooltip": "Will replace the Colorbleed ID with a new one (asset ID : Unique number)" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\colorbleedRename.py", - "sourcetype": "file", - "tags": ["cleanup", "rename", "ui"], - "title": "Colorbleed Renamer", - "tooltip": "Colorbleed Rename UI" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\renameShapesToTransform.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "renameShapesToTransform" - ], - "title": "Rename Shapes To Transform", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\reorderUI.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "reorderUI" - ], - "title": "Reorder UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\cleanup\\pastedCleaner.py", - "sourcetype": "file", - "tags": [ - "cleanup", - "pastedCleaner" - ], - "title": "Pasted Cleaner", - "tooltip": "" - } - ] - }, - { - "type": "menu", - "title": "Others", - "items": [ - { - "type": "menu", - "sourcetype": "file", - "title": "Yeti", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\yeti\\cache_selected_yeti_nodes.py", - "sourcetype": "file", - "tags": ["others", "yeti", "cache", "selected"], - "title": "Cache Selected Yeti Nodes", - "tooltip": "" - } - ] - }, - { - "type": "menu", - "title": "Hair", - "tooltip": "", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\hair\\recolorHairCurrentCurve", - "sourcetype": "file", - "tags": ["others", "selectSoftSelection"], - "title": "Select Soft Selection", - "tooltip": "" - } - ] - }, - { - "type": "menu", - "command": "$COLORBLEED_SCRIPTS\\others\\display", - "sourcetype": "file", - "tags": [ - "others", - "display" - ], - "title": "Display", - "items": [ - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\display\\wireframeSelectedObjects.py", - "sourcetype": "file", - "tags": ["others", "wireframe","selected","objects"], - "title": "Wireframe Selected Objects", - "tooltip": "" - } - ] - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\archiveSceneUI.py", - "sourcetype": "file", - "tags": [ - "others", - "archiveSceneUI" - ], - "title": "Archive Scene UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\getSimilarMeshes.py", - "sourcetype": "file", - "tags": [ - "others", - "getSimilarMeshes" - ], - "title": "Get Similar Meshes", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\createBoundingBoxEachSelected.py", - "sourcetype": "file", - "tags": [ - "others", - "createBoundingBoxEachSelected" - ], - "title": "Create BoundingBox Each Selected", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\curveFromPositionEveryFrame.py", - "sourcetype": "file", - "tags": [ - "others", - "curveFromPositionEveryFrame" - ], - "title": "Curve From Position", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\instanceLeafSmartTransform.py", - "sourcetype": "file", - "tags": ["others", "instance","leaf", "smart", "transform"], - "title": "Instance Leaf Smart Transform", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\instanceSmartTransform.py", - "sourcetype": "file", - "tags": ["others", "instance", "smart", "transform"], - "title": "Instance Smart Transform", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\randomizeUVShellsSelectedObjects.py", - "sourcetype": "file", - "tags": [ - "others", - "randomizeUVShellsSelectedObjects" - ], - "title": "Randomize UV Shells", - "tooltip": "Select objects before running action" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\centerPivotGroup.py", - "sourcetype": "file", - "tags": [ - "others", - "centerPivotGroup" - ], - "title": "Center Pivot Group", - "tooltip": "" - }, - {"type": "separator"}, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\locatorsOnSelectedFaces.py", - "sourcetype": "file", - "tags": [ - "others", - "locatorsOnSelectedFaces" - ], - "title": "Locators On Selected Faces", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\locatorsOnEdgeSelectionPrompt.py", - "sourcetype": "file", - "tags": [ - "others", - "locatorsOnEdgeSelectionPrompt" - ], - "title": "Locators On Edge Selection Prompt", - "tooltip": "" - }, - {"type": "separator"}, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\copyDeformers.py", - "sourcetype": "file", - "tags": [ - "others", - "copyDeformers" - ], - "title": "Copy Deformers", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\selectInReferenceEditor.py", - "sourcetype": "file", - "tags": [ - "others", - "selectInReferenceEditor" - ], - "title": "Select In Reference Editor", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\selectConstrainingObject.py", - "sourcetype": "file", - "tags": [ - "others", - "selectConstrainingObject" - ], - "title": "Select Constraining Object", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\deformerSetRelationsUI.py", - "sourcetype": "file", - "tags": [ - "others", - "deformerSetRelationsUI" - ], - "title": "Deformer Set Relations UI", - "tooltip": "" - }, - { - "type": "action", - "command": "$COLORBLEED_SCRIPTS\\others\\recreateBaseNodesForAllLatticeNodes.py", - "sourcetype": "file", - "tags": ["others", "recreate","base", "nodes", "lattice"], - "title": "Recreate Base Nodes For Lattice Nodes", - "tooltip": "" - } - ] - } -] \ No newline at end of file +[{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\save_scene_incremental.py", + "sourcetype": "file", + "title": "Version Up", + "tooltip": "Incremental save with a specific format" +}, +{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\show_current_scene_in_explorer.py", + "sourcetype": "file", + "title": "Explore current scene..", + "tooltip": "Show current scene in Explorer" +}, +{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\avalon\\launch_manager.py", + "sourcetype": "file", + "title": "Project Manager", + "tooltip": "Add assets to the project" +}, +{ + "type": "separator" +}, +{ + "type": "menu", + "title": "Modeling", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\duplicate_normalized.py", + "sourcetype": "file", + "tags": ["modeling", + "duplicate", + "normalized"], + "title": "Duplicate Normalized", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\transferUVs.py", + "sourcetype": "file", + "tags": ["modeling", + "transfer", + "uv"], + "title": "Transfer UVs", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\mirrorSymmetry.py", + "sourcetype": "file", + "tags": ["modeling", + "mirror", + "symmetry"], + "title": "Mirror Symmetry", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\selectOutlineUI.py", + "sourcetype": "file", + "tags": ["modeling", + "select", + "outline", + "ui"], + "title": "Select Outline UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyDeleteOtherUVSets.py", + "sourcetype": "file", + "tags": ["modeling", + "polygon", + "uvset", + "delete"], + "title": "Polygon Delete Other UV Sets", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyCombineQuick.py", + "sourcetype": "file", + "tags": ["modeling", + "combine", + "polygon", + "quick"], + "title": "Polygon Combine Quick", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\separateMeshPerShader.py", + "sourcetype": "file", + "tags": ["modeling", + "separateMeshPerShader"], + "title": "Separate Mesh Per Shader", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyDetachSeparate.py", + "sourcetype": "file", + "tags": ["modeling", + "poly", + "detach", + "separate"], + "title": "Polygon Detach and Separate", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\polyRelaxVerts.py", + "sourcetype": "file", + "tags": ["modeling", + "relax", + "verts"], + "title": "Polygon Relax Vertices", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\polySelectEveryNthEdgeUI.py", + "sourcetype": "file", + "tags": ["modeling", + "select", + "nth", + "edge", + "ui"], + "title": "Select Every Nth Edge" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\modeling\\djPFXUVs.py", + "sourcetype": "file", + "tags": ["modeling", + "djPFX", + "UVs"], + "title": "dj PFX UVs", + "tooltip": "" + }] +}, +{ + "type": "menu", + "title": "Rigging", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\addCurveBetween.py", + "sourcetype": "file", + "tags": ["rigging", + "addCurveBetween", + "file"], + "title": "Add Curve Between" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\averageSkinWeights.py", + "sourcetype": "file", + "tags": ["rigging", + "average", + "skin weights", + "file"], + "title": "Average Skin Weights" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\cbSmoothSkinWeightUI.py", + "sourcetype": "file", + "tags": ["rigging", + "cbSmoothSkinWeightUI", + "file"], + "title": "CB Smooth Skin Weight UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\channelBoxManagerUI.py", + "sourcetype": "file", + "tags": ["rigging", + "channelBoxManagerUI", + "file"], + "title": "Channel Box Manager UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\characterAutorigger.py", + "sourcetype": "file", + "tags": ["rigging", + "characterAutorigger", + "file"], + "title": "Character Auto Rigger" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\connectUI.py", + "sourcetype": "file", + "tags": ["rigging", + "connectUI", + "file"], + "title": "Connect UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\copySkinWeightsLocal.py", + "sourcetype": "file", + "tags": ["rigging", + "copySkinWeightsLocal", + "file"], + "title": "Copy Skin Weights Local" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\createCenterLocator.py", + "sourcetype": "file", + "tags": ["rigging", + "createCenterLocator", + "file"], + "title": "Create Center Locator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\freezeTransformToGroup.py", + "sourcetype": "file", + "tags": ["rigging", + "freezeTransformToGroup", + "file"], + "title": "Freeze Transform To Group" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\groupSelected.py", + "sourcetype": "file", + "tags": ["rigging", + "groupSelected", + "file"], + "title": "Group Selected" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\ikHandlePoleVectorLocator.py", + "sourcetype": "file", + "tags": ["rigging", + "ikHandlePoleVectorLocator", + "file"], + "title": "IK Handle Pole Vector Locator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\jointOrientUI.py", + "sourcetype": "file", + "tags": ["rigging", + "jointOrientUI", + "file"], + "title": "Joint Orient UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\jointsOnCurve.py", + "sourcetype": "file", + "tags": ["rigging", + "jointsOnCurve", + "file"], + "title": "Joints On Curve" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\resetBindSelectedSkinJoints.py", + "sourcetype": "file", + "tags": ["rigging", + "resetBindSelectedSkinJoints", + "file"], + "title": "Reset Bind Selected Skin Joints" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\selectSkinclusterJointsFromSelectedComponents.py", + "sourcetype": "file", + "tags": ["rigging", + "selectSkinclusterJointsFromSelectedComponents", + "file"], + "title": "Select Skincluster Joints From Selected Components" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\selectSkinclusterJointsFromSelectedMesh.py", + "sourcetype": "file", + "tags": ["rigging", + "selectSkinclusterJointsFromSelectedMesh", + "file"], + "title": "Select Skincluster Joints From Selected Mesh" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\setJointLabels.py", + "sourcetype": "file", + "tags": ["rigging", + "setJointLabels", + "file"], + "title": "Set Joint Labels" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\setJointOrientationFromCurrentRotation.py", + "sourcetype": "file", + "tags": ["rigging", + "setJointOrientationFromCurrentRotation", + "file"], + "title": "Set Joint Orientation From Current Rotation" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\setSelectedJointsOrientationZero.py", + "sourcetype": "file", + "tags": ["rigging", + "setSelectedJointsOrientationZero", + "file"], + "title": "Set Selected Joints Orientation Zero" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\mirrorCurveShape.py", + "sourcetype": "file", + "tags": ["rigging", + "mirrorCurveShape", + "file"], + "title": "Mirror Curve Shape" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\setRotationOrderUI.py", + "sourcetype": "file", + "tags": ["rigging", + "setRotationOrderUI", + "file"], + "title": "Set Rotation Order UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\paintItNowUI.py", + "sourcetype": "file", + "tags": ["rigging", + "paintItNowUI", + "file"], + "title": "Paint It Now UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\parentScaleConstraint.py", + "sourcetype": "file", + "tags": ["rigging", + "parentScaleConstraint", + "file"], + "title": "Parent Scale Constraint" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\quickSetWeightsUI.py", + "sourcetype": "file", + "tags": ["rigging", + "quickSetWeightsUI", + "file"], + "title": "Quick Set Weights UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\rapidRig.py", + "sourcetype": "file", + "tags": ["rigging", + "rapidRig", + "file"], + "title": "Rapid Rig" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\regenerate_blendshape_targets.py", + "sourcetype": "file", + "tags": ["rigging", + "regenerate_blendshape_targets", + "file"], + "title": "Regenerate Blendshape Targets" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\removeRotationAxis.py", + "sourcetype": "file", + "tags": ["rigging", + "removeRotationAxis", + "file"], + "title": "Remove Rotation Axis" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\resetBindSelectedMeshes.py", + "sourcetype": "file", + "tags": ["rigging", + "resetBindSelectedMeshes", + "file"], + "title": "Reset Bind Selected Meshes" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\simpleControllerOnSelection.py", + "sourcetype": "file", + "tags": ["rigging", + "simpleControllerOnSelection", + "file"], + "title": "Simple Controller On Selection" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\simpleControllerOnSelectionHierarchy.py", + "sourcetype": "file", + "tags": ["rigging", + "simpleControllerOnSelectionHierarchy", + "file"], + "title": "Simple Controller On Selection Hierarchy" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\superRelativeCluster.py", + "sourcetype": "file", + "tags": ["rigging", + "superRelativeCluster", + "file"], + "title": "Super Relative Cluster" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\tfSmoothSkinWeight.py", + "sourcetype": "file", + "tags": ["rigging", + "tfSmoothSkinWeight", + "file"], + "title": "TF Smooth Skin Weight" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\toggleIntermediates.py", + "sourcetype": "file", + "tags": ["rigging", + "toggleIntermediates", + "file"], + "title": "Toggle Intermediates" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\toggleSegmentScaleCompensate.py", + "sourcetype": "file", + "tags": ["rigging", + "toggleSegmentScaleCompensate", + "file"], + "title": "Toggle Segment Scale Compensate" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\rigging\\toggleSkinclusterDeformNormals.py", + "sourcetype": "file", + "tags": ["rigging", + "toggleSkinclusterDeformNormals", + "file"], + "title": "Toggle Skincluster Deform Normals" + }] +}, +{ + "type": "menu", + "title": "Shading", + "items": [{ + "type": "menu", + "title": "VRay", + "items": [{ + "type": "action", + "title": "Import Proxies", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayImportProxies.py", + "sourcetype": "file", + "tags": ["shading", + "vray", + "import", + "proxies"], + "tooltip": "" + }, + { + "type": "separator" + }, + { + "type": "action", + "title": "Select All GES", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectAllGES.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "select All GES"] + }, + { + "type": "action", + "title": "Select All GES Under Selection", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectAllGESUnderSelection.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "select", + "all", + "GES"] + }, + { + "type": "separator" + }, + { + "type": "action", + "title": "Selection To VRay Mesh", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\selectionToVrayMesh.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "selection", + "vraymesh"] + }, + { + "type": "action", + "title": "Add VRay Round Edges Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayRoundEdgesAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "round edges", + "attribute"] + }, + { + "type": "action", + "title": "Add Gamma", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayAddGamma.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "add gamma"] + }, + { + "type": "separator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\select_vraymesh_materials_with_unconnected_shader_slots.py", + "sourcetype": "file", + "title": "Select Unconnected Shader Materials", + "tags": ["shading", + "vray", + "select", + "vraymesh", + "materials", + "unconnected shader slots"], + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayMergeSimilarVRayMeshMaterials.py", + "sourcetype": "file", + "title": "Merge Similar VRay Mesh Materials", + "tags": ["shading", + "vray", + "Merge", + "VRayMesh", + "Materials"], + "tooltip": "" + }, + { + "type": "action", + "title": "Create Two Sided Material", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayCreate2SidedMtlForSelectedMtlRenamed.py", + "sourcetype": "file", + "tooltip": "Creates two sided material for selected material and renames it", + "tags": ["shading", + "vray", + "two sided", + "material"] + }, + { + "type": "action", + "title": "Create Two Sided Material For Selected", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayCreate2SidedMtlForSelectedMtl.py", + "sourcetype": "file", + "tooltip": "Select material to create a two sided version from it", + "tags": ["shading", + "vray", + "Create2SidedMtlForSelectedMtl.py"] + }, + { + "type": "separator" + }, + { + "type": "action", + "title": "Add OpenSubdiv Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayOpenSubdivAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "add", + "open subdiv", + "attribute"] + }, + { + "type": "action", + "title": "Remove OpenSubdiv Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\removeVrayOpenSubdivAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "remove", + "opensubdiv", + "attributee"] + }, + { + "type": "separator" + }, + { + "type": "action", + "title": "Add Subdivision Attribute", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVraySubdivisionAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "addVraySubdivisionAttribute"] + }, + { + "type": "action", + "title": "Remove Subdivision Attribute.py", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\removeVraySubdivisionAttribute.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "remove", + "subdivision", + "attribute"] + }, + { + "type": "separator" + }, + { + "type": "action", + "title": "Add Vray Object Ids", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayObjectIds.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "add", + "object id"] + }, + { + "type": "action", + "title": "Add Vray Material Ids", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\addVrayMaterialIds.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "addVrayMaterialIds.py"] + }, + { + "type": "separator" + }, + { + "type": "action", + "title": "Set Physical DOF Depth", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\vrayPhysicalDOFSetDepth.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "physical", + "DOF ", + "Depth"] + }, + { + "type": "action", + "title": "Magic Vray Proxy UI", + "command": "$COLORBLEED_SCRIPTS\\shading\\vray\\magicVrayProxyUI.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "magicVrayProxyUI"] + }] + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\pyblish\\lighting\\set_filename_prefix.py", + "sourcetype": "file", + "tags": ["shading", + "lookdev", + "assign", + "shaders", + "prefix", + "filename", + "render"], + "title": "Set filename prefix", + "tooltip": "Set the render file name prefix." + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\look_manager_ui.py", + "sourcetype": "file", + "tags": ["shading", + "look", + "assign", + "shaders", + "auto"], + "title": "Look Manager", + "tooltip": "Open the Look Manager UI for look assignment" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\LightLinkUi.py", + "sourcetype": "file", + "tags": ["shading", + "light", + "link", + "ui"], + "title": "Light Link UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\vdviewer_ui.py", + "sourcetype": "file", + "tags": ["shading", + "look", + "vray", + "displacement", + "shaders", + "auto"], + "title": "VRay Displ Viewer", + "tooltip": "Open the VRay Displacement Viewer, select and control the content of the set" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\setTexturePreviewToCLRImage.py", + "sourcetype": "file", + "tags": ["shading", + "CLRImage", + "textures", + "preview"], + "title": "Set Texture Preview To CLRImage", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\fixDefaultShaderSetBehavior.py", + "sourcetype": "file", + "tags": ["shading", + "fix", + "DefaultShaderSet", + "Behavior"], + "title": "Fix Default Shader Set Behavior", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\fixSelectedShapesReferenceAssignments.py", + "sourcetype": "file", + "tags": ["shading", + "fix", + "Selected", + "Shapes", + "Reference", + "Assignments"], + "title": "Fix Shapes Reference Assignments", + "tooltip": "Select shapes to fix the reference assignments" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\selectLambert1Members.py", + "sourcetype": "file", + "tags": ["shading", + "selectLambert1Members"], + "title": "Select Lambert1 Members", + "tooltip": "Selects all objects which have the Lambert1 shader assigned" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\selectShapesWithoutShader.py", + "sourcetype": "file", + "tags": ["shading", + "selectShapesWithoutShader"], + "title": "Select Shapes Without Shader", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\fixRenderLayerOutAdjustmentErrors.py", + "sourcetype": "file", + "tags": ["shading", + "fixRenderLayerOutAdjustmentErrors"], + "title": "Fix RenderLayer Out Adjustment Errors", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\shading\\fix_renderlayer_missing_node_override.py", + "sourcetype": "file", + "tags": ["shading", + "renderlayer", + "missing", + "reference", + "switch", + "layer"], + "title": "Fix RenderLayer Missing Referenced Nodes Overrides", + "tooltip": "" + }, + { + "type": "action", + "title": "Image 2 Tiled EXR", + "command": "$COLORBLEED_SCRIPTS\\shading\\open_img2exr.py", + "sourcetype": "file", + "tooltip": "", + "tags": ["shading", + "vray", + "exr"] + }] +}, +{ + "type": "menu", + "title": "Rendering", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\pyblish\\open_deadline_submission_settings.py", + "sourcetype": "file", + "tags": ["settings", + "deadline", + "globals", + "render"], + "title": "DL Submission Settings UI", + "tooltip": "Open the Deadline Submission Settings UI" + }] +}, +{ + "type": "menu", + "title": "Animation", + "items": [{ + "type": "menu", + "title": "Attributes", + "tooltip": "", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyValues.py", + "sourcetype": "file", + "tags": ["animation", + "copy", + "attributes"], + "title": "Copy Values", + "tooltip": "Copy attribute values" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyInConnections.py", + "sourcetype": "file", + "tags": ["animation", + "copy", + "attributes", + "connections", + "incoming"], + "title": "Copy In Connections", + "tooltip": "Copy incoming connections" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyOutConnections.py", + "sourcetype": "file", + "tags": ["animation", + "copy", + "attributes", + "connections", + "out"], + "title": "Copy Out Connections", + "tooltip": "Copy outcoming connections" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformLocal.py", + "sourcetype": "file", + "tags": ["animation", + "copy", + "attributes", + "transforms", + "local"], + "title": "Copy Local Transfroms", + "tooltip": "Copy local transfroms" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformMatrix.py", + "sourcetype": "file", + "tags": ["animation", + "copy", + "attributes", + "transforms", + "matrix"], + "title": "Copy Matrix Transfroms", + "tooltip": "Copy Matrix transfroms" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\copyTransformUI.py", + "sourcetype": "file", + "tags": ["animation", + "copy", + "attributes", + "transforms", + "UI"], + "title": "Copy Transforms UI", + "tooltip": "Open the Copy Transforms UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\attributes\\simpleCopyUI.py", + "sourcetype": "file", + "tags": ["animation", + "copy", + "attributes", + "transforms", + "UI", + "simple"], + "title": "Simple Copy UI", + "tooltip": "Open the simple Copy Transforms UI" + }] + }, + { + "type": "menu", + "title": "Optimize", + "tooltip": "Optimization scripts", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\optimize\\toggleFreezeHierarchy.py", + "sourcetype": "file", + "tags": ["animation", + "hierarchy", + "toggle", + "freeze"], + "title": "Toggle Freeze Hierarchy", + "tooltip": "Freeze and unfreeze hierarchy" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\animation\\optimize\\toggleParallelNucleus.py", + "sourcetype": "file", + "tags": ["animation", + "nucleus", + "toggle", + "parallel"], + "title": "Toggle Parallel Nucleus", + "tooltip": "Toggle parallel nucleus" + }] + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\bakeSelectedToWorldSpace.py", + "tags": ["animation", + "bake", + "selection", + "worldspace.py"], + "title": "Bake Selected To Worldspace", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\timeStepper.py", + "tags": ["animation", + "time", + "stepper"], + "title": "Time Stepper", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\capture_ui.py", + "tags": ["animation", + "capture", + "ui", + "screen", + "movie", + "image"], + "title": "Capture UI", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\simplePlayblastUI.py", + "tags": ["animation", + "simple", + "playblast", + "ui"], + "title": "Simple Playblast UI", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\tweenMachineUI.py", + "tags": ["animation", + "tween", + "machine"], + "title": "Tween Machine UI", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\selectAllAnimationCurves.py", + "tags": ["animation", + "select", + "curves"], + "title": "Select All Animation Curves", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\pathAnimation.py", + "tags": ["animation", + "path", + "along"], + "title": "Path Animation", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\offsetSelectedObjectsUI.py", + "tags": ["animation", + "offsetSelectedObjectsUI.py"], + "title": "Offset Selected Objects UI", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\key_amplifier_ui.py", + "tags": ["animation", + "key", + "amplifier"], + "title": "Key Amplifier UI", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\anim_scene_optimizer.py", + "tags": ["animation", + "anim_scene_optimizer.py"], + "title": "Anim_Scene_Optimizer", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\zvParentMaster.py", + "tags": ["animation", + "zvParentMaster.py"], + "title": "ZV Parent Master", + "type": "action" + }, + { + "sourcetype": "file", + "command": "$COLORBLEED_SCRIPTS\\animation\\poseLibrary.py", + "tags": ["animation", + "poseLibrary.py"], + "title": "Pose Library", + "type": "action" + }] +}, +{ + "type": "menu", + "title": "Layout", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\alignDistributeUI.py", + "sourcetype": "file", + "tags": ["layout", + "align", + "Distribute", + "UI"], + "title": "Align Distribute UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\alignSimpleUI.py", + "sourcetype": "file", + "tags": ["layout", + "align", + "UI", + "Simple"], + "title": "Align Simple UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\center_locator.py", + "sourcetype": "file", + "tags": ["layout", + "center", + "locator"], + "title": "Center Locator", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\average_locator.py", + "sourcetype": "file", + "tags": ["layout", + "average", + "locator"], + "title": "Average Locator", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\selectWithinProximityUI.py", + "sourcetype": "file", + "tags": ["layout", + "select", + "proximity", + "ui"], + "title": "Select Within Proximity UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\dupCurveUI.py", + "sourcetype": "file", + "tags": ["layout", + "Duplicate", + "Curve", + "UI"], + "title": "Duplicate Curve UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\randomDeselectUI.py", + "sourcetype": "file", + "tags": ["layout", + "random", + "Deselect", + "UI"], + "title": "Random Deselect UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\multiReferencerUI.py", + "sourcetype": "file", + "tags": ["layout", + "multi", + "reference"], + "title": "Multi Referencer UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\duplicateOffsetUI.py", + "sourcetype": "file", + "tags": ["layout", + "duplicate", + "offset", + "UI"], + "title": "Duplicate Offset UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\spPaint3d.py", + "sourcetype": "file", + "tags": ["layout", + "spPaint3d", + "paint", + "tool"], + "title": "SP Paint 3d", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\randomizeUI.py", + "sourcetype": "file", + "tags": ["layout", + "randomize", + "UI"], + "title": "Randomize UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\layout\\distributeWithinObjectUI.py", + "sourcetype": "file", + "tags": ["layout", + "distribute", + "ObjectUI", + "within"], + "title": "Distribute Within Object UI", + "tooltip": "" + }] +}, +{ + "type": "menu", + "title": "Particles", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjects.py", + "sourcetype": "file", + "tags": ["particles", + "instancerToObjects"], + "title": "Instancer To Objects", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjectsInstances.py", + "sourcetype": "file", + "tags": ["particles", + "instancerToObjectsInstances"], + "title": "Instancer To Objects Instances", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\objectsToParticlesAndInstancerCleanSource.py", + "sourcetype": "file", + "tags": ["particles", + "objects", + "Particles", + "Instancer", + "Clean", + "Source"], + "title": "Objects To Particles & Instancer - Clean Source", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\particleComponentsToLocators.py", + "sourcetype": "file", + "tags": ["particles", + "components", + "locators"], + "title": "Particle Components To Locators", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\objectsToParticlesAndInstancer.py", + "sourcetype": "file", + "tags": ["particles", + "objects", + "particles", + "instancer"], + "title": "Objects To Particles And Instancer", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\spawnParticlesOnMesh.py", + "sourcetype": "file", + "tags": ["particles", + "spawn", + "on", + "mesh"], + "title": "Spawn Particles On Mesh", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjectsInstancesWithAnimation.py", + "sourcetype": "file", + "tags": ["particles", + "instancerToObjectsInstancesWithAnimation"], + "title": "Instancer To Objects Instances With Animation", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\objectsToParticles.py", + "sourcetype": "file", + "tags": ["particles", + "objectsToParticles"], + "title": "Objects To Particles", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\add_particle_cacheFile_attrs.py", + "sourcetype": "file", + "tags": ["particles", + "add_particle_cacheFile_attrs"], + "title": "Add Particle CacheFile Attributes", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\mergeParticleSystems.py", + "sourcetype": "file", + "tags": ["particles", + "mergeParticleSystems"], + "title": "Merge Particle Systems", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\particlesToLocators.py", + "sourcetype": "file", + "tags": ["particles", + "particlesToLocators"], + "title": "Particles To Locators", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\instancerToObjectsWithAnimation.py", + "sourcetype": "file", + "tags": ["particles", + "instancerToObjectsWithAnimation"], + "title": "Instancer To Objects With Animation", + "tooltip": "" + }, + { + "type": "separator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\mayaReplicateHoudiniTool.py", + "sourcetype": "file", + "tags": ["particles", + "houdini", + "houdiniTool", + "houdiniEngine"], + "title": "Replicate Houdini Tool", + "tooltip": "" + }, + { + "type": "separator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\clearInitialState.py", + "sourcetype": "file", + "tags": ["particles", + "clearInitialState"], + "title": "Clear Initial State", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\particles\\killSelectedParticles.py", + "sourcetype": "file", + "tags": ["particles", + "killSelectedParticles"], + "title": "Kill Selected Particles", + "tooltip": "" + }] +}, +{ + "type": "menu", + "title": "Yeti", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\yeti\\yeti_rig_manager.py", + "sourcetype": "file", + "tags": ["yeti", + "rig", + "fur", + "manager"], + "title": "Open Yeti Rig Manager", + "tooltip": "" + }] +}, +{ + "type": "menu", + "title": "Cleanup", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\repair_faulty_containers.py", + "sourcetype": "file", + "tags": ["cleanup", + "repair", + "containers"], + "title": "Find and Repair Containers", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\selectByType.py", + "sourcetype": "file", + "tags": ["cleanup", + "selectByType"], + "title": "Select By Type", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\selectIntermediateObjects.py", + "sourcetype": "file", + "tags": ["cleanup", + "selectIntermediateObjects"], + "title": "Select Intermediate Objects", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\selectNonUniqueNames.py", + "sourcetype": "file", + "tags": ["cleanup", + "select", + "non unique", + "names"], + "title": "Select Non Unique Names", + "tooltip": "" + }, + { + "type": "separator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeNamespaces.py", + "sourcetype": "file", + "tags": ["cleanup", + "remove", + "namespaces"], + "title": "Remove Namespaces", + "tooltip": "Remove all namespaces" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\remove_user_defined_attributes.py", + "sourcetype": "file", + "tags": ["cleanup", + "remove_user_defined_attributes"], + "title": "Remove User Defined Attributes", + "tooltip": "Remove all user-defined attributs from all nodes" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeUnknownNodes.py", + "sourcetype": "file", + "tags": ["cleanup", + "removeUnknownNodes"], + "title": "Remove Unknown Nodes", + "tooltip": "Remove all unknown nodes" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeUnloadedReferences.py", + "sourcetype": "file", + "tags": ["cleanup", + "removeUnloadedReferences"], + "title": "Remove Unloaded References", + "tooltip": "Remove all unloaded references" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\removeReferencesFailedEdits.py", + "sourcetype": "file", + "tags": ["cleanup", + "removeReferencesFailedEdits"], + "title": "Remove References Failed Edits", + "tooltip": "Remove failed edits for all references" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\remove_unused_looks.py", + "sourcetype": "file", + "tags": ["cleanup", + "removeUnusedLooks"], + "title": "Remove Unused Looks", + "tooltip": "Remove all loaded yet unused Avalon look containers" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\deleteGhostIntermediateObjects.py", + "sourcetype": "file", + "tags": ["cleanup", + "deleteGhostIntermediateObjects"], + "title": "Delete Ghost Intermediate Objects", + "tooltip": "" + }, + { + "type": "separator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\resetViewportCache.py", + "sourcetype": "file", + "tags": ["cleanup", + "reset", + "viewport", + "cache"], + "title": "Reset Viewport Cache", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\uniqifyNodeNames.py", + "sourcetype": "file", + "tags": ["cleanup", + "uniqifyNodeNames"], + "title": "Uniqify Node Names", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\autoRenameFileNodes.py", + "sourcetype": "file", + "tags": ["cleanup", + "auto", + "rename", + "filenodes"], + "title": "Auto Rename File Nodes", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\update_asset_id.py", + "sourcetype": "file", + "tags": ["cleanup", + "update", + "database", + "asset", + "id"], + "title": "Update Asset ID", + "tooltip": "Will replace the Colorbleed ID with a new one (asset ID : Unique number)" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\colorbleedRename.py", + "sourcetype": "file", + "tags": ["cleanup", + "rename", + "ui"], + "title": "Colorbleed Renamer", + "tooltip": "Colorbleed Rename UI" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\renameShapesToTransform.py", + "sourcetype": "file", + "tags": ["cleanup", + "renameShapesToTransform"], + "title": "Rename Shapes To Transform", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\reorderUI.py", + "sourcetype": "file", + "tags": ["cleanup", + "reorderUI"], + "title": "Reorder UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\cleanup\\pastedCleaner.py", + "sourcetype": "file", + "tags": ["cleanup", + "pastedCleaner"], + "title": "Pasted Cleaner", + "tooltip": "" + }] +}, +{ + "type": "menu", + "title": "Others", + "items": [{ + "type": "menu", + "sourcetype": "file", + "title": "Yeti", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\yeti\\cache_selected_yeti_nodes.py", + "sourcetype": "file", + "tags": ["others", + "yeti", + "cache", + "selected"], + "title": "Cache Selected Yeti Nodes", + "tooltip": "" + }] + }, + { + "type": "menu", + "title": "Hair", + "tooltip": "", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\hair\\recolorHairCurrentCurve", + "sourcetype": "file", + "tags": ["others", + "selectSoftSelection"], + "title": "Select Soft Selection", + "tooltip": "" + }] + }, + { + "type": "menu", + "command": "$COLORBLEED_SCRIPTS\\others\\display", + "sourcetype": "file", + "tags": ["others", + "display"], + "title": "Display", + "items": [{ + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\display\\wireframeSelectedObjects.py", + "sourcetype": "file", + "tags": ["others", + "wireframe", + "selected", + "objects"], + "title": "Wireframe Selected Objects", + "tooltip": "" + }] + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\archiveSceneUI.py", + "sourcetype": "file", + "tags": ["others", + "archiveSceneUI"], + "title": "Archive Scene UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\getSimilarMeshes.py", + "sourcetype": "file", + "tags": ["others", + "getSimilarMeshes"], + "title": "Get Similar Meshes", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\createBoundingBoxEachSelected.py", + "sourcetype": "file", + "tags": ["others", + "createBoundingBoxEachSelected"], + "title": "Create BoundingBox Each Selected", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\curveFromPositionEveryFrame.py", + "sourcetype": "file", + "tags": ["others", + "curveFromPositionEveryFrame"], + "title": "Curve From Position", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\instanceLeafSmartTransform.py", + "sourcetype": "file", + "tags": ["others", + "instance", + "leaf", + "smart", + "transform"], + "title": "Instance Leaf Smart Transform", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\instanceSmartTransform.py", + "sourcetype": "file", + "tags": ["others", + "instance", + "smart", + "transform"], + "title": "Instance Smart Transform", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\randomizeUVShellsSelectedObjects.py", + "sourcetype": "file", + "tags": ["others", + "randomizeUVShellsSelectedObjects"], + "title": "Randomize UV Shells", + "tooltip": "Select objects before running action" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\centerPivotGroup.py", + "sourcetype": "file", + "tags": ["others", + "centerPivotGroup"], + "title": "Center Pivot Group", + "tooltip": "" + }, + { + "type": "separator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\locatorsOnSelectedFaces.py", + "sourcetype": "file", + "tags": ["others", + "locatorsOnSelectedFaces"], + "title": "Locators On Selected Faces", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\locatorsOnEdgeSelectionPrompt.py", + "sourcetype": "file", + "tags": ["others", + "locatorsOnEdgeSelectionPrompt"], + "title": "Locators On Edge Selection Prompt", + "tooltip": "" + }, + { + "type": "separator" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\copyDeformers.py", + "sourcetype": "file", + "tags": ["others", + "copyDeformers"], + "title": "Copy Deformers", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\selectInReferenceEditor.py", + "sourcetype": "file", + "tags": ["others", + "selectInReferenceEditor"], + "title": "Select In Reference Editor", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\selectConstrainingObject.py", + "sourcetype": "file", + "tags": ["others", + "selectConstrainingObject"], + "title": "Select Constraining Object", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\deformerSetRelationsUI.py", + "sourcetype": "file", + "tags": ["others", + "deformerSetRelationsUI"], + "title": "Deformer Set Relations UI", + "tooltip": "" + }, + { + "type": "action", + "command": "$COLORBLEED_SCRIPTS\\others\\recreateBaseNodesForAllLatticeNodes.py", + "sourcetype": "file", + "tags": ["others", + "recreate", + "base", + "nodes", + "lattice"], + "title": "Recreate Base Nodes For Lattice Nodes", + "tooltip": "" + }] +}] \ No newline at end of file From 992262e09c53b86e5a75564cd7ff915d734067c3 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 12:12:41 +0200 Subject: [PATCH 113/121] added validator for pre render callbacks scripts --- .../validate_renderscript_callbacks.py | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py diff --git a/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py b/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py new file mode 100644 index 0000000000..4dc4ba171e --- /dev/null +++ b/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py @@ -0,0 +1,96 @@ +from maya import cmds + +import pyblish.api +import colorbleed.api + + +class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): + """Check if the render script callbacks will be used during the rendering + + In order to ensure the render tasks are executed properly we need to check + if the pre and post render callbacks are actually used. + + For example: + Yeti is not loaded but its callback scripts are still set in the + render settings. This will cause an error because Maya tries to find + and execute the callbacks. + + Developer note: + The pre and post render callbacks cannot be overridden + + """ + + order = colorbleed.api.ValidateContentsOrder + label = "Render Script Callbacks" + hosts = ["maya"] + families = ["colorbleed.renderlayer"] + + def process(self, instance): + + invalid = self.get_invalid(instance) + if invalid: + raise ValueError("Invalid render callbacks found for '%s'!" + % instance.name) + + @classmethod + def get_invalid(cls, instance): + + invalid = False + + # lookup per render + render_scripts = {"vray": + {"pre": "catch(`pgYetiVRayPreRender`)", + "post": "catch(`pgYetiVRayPostRender`)"}, + "arnold": + {"pre": "pgYetiPreRender"} + } + + yeti_loaded = cmds.pluginInfo("pgYetiMaya", query=True, loaded=True) + + renderer = instance.data["renderer"] + if renderer == "redshift": + cls.log.info("Redshift ignores any pre and post render callbacks") + return False + + callback_lookup = render_scripts.get(renderer, {}) + if not callback_lookup: + cls.log.error( + "Renderer '%s' is not supported in this plugin" % renderer + ) + + pre_render_callback = cmds.getAttr("defaultRenderGlobals.preMel") + post_render_callback = cmds.getAttr("defaultRenderGlobals.postMel") + + pre_script = callback_lookup.get("pre", "") + post_script = callback_lookup.get("post", "") + + # If not loaded + if not yeti_loaded: + if pre_script and pre_script in pre_render_callback: + cls.log.error( + "Found pre render callback which is not uses!" + ) + invalid = True + + if post_script and post_script in post_render_callback: + cls.log.error( + "Found post render callback which is not used!" + ) + invalid = True + else: + if pre_script: + pre_callbacks = pre_render_callback.split(";") + if pre_script not in pre_callbacks: + cls.log.error( + "Could not find required pre render callback " + "`%s`" % pre_script) + invalid = True + + if post_script: + post_callbacks = post_render_callback.split(";") + if post_script not in post_callbacks: + cls.log.error("Could not find required post render callback" + " `%s`" % post_script) + invalid = True + + return invalid From 0a1419ab47aae3b96dd67edba2f7b06431483228 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 12:17:05 +0200 Subject: [PATCH 114/121] improved validator --- .../validate_renderscript_callbacks.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py b/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py index 4dc4ba171e..7b0144de57 100644 --- a/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py +++ b/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py @@ -61,25 +61,25 @@ class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): pre_render_callback = cmds.getAttr("defaultRenderGlobals.preMel") post_render_callback = cmds.getAttr("defaultRenderGlobals.postMel") + pre_callbacks = pre_render_callback.split(";") + post_callbacks = post_render_callback.split(";") + pre_script = callback_lookup.get("pre", "") post_script = callback_lookup.get("post", "") # If not loaded if not yeti_loaded: - if pre_script and pre_script in pre_render_callback: - cls.log.error( - "Found pre render callback which is not uses!" - ) + if pre_script and pre_script in pre_callbacks: + cls.log.error("Found pre render callback '%s' which is not " + "uses!" % pre_script) invalid = True - if post_script and post_script in post_render_callback: - cls.log.error( - "Found post render callback which is not used!" - ) + if post_script and post_script in post_callbacks: + cls.log.error("Found post render callback '%s which is " + "not used!" % post_script) invalid = True else: if pre_script: - pre_callbacks = pre_render_callback.split(";") if pre_script not in pre_callbacks: cls.log.error( "Could not find required pre render callback " @@ -87,7 +87,6 @@ class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): invalid = True if post_script: - post_callbacks = post_render_callback.split(";") if post_script not in post_callbacks: cls.log.error("Could not find required post render callback" " `%s`" % post_script) From 6367af132202af6c42ea14c2c3268f023e54ae6f Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 12:25:57 +0200 Subject: [PATCH 115/121] Changed error to warning for non supported renderers --- .../maya/publish/validate_renderscript_callbacks.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py b/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py index 7b0144de57..f0f4721449 100644 --- a/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py +++ b/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py @@ -35,8 +35,6 @@ class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): @classmethod def get_invalid(cls, instance): - invalid = False - # lookup per render render_scripts = {"vray": {"pre": "catch(`pgYetiVRayPreRender`)", @@ -54,9 +52,9 @@ class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): callback_lookup = render_scripts.get(renderer, {}) if not callback_lookup: - cls.log.error( - "Renderer '%s' is not supported in this plugin" % renderer - ) + cls.log.warning("Renderer '%s' is not supported in this plugin" + % renderer) + return False pre_render_callback = cmds.getAttr("defaultRenderGlobals.preMel") post_render_callback = cmds.getAttr("defaultRenderGlobals.postMel") @@ -68,6 +66,7 @@ class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): post_script = callback_lookup.get("post", "") # If not loaded + invalid = False if not yeti_loaded: if pre_script and pre_script in pre_callbacks: cls.log.error("Found pre render callback '%s' which is not " From 2932118b6c103b6b21b3fc474998adbb9f1ea5ba Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 12:45:44 +0200 Subject: [PATCH 116/121] PEP08 --- colorbleed/plugins/maya/publish/submit_deadline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/maya/publish/submit_deadline.py b/colorbleed/plugins/maya/publish/submit_deadline.py index 45ef683757..d2b394c1d1 100644 --- a/colorbleed/plugins/maya/publish/submit_deadline.py +++ b/colorbleed/plugins/maya/publish/submit_deadline.py @@ -231,7 +231,8 @@ class MayaSubmitDeadline(pyblish.api.InstancePlugin): render_globals = instance.data.get("renderGlobals", {}) payload["JobInfo"].update(render_globals) - self.log.info("using render plugin : {}".format(payload["JobInfo"]["Plugin"])) + plugin = payload["JobInfo"]["Plugin"] + self.log.info("using render plugin : {}".format(plugin)) self.preflight_check(instance) From 5415bd943fa4519e9d2309b4498c5bea84348be5 Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 12:46:12 +0200 Subject: [PATCH 117/121] Force default pools for publish job --- colorbleed/plugins/global/publish/submit_publish_job.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/colorbleed/plugins/global/publish/submit_publish_job.py b/colorbleed/plugins/global/publish/submit_publish_job.py index 34a09c9b81..4a2daa1d40 100644 --- a/colorbleed/plugins/global/publish/submit_publish_job.py +++ b/colorbleed/plugins/global/publish/submit_publish_job.py @@ -293,6 +293,10 @@ class SubmitDependentImageSequenceJobDeadline(pyblish.api.InstancePlugin): ) for index, key in enumerate(environment) }) + # Avoid copied pools and remove secondary pool + payload["JobInfo"]["Pool"] = "none" + payload["JobInfo"].pop("SecondaryPool", None) + self.log.info("Submitting..") self.log.info(json.dumps(payload, indent=4, sort_keys=True)) From 79db80ba66504981ad8aab60aaa4e66509a5858d Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 13:02:52 +0200 Subject: [PATCH 118/121] set primitve to detail pattern at creation --- colorbleed/plugins/houdini/create/create_pointcache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 481404dcec..8fc0603bd1 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -22,7 +22,8 @@ class CreatePointCache(houdini.Creator): parms = {"use_sop_path": True, # Export single node from SOP Path "build_from_path": True, # Direct path of primitive in output - "path_attrib": "path", # Pass path attribute for output + "path_attrib": "path", # Pass path attribute for output\ + "prim_to_detail_pattern": "cbId", "format": 2, # Set format to Ogawa "filename": "$HIP/pyblish/%s.abc" % self.name} From 0caf0ff76a61ed567a4cb265d33a9cde2979caca Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 15:17:53 +0200 Subject: [PATCH 119/121] added *args to function signatures --- colorbleed/houdini/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/colorbleed/houdini/__init__.py b/colorbleed/houdini/__init__.py index 4025ef731a..4d407af609 100644 --- a/colorbleed/houdini/__init__.py +++ b/colorbleed/houdini/__init__.py @@ -44,11 +44,11 @@ def install(): avalon.data["familiesStateToggled"] = ["colorbleed.imagesequence"] -def on_init(_): +def on_init(*args): houdini.on_houdini_initialize() -def on_save(): +def on_save(*args): avalon.logger.info("Running callback on save..") @@ -59,7 +59,9 @@ def on_save(): lib.set_id(node, new_id, overwrite=False) -def on_open(): +def on_open(*args): + + avalon.logger.info("Running callback on open..") update_task_from_path(hou.hipFile.path()) From 3ee17b8c119c37fffe07a79a37ef4c2ae167d86f Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 15:29:02 +0200 Subject: [PATCH 120/121] Lock prim_to_detail_pattern parameter after creation --- colorbleed/plugins/houdini/create/create_pointcache.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/colorbleed/plugins/houdini/create/create_pointcache.py b/colorbleed/plugins/houdini/create/create_pointcache.py index 8fc0603bd1..c54a6c91a6 100644 --- a/colorbleed/plugins/houdini/create/create_pointcache.py +++ b/colorbleed/plugins/houdini/create/create_pointcache.py @@ -32,3 +32,9 @@ class CreatePointCache(houdini.Creator): parms.update({"sop_path": node.path()}) instance.setParms(parms) + + # Lock any parameters in this list + to_lock = ["prim_to_detail_pattern"] + for name in to_lock: + parm = instance.parm(name) + parm.lock(True) From b55a5bd89ebdbc172c793dc10803c45df81cffbc Mon Sep 17 00:00:00 2001 From: wikoreman Date: Thu, 11 Oct 2018 15:59:01 +0200 Subject: [PATCH 121/121] Renamed module, class and label for clearification --- ...t_callbacks.py => validate_yeti_renderscript_callbacks.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename colorbleed/plugins/maya/publish/{validate_renderscript_callbacks.py => validate_yeti_renderscript_callbacks.py} (96%) diff --git a/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py b/colorbleed/plugins/maya/publish/validate_yeti_renderscript_callbacks.py similarity index 96% rename from colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py rename to colorbleed/plugins/maya/publish/validate_yeti_renderscript_callbacks.py index f0f4721449..ad4d1db911 100644 --- a/colorbleed/plugins/maya/publish/validate_renderscript_callbacks.py +++ b/colorbleed/plugins/maya/publish/validate_yeti_renderscript_callbacks.py @@ -4,7 +4,7 @@ import pyblish.api import colorbleed.api -class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): +class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin): """Check if the render script callbacks will be used during the rendering In order to ensure the render tasks are executed properly we need to check @@ -21,7 +21,7 @@ class ValidateRenderScriptCallbacks(pyblish.api.InstancePlugin): """ order = colorbleed.api.ValidateContentsOrder - label = "Render Script Callbacks" + label = "Yeti Render Script Callbacks" hosts = ["maya"] families = ["colorbleed.renderlayer"]