From e19e7e1f23ad9c0206305ecb4853cc9d7586be3f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Jun 2023 17:10:55 +0100 Subject: [PATCH 001/163] initial working version of abc options. --- openpype/hosts/maya/api/lib.py | 136 ++++++-- .../maya/plugins/create/create_animation.py | 87 ++++-- .../maya/plugins/create/create_pointcache.py | 76 +++-- .../plugins/publish/extract_pointcache.py | 43 ++- .../defaults/project_settings/maya.json | 66 +++- .../schemas/schema_maya_create.json | 292 ++++++++++++++++-- 6 files changed, 576 insertions(+), 124 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index b02d3c9b39d..fb65bc1072d 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -98,6 +98,7 @@ "renderableOnly": bool, "step": float, "stripNamespaces": bool, + "verbose": bool, "uvWrite": bool, "wholeFrameGeo": bool, "worldSpace": bool, @@ -115,7 +116,9 @@ "melPostJobCallback": str, "pythonPerFrameCallback": str, "pythonPostJobCallback": str, - "selection": bool + "selection": bool, + "preRoll": bool, + "preRollStartFrame": int } INT_FPS = {15, 24, 25, 30, 48, 50, 60, 44100, 48000} @@ -1083,12 +1086,28 @@ def is_visible(node, def extract_alembic(file, startFrame=None, endFrame=None, + frameRange="", + eulerFilter=True, + noNormals=False, + preRoll=False, + renderableOnly=False, selection=True, uvWrite=True, - eulerFilter=True, + writeColorSets=False, + writeFaceSets=False, + wholeFrameGeo=False, + worldSpace=False, + writeVisibility=False, + writeUVSets=False, + writeCreases=False, dataFormat="ogawa", + step=1.0, + attr=[], + attrPrefix=[], + root=[], + stripNamespaces=True, verbose=False, - **kwargs): + preRollStartFrame=0): """Extract a single Alembic Cache. This extracts an Alembic cache using the `-selection` flag to minimize @@ -1106,37 +1125,87 @@ def extract_alembic(file, string formatted as: "startFrame endFrame". This argument overrides `startFrame` and `endFrame` arguments. - dataFormat (str): The data format to use for the cache, - defaults to "ogawa" - - verbose (bool): When on, outputs frame number information to the - Script Editor or output window during extraction. + eulerFilter (bool): When on, X, Y, and Z rotation data is filtered with + an Euler filter. Euler filtering helps resolve irregularities in + rotations especially if X, Y, and Z rotations exceed 360 degrees. + Defaults to True. noNormals (bool): When on, normal data from the original polygon objects is not included in the exported Alembic cache file. + preRoll (bool): This frame range will not be sampled. + Defaults to False. + renderableOnly (bool): When on, any non-renderable nodes or hierarchy, such as hidden objects, are not included in the Alembic file. Defaults to False. - stripNamespaces (bool): When on, any namespaces associated with the - exported objects are removed from the Alembic file. For example, an - object with the namespace taco:foo:bar appears as bar in the - Alembic file. + selection (bool): Write out all all selected nodes from the + active selection list that are descendents of the roots specified + with -root. Defaults to False. uvWrite (bool): When on, UV data from polygon meshes and subdivision objects are written to the Alembic file. Only the current UV map is included. + writeColorSets (bool): Write all color sets on MFnMeshes as + color 3 or color 4 indexed geometry parameters with face varying + scope. Defaults to False. + + writeFaceSets (bool): Write all Face sets on MFnMeshes. + Defaults to False. + + wholeFrameGeo (bool): Data for geometry will only be written + out on whole frames. Defaults to False. + worldSpace (bool): When on, the top node in the node hierarchy is stored as world space. By default, these nodes are stored as local space. Defaults to False. - eulerFilter (bool): When on, X, Y, and Z rotation data is filtered with - an Euler filter. Euler filtering helps resolve irregularities in - rotations especially if X, Y, and Z rotations exceed 360 degrees. - Defaults to True. + writeVisibility (bool): Visibility state will be stored in + the Alembic file. Otherwise everything written out is treated as + visible. Defaults to False. + + writeUVSets (bool): Write all uv sets on MFnMeshes as vector + 2 indexed geometry parameters with face varying scope. Defaults to + False. + + writeCreases (bool): If the mesh has crease edges or crease + vertices, the mesh (OPolyMesh) would now be written out as an OSubD + and crease info will be stored in the Alembic file. Otherwise, + creases info won't be preserved in Alembic file unless a custom + Boolean attribute SubDivisionMesh has been added to mesh node and + its value is true. Defaults to False. + + dataFormat (str): The data format to use for the cache, + defaults to "ogawa" + + step (float): The time interval (expressed in frames) at + which the frame range is sampled. Additional samples around each + frame can be specified with -frs. Defaults to 1.0. + + attr (list of str, optional): A specific geometric attribute to write + out. Defaults to []. + + attrPrefix (list of str, optional): Prefix filter for determining which + geometric attributes to write out. Defaults to ["ABC_"]. + + root (list of str): Maya dag path which will be parented to + the root of the Alembic file. Defaults to [], which means the + entire scene will be written out. + stripNamespaces (bool): When on, any namespaces associated with the + exported objects are removed from the Alembic file. For example, an + object with the namespace taco:foo:bar appears as bar in the + Alembic file. + + verbose (bool): When on, outputs frame number information to the + Script Editor or output window during extraction. + + preRollStartFrame (float): The frame to start scene + evaluation at. This is used to set the starting frame for time + dependent translations and can be used to evaluate run-up that + isn't actually translated. Defaults to 0. """ # Ensure alembic exporter is loaded @@ -1147,7 +1216,7 @@ def extract_alembic(file, # Pass the start and end frame on as `frameRange` so that it # never conflicts with that argument - if "frameRange" not in kwargs: + if not frameRange: # Fallback to maya timeline if no start or end frame provided. if startFrame is None: startFrame = cmds.playbackOptions(query=True, @@ -1159,23 +1228,38 @@ def extract_alembic(file, # Ensure valid types are converted to frame range assert isinstance(startFrame, _alembic_options["startFrame"]) assert isinstance(endFrame, _alembic_options["endFrame"]) - kwargs["frameRange"] = "{0} {1}".format(startFrame, endFrame) + frameRange = "{0} {1}".format(startFrame, endFrame) else: # Allow conversion from tuple for `frameRange` - frame_range = kwargs["frameRange"] - if isinstance(frame_range, (list, tuple)): - assert len(frame_range) == 2 - kwargs["frameRange"] = "{0} {1}".format(frame_range[0], - frame_range[1]) + if isinstance(frameRange, (list, tuple)): + assert len(frameRange) == 2 + frameRange = "{0} {1}".format(frameRange[0], frameRange[1]) # Assemble options options = { "selection": selection, - "uvWrite": uvWrite, + "frameRange": frameRange, "eulerFilter": eulerFilter, - "dataFormat": dataFormat + "noNormals": noNormals, + "preRoll": preRoll, + "renderableOnly": renderableOnly, + "selection": selection, + "uvWrite": uvWrite, + "writeColorSets": writeColorSets, + "writeFaceSets": writeFaceSets, + "wholeFrameGeo": wholeFrameGeo, + "worldSpace": worldSpace, + "writeVisibility": writeVisibility, + "writeUVSets": writeUVSets, + "writeCreases": writeCreases, + "dataFormat": dataFormat, + "step": step, + "attr": attr, + "attrPrefix": attrPrefix, + "stripNamespaces": stripNamespaces, + "verbose": verbose, + "preRollStartFrame": preRollStartFrame } - options.update(kwargs) # Validate options for key, value in options.copy().items(): diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py index 095cbcdd647..92f25566801 100644 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ b/openpype/hosts/maya/plugins/create/create_animation.py @@ -17,44 +17,65 @@ class CreateAnimation(plugin.Creator): label = "Animation" family = "animation" icon = "male" - write_color_sets = False - write_face_sets = False - include_parent_hierarchy = False - include_user_defined_attributes = False + includeUserDefinedAttributes = False + eulerFilter = True + noNormals = False + preRoll = False + renderableOnly = False + uvWrite = True + writeColorSets = False + writeFaceSets = False + wholeFrameGeo = False + worldSpace = True + writeVisibility = True + writeUVSets = True + writeCreases = False + dataFormat = "ogawa" + step = 1.0 + attr = "" + attrPrefix = "" + stripNamespaces = True + verbose = False + preRollStartFrame = 0 + farm = False + priority = 50 + includeParentHierarchy = False # Include parent groups + refresh = False # Default to suspend refresh. + visibleOnly = False # only nodes that are visible def __init__(self, *args, **kwargs): super(CreateAnimation, self).__init__(*args, **kwargs) - # create an ordered dict with the existing data first - # get basic animation data : start / end / handles / steps for key, value in lib.collect_animation_data().items(): self.data[key] = value - # Write vertex colors with the geometry. - self.data["writeColorSets"] = self.write_color_sets - self.data["writeFaceSets"] = self.write_face_sets - - # Include only renderable visible shapes. - # Skips locators and empty transforms - self.data["renderableOnly"] = False - - # Include only nodes that are visible at least once during the - # frame range. - self.data["visibleOnly"] = False - - # Include the groups above the out_SET content - self.data["includeParentHierarchy"] = self.include_parent_hierarchy - - # Default to exporting world-space - self.data["worldSpace"] = True - - # Default to not send to farm. - self.data["farm"] = False - self.data["priority"] = 50 - - # Default to write normals. - self.data["writeNormals"] = True - - value = self.include_user_defined_attributes - self.data["includeUserDefinedAttributes"] = value + attrs = [ + "includeUserDefinedAttributes", + "eulerFilter", + "noNormals", + "preRoll", + "renderableOnly", + "uvWrite", + "writeColorSets", + "writeFaceSets", + "wholeFrameGeo", + "worldSpace", + "writeVisibility", + "writeUVSets", + "writeCreases", + "dataFormat", + "step", + "attr", + "attrPrefix", + "stripNamespaces", + "verbose", + "preRollStartFrame", + "farm", + "priority", + "includeParentHierarchy", + "refresh", + "visibleOnly" + ] + for attr in attrs: + self.data[attr] = getattr(self, attr) diff --git a/openpype/hosts/maya/plugins/create/create_pointcache.py b/openpype/hosts/maya/plugins/create/create_pointcache.py index 1b8d5e68500..c1e6056d284 100644 --- a/openpype/hosts/maya/plugins/create/create_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_pointcache.py @@ -13,9 +13,31 @@ class CreatePointCache(plugin.Creator): label = "Point Cache" family = "pointcache" icon = "gears" - write_color_sets = False - write_face_sets = False - include_user_defined_attributes = False + includeUserDefinedAttributes = False + eulerFilter = True + noNormals = False + preRoll = False + renderableOnly = False + uvWrite = True + writeColorSets = False + writeFaceSets = False + wholeFrameGeo = False + worldSpace = True + writeVisibility = True + writeUVSets = True + writeCreases = False + dataFormat = "ogawa" + step = 1.0 + attr = "" + attrPrefix = "" + stripNamespaces = True + verbose = False + preRollStartFrame = 0 + farm = False + priority = 50 + includeParentHierarchy = False # Include parent groups + refresh = False # Default to suspend refresh. + visibleOnly = False # only nodes that are visible def __init__(self, *args, **kwargs): super(CreatePointCache, self).__init__(*args, **kwargs) @@ -23,25 +45,35 @@ def __init__(self, *args, **kwargs): # Add animation data self.data.update(lib.collect_animation_data()) - # Vertex colors with the geometry. - self.data["writeColorSets"] = self.write_color_sets - # Vertex colors with the geometry. - self.data["writeFaceSets"] = self.write_face_sets - self.data["renderableOnly"] = False # Only renderable visible shapes - self.data["visibleOnly"] = False # only nodes that are visible - self.data["includeParentHierarchy"] = False # Include parent groups - self.data["worldSpace"] = True # Default to exporting world-space - self.data["refresh"] = False # Default to suspend refresh. - - # Add options for custom attributes - value = self.include_user_defined_attributes - self.data["includeUserDefinedAttributes"] = value - self.data["attr"] = "" - self.data["attrPrefix"] = "" - - # Default to not send to farm. - self.data["farm"] = False - self.data["priority"] = 50 + attrs = [ + "includeUserDefinedAttributes", + "eulerFilter", + "noNormals", + "preRoll", + "renderableOnly", + "uvWrite", + "writeColorSets", + "writeFaceSets", + "wholeFrameGeo", + "worldSpace", + "writeVisibility", + "writeUVSets", + "writeCreases", + "dataFormat", + "step", + "attr", + "attrPrefix", + "stripNamespaces", + "verbose", + "preRollStartFrame", + "farm", + "priority", + "includeParentHierarchy", + "refresh", + "visibleOnly" + ] + for attr in attrs: + self.data[attr] = getattr(self, attr) def process(self): instance = super(CreatePointCache, self).process() diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index f44c13767cf..10817507492 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -52,19 +52,29 @@ def process(self, instance): filename = "{name}.abc".format(**instance.data) path = os.path.join(parent_dir, filename) - options = { - "step": instance.data.get("step", 1.0), - "attr": attrs, - "attrPrefix": attr_prefixes, - "writeVisibility": True, - "writeCreases": True, - "writeColorSets": instance.data.get("writeColorSets", False), - "writeFaceSets": instance.data.get("writeFaceSets", False), - "uvWrite": True, - "selection": True, - "worldSpace": instance.data.get("worldSpace", True) - } - + options = {"selection": True} + option_keys = [ + "eulerFilter", + "noNormals", + "preRoll", + "renderableOnly", + "uvWrite", + "writeColorSets", + "writeFaceSets", + "wholeFrameGeo", + "worldSpace", + "writeVisibility", + "writeUVSets", + "writeCreases", + "dataFormat", + "step", + "stripNamespaces", + "verbose", + "preRollStartFrame" + ] + for key in option_keys: + options[key] = instance.data[key] + self.log.info(options) if not instance.data.get("includeParentHierarchy", True): # Set the root nodes if we don't want to include parents # The roots are to be considered the ones that are the actual @@ -81,12 +91,13 @@ def process(self, instance): # flag does not filter out those that are only hidden on some # frames as it counts "animated" or "connected" visibilities as # if it's always visible. - nodes = list(iter_visible_nodes_in_range(nodes, - start=start, - end=end)) + nodes = list( + iter_visible_nodes_in_range(nodes, start=start, end=end) + ) suspend = not instance.data.get("refresh", False) self.log.info(nodes) + self.log.info(options) with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(nodes, noExpand=True) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 19c3da13e6c..7072e2d1a43 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -556,13 +556,38 @@ }, "CreateAnimation": { "enabled": false, + "includeUserDefinedAttributes": false, + "eulerFilter": true, + "noNormals": false, + "preRoll": false, + "renderableOnly": false, + "uvWrite": true, + "WriteColorSets": false, + "writeFaceSets": false, + "wholeFrameGeo": false, + "worldSpace": true, + "writeVisibility": true, + "writeUVSets": true, + "writeCreases": false, + "dataFormat": "ogawa", + "step": 1.0, + "attr": "", + "attrPrefix": "", + "stripNamespaces": true, + "verbose": false, + "preRollStartFrame": 0, + "farm": false, + "priority": 50, + "includeParentHierarchy": false, + "refresh": false, + "visibleOnly": false, + "defaults": [ + "Main" + ], "write_color_sets": false, "write_face_sets": false, "include_parent_hierarchy": false, - "include_user_defined_attributes": false, - "defaults": [ - "Main" - ] + "include_user_defined_attributes": false }, "CreateModel": { "enabled": true, @@ -576,12 +601,37 @@ }, "CreatePointCache": { "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "include_user_defined_attributes": false, + "includeUserDefinedAttributes": false, + "eulerFilter": true, + "noNormals": false, + "preRoll": false, + "renderableOnly": false, + "uvWrite": true, + "WriteColorSets": false, + "writeFaceSets": false, + "wholeFrameGeo": false, + "worldSpace": true, + "writeVisibility": true, + "writeUVSets": true, + "writeCreases": false, + "dataFormat": "ogawa", + "step": 1.0, + "attr": "", + "attrPrefix": "", + "stripNamespaces": true, + "verbose": false, + "preRollStartFrame": 0, + "farm": false, + "priority": 50, + "includeParentHierarchy": false, + "refresh": false, + "visibleOnly": false, "defaults": [ "Main" - ] + ], + "write_color_sets": false, + "write_face_sets": false, + "include_user_defined_attributes": false }, "CreateProxyAlembic": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index a8b76a03318..aeff809cf66 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -127,31 +127,158 @@ "key": "enabled", "label": "Enabled" }, + { + "type": "boolean", + "key": "includeUserDefinedAttributes", + "label": "Include User Defined Attributes" + }, + { + "type": "boolean", + "key": "eulerFilter", + "label": "Euler Filter" + }, + { + "type": "boolean", + "key": "noNormals", + "label": "No Normals" + }, + { + "type": "boolean", + "key": "preRoll", + "label": "Pre Roll" + }, + { + "type": "boolean", + "key": "renderableOnly", + "label": "Renderable Only" + }, + { + "type": "boolean", + "key": "uvWrite", + "label": "UV Write" + }, + { + "type": "boolean", + "key": "WriteColorSets", + "label": "Write Color Sets" + }, + { + "type": "boolean", + "key": "writeFaceSets", + "label": "Write Face Sets" + }, + { + "type": "boolean", + "key": "wholeFrameGeo", + "label": "Whole Frame Geo" + }, + { + "type": "boolean", + "key": "worldSpace", + "label": "World Space" + }, + { + "type": "boolean", + "key": "writeVisibility", + "label": "Write Visibility" + }, + { + "type": "boolean", + "key": "writeUVSets", + "label": "Write UV Sets" + }, + { + "type": "boolean", + "key": "writeCreases", + "label": "Write Creases" + }, + { + "type": "text", + "key": "dataFormat", + "label": "Data Format" + }, + { + "type": "number", + "key": "step", + "label": "Step", + "minimum": 0.0, + "decimal": 4 + }, + { + "type": "text", + "key": "attr", + "label": "Attr" + }, + { + "type": "text", + "key": "attrPrefix", + "label": "Attr Prefix" + }, + { + "type": "boolean", + "key": "stripNamespaces", + "label": "StripNamespaces" + }, + { + "type": "boolean", + "key": "verbose", + "label": "Verbose" + }, + { + "type": "number", + "key": "preRollStartFrame", + "label": "Pre Roll Start Frame" + }, + { + "type": "boolean", + "key": "farm", + "label": "Farm" + }, + { + "type": "number", + "key": "priority", + "label": "Priority" + }, + { + "type": "boolean", + "key": "includeParentHierarchy", + "label": "Include Parent Hierarchy" + }, + { + "type": "boolean", + "key": "refresh", + "label": "Refresh" + }, + { + "type": "boolean", + "key": "visibleOnly", + "label": "Visible Only" + }, + { + "type": "list", + "key": "defaults", + "label": "Default Subsets", + "object_type": "text" + }, { "type": "boolean", "key": "write_color_sets", - "label": "Write Color Sets" + "label": "DEPRECATED! Write Color Sets" }, { "type": "boolean", "key": "write_face_sets", - "label": "Write Face Sets" + "label": "DEPRECATED! Write Face Sets" }, { "type": "boolean", "key": "include_parent_hierarchy", - "label": "Include Parent Hierarchy" + "label": "DEPRECATED! Include Parent Hierarchy" }, { "type": "boolean", "key": "include_user_defined_attributes", - "label": "Include User Defined Attributes" - }, - { - "type": "list", - "key": "defaults", - "label": "Default Subsets", - "object_type": "text" + "label": "DEPRECATED! Include User Defined Attributes" } ] }, @@ -198,25 +325,152 @@ "label": "Enabled" }, { - "type": "boolean", - "key": "write_color_sets", - "label": "Write Color Sets" + "type": "boolean", + "key": "includeUserDefinedAttributes", + "label": "Include User Defined Attributes" }, { - "type": "boolean", - "key": "write_face_sets", - "label": "Write Face Sets" + "type": "boolean", + "key": "eulerFilter", + "label": "Euler Filter" }, { - "type": "boolean", - "key": "include_user_defined_attributes", - "label": "Include User Defined Attributes" + "type": "boolean", + "key": "noNormals", + "label": "No Normals" + }, + { + "type": "boolean", + "key": "preRoll", + "label": "Pre Roll" + }, + { + "type": "boolean", + "key": "renderableOnly", + "label": "Renderable Only" + }, + { + "type": "boolean", + "key": "uvWrite", + "label": "UV Write" + }, + { + "type": "boolean", + "key": "WriteColorSets", + "label": "Write Color Sets" + }, + { + "type": "boolean", + "key": "writeFaceSets", + "label": "Write Face Sets" + }, + { + "type": "boolean", + "key": "wholeFrameGeo", + "label": "Whole Frame Geo" + }, + { + "type": "boolean", + "key": "worldSpace", + "label": "World Space" + }, + { + "type": "boolean", + "key": "writeVisibility", + "label": "Write Visibility" + }, + { + "type": "boolean", + "key": "writeUVSets", + "label": "Write UV Sets" + }, + { + "type": "boolean", + "key": "writeCreases", + "label": "Write Creases" + }, + { + "type": "text", + "key": "dataFormat", + "label": "Data Format" + }, + { + "type": "number", + "key": "step", + "label": "Step", + "minimum": 0.0, + "decimal": 4 + }, + { + "type": "text", + "key": "attr", + "label": "Attr" + }, + { + "type": "text", + "key": "attrPrefix", + "label": "Attr Prefix" + }, + { + "type": "boolean", + "key": "stripNamespaces", + "label": "StripNamespaces" + }, + { + "type": "boolean", + "key": "verbose", + "label": "Verbose" + }, + { + "type": "number", + "key": "preRollStartFrame", + "label": "Pre Roll Start Frame" + }, + { + "type": "boolean", + "key": "farm", + "label": "Farm" + }, + { + "type": "number", + "key": "priority", + "label": "Priority" + }, + { + "type": "boolean", + "key": "includeParentHierarchy", + "label": "Include Parent Hierarchy" + }, + { + "type": "boolean", + "key": "refresh", + "label": "Refresh" + }, + { + "type": "boolean", + "key": "visibleOnly", + "label": "Visible Only" }, { "type": "list", "key": "defaults", "label": "Default Subsets", "object_type": "text" + }, + { + "type": "boolean", + "key": "write_color_sets", + "label": "DEPRECATED! Write Color Sets" + }, + { + "type": "boolean", + "key": "write_face_sets", + "label": "DEPRECATED! Write Face Sets" + }, + { + "type": "boolean", + "key": "include_user_defined_attributes", + "label": "DEPRECATED! Include User Defined Attributes" } ] }, From d87e64e4ea22b8ba16afb549e8b4f222ddc45c95 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Jun 2023 08:40:03 +0100 Subject: [PATCH 002/163] Docs --- website/docs/artist_hosts_maya.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/website/docs/artist_hosts_maya.md b/website/docs/artist_hosts_maya.md index e36ccb77d22..bc7b7199900 100644 --- a/website/docs/artist_hosts_maya.md +++ b/website/docs/artist_hosts_maya.md @@ -347,6 +347,24 @@ Example setup: - **Include User Defined Attribudes**: include all user defined attributes in the publish. - **Farm**: if your studio has Deadline configured, artists could choose to offload potentially long running export of pointache and publish it to the farm. Only thing that is necessary is to toggle this attribute in created pointcache instance to True. - **Priority**: Farm priority. +- **Euler Filter**: Apply Euler filter while sampling rotations. +- **No Normals**: Present normal data for Alembic poly meshes will not be written. +- **Pre Roll**: This frame range will not be sampled. +- **Renderable Only**: Non-renderable hierarchy (invisible, or templated) will not be written out. +- **UV Write**: Uv data for PolyMesh and SubD shapes will be written to the Alembic file. Only the current uv map is used. +- **Write Color Sets**: Write all color sets on MFnMeshes as color 3 or color 4 indexed geometry parameters with face varying scope. +- **Write Face Sets**: Write all Face sets on MFnMeshes. +- **Whole Frame Geo**: Data for geometry will only be written out on whole frames. +- **World Space**: Any root nodes will be stored in world space. +- **Write Visibility**: Visibility state will be stored in the Alembic file. Otherwise everything written out is treated as visible. +- **Write UV Sets**: Write all uv sets on MFnMeshes as vector 2 indexed geometry parameters with face varying scope. +- **Write Creases**: If the mesh has crease edges or crease vertices, the mesh (OPolyMesh) would now be written out as an OSubD and crease info will be stored in the Alembic file. Otherwise, creases info won't be preserved in Alembic file unless a custom Boolean attribute SubDivisionMesh has been added to mesh node and its value is true. +- **Data Format**: The data format to use to write the file. Can be either "HDF" or "Ogawa". +- **Strip Namespaces**: Namespaces will be stripped off of the node before being written to Alembic. The int specifies how many namespaces will be stripped off of the node name. Be careful that the new stripped name does not collide with other sibling node names. +- **Verbose**: Prints the current frame that is being evaluated. +- **Pre Roll Start Frame**: The frame to start scene evaluation at. This is used to set the starting frame for time dependent translations and can be used to evaluate run-up that isn't actually translated. +- **Include Parent Hierarchy**: Set the root nodes if we don't want to include parents. The roots are to be considered the ones that are the actual direct members of the set. +- **Visible Only**: Does not filter out nodes that are only hidden on some frames as it counts "animated" or "connected" visibilities as if it's always visible. ### Loading Point Caches From c07729dc9a2a4ccf8374e91e9e866678c7516017 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Jun 2023 08:57:01 +0100 Subject: [PATCH 003/163] Hound --- openpype/hosts/maya/api/lib.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index fb65bc1072d..a34318f4e98 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -3,7 +3,6 @@ import os from pprint import pformat import sys -import platform import uuid import re @@ -1102,9 +1101,9 @@ def extract_alembic(file, writeCreases=False, dataFormat="ogawa", step=1.0, - attr=[], - attrPrefix=[], - root=[], + attr=None, + attrPrefix=None, + root=None, stripNamespaces=True, verbose=False, preRollStartFrame=0): From e4a76418557b679c74f6755791afae0023d0fe04 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Jun 2023 10:51:10 +0100 Subject: [PATCH 004/163] Backwards compatibility attributes. --- .../maya/plugins/publish/collect_animation.py | 38 ++++++++++++------- .../plugins/publish/collect_pointcache.py | 29 +++++++++----- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index 8f523f770be..74efe6af9bf 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -59,16 +59,28 @@ def process(self, instance): instance.data["families"].append("publish.farm") # Collect user defined attributes. - if not instance.data.get("includeUserDefinedAttributes", False): - return - - user_defined_attributes = set() - for node in hierarchy: - attrs = cmds.listAttr(node, userDefined=True) or list() - shapes = cmds.listRelatives(node, shapes=True) or list() - for shape in shapes: - attrs.extend(cmds.listAttr(shape, userDefined=True) or list()) - - user_defined_attributes.update(attrs) - - instance.data["userDefinedAttributes"] = list(user_defined_attributes) + if instance.data.get("includeUserDefinedAttributes", False): + user_defined_attributes = set() + for node in hierarchy: + attrs = cmds.listAttr(node, userDefined=True) or list() + shapes = cmds.listRelatives(node, shapes=True) or list() + for shape in shapes: + attrs.extend( + cmds.listAttr(shape, userDefined=True) or list() + ) + + user_defined_attributes.update(attrs) + + instance.data["userDefinedAttributes"] = list( + user_defined_attributes + ) + + # Backwards compatibility for attributes. + backwards_mapping = { + "write_color_sets": "writeColorSets", + "write_face_sets": "writeFaceSets", + "include_parent_hierarchy": "includeParentHierarchy", + "include_user_defined_attributes": "includeUserDefinedAttributes" + } + for key, value in backwards_mapping.items(): + instance.data[value] = instance.data[key] diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index d0430c56121..eba6e510e7a 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -45,15 +45,26 @@ def process(self, instance): # Collect user defined attributes. if not instance.data.get("includeUserDefinedAttributes", False): - return + user_defined_attributes = set() + for node in instance: + attrs = cmds.listAttr(node, userDefined=True) or list() + shapes = cmds.listRelatives(node, shapes=True) or list() + for shape in shapes: + attrs.extend( + cmds.listAttr(shape, userDefined=True) or list() + ) - user_defined_attributes = set() - for node in instance: - attrs = cmds.listAttr(node, userDefined=True) or list() - shapes = cmds.listRelatives(node, shapes=True) or list() - for shape in shapes: - attrs.extend(cmds.listAttr(shape, userDefined=True) or list()) + user_defined_attributes.update(attrs) - user_defined_attributes.update(attrs) + instance.data["userDefinedAttributes"] = list( + user_defined_attributes + ) - instance.data["userDefinedAttributes"] = list(user_defined_attributes) + # Backwards compatibility for attributes. + backwards_mapping = { + "write_color_sets": "writeColorSets", + "write_face_sets": "writeFaceSets", + "include_user_defined_attributes": "includeUserDefinedAttributes" + } + for key, value in backwards_mapping.items(): + instance.data[value] = instance.data[key] From cda90897b697f23b10df09244ca854f7eafe9cb3 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Jun 2023 10:51:26 +0100 Subject: [PATCH 005/163] Debug logging --- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 10817507492..1968cad976b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -96,8 +96,7 @@ def process(self, instance): ) suspend = not instance.data.get("refresh", False) - self.log.info(nodes) - self.log.info(options) + self.log.debug(nodes) with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(nodes, noExpand=True) From 1a5201af40b2c75ebad89a687e61aa9d1827dc08 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Jun 2023 10:51:37 +0100 Subject: [PATCH 006/163] Remove redundant code. --- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 1968cad976b..afbbc322908 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -81,10 +81,6 @@ def process(self, instance): # direct members of the set options["root"] = roots - if int(cmds.about(version=True)) >= 2017: - # Since Maya 2017 alembic supports multiple uv sets - write them. - options["writeUVSets"] = True - if instance.data.get("visibleOnly", False): # If we only want to include nodes that are visible in the frame # range then we need to do our own check. Alembic's `visibleOnly` From 0342870cdb66374aa55a9a8fcde7662b3385df49 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Jun 2023 11:12:32 +0100 Subject: [PATCH 007/163] Failsafe --- openpype/hosts/maya/plugins/publish/collect_animation.py | 3 ++- openpype/hosts/maya/plugins/publish/collect_pointcache.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index 74efe6af9bf..af852b031b7 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -83,4 +83,5 @@ def process(self, instance): "include_user_defined_attributes": "includeUserDefinedAttributes" } for key, value in backwards_mapping.items(): - instance.data[value] = instance.data[key] + if key in instance.data: + instance.data[value] = instance.data[key] diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index eba6e510e7a..537e6d44c51 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -67,4 +67,5 @@ def process(self, instance): "include_user_defined_attributes": "includeUserDefinedAttributes" } for key, value in backwards_mapping.items(): - instance.data[value] = instance.data[key] + if key in instance.data: + instance.data[value] = instance.data[key] From fab76c619a3409043a68a6cdcdf40d92095ae35e Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Jun 2023 11:54:53 +0100 Subject: [PATCH 008/163] Ensure includeUserDefinedAttributes gets correct value --- .../maya/plugins/publish/collect_animation.py | 22 +++++++++---------- .../plugins/publish/collect_pointcache.py | 22 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index af852b031b7..5cfdb358cef 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -58,6 +58,17 @@ def process(self, instance): if instance.data.get("farm"): instance.data["families"].append("publish.farm") + # Backwards compatibility for attributes. + backwards_mapping = { + "write_color_sets": "writeColorSets", + "write_face_sets": "writeFaceSets", + "include_parent_hierarchy": "includeParentHierarchy", + "include_user_defined_attributes": "includeUserDefinedAttributes" + } + for key, value in backwards_mapping.items(): + if key in instance.data: + instance.data[value] = instance.data[key] + # Collect user defined attributes. if instance.data.get("includeUserDefinedAttributes", False): user_defined_attributes = set() @@ -74,14 +85,3 @@ def process(self, instance): instance.data["userDefinedAttributes"] = list( user_defined_attributes ) - - # Backwards compatibility for attributes. - backwards_mapping = { - "write_color_sets": "writeColorSets", - "write_face_sets": "writeFaceSets", - "include_parent_hierarchy": "includeParentHierarchy", - "include_user_defined_attributes": "includeUserDefinedAttributes" - } - for key, value in backwards_mapping.items(): - if key in instance.data: - instance.data[value] = instance.data[key] diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 537e6d44c51..4f3ae833fd4 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -43,8 +43,18 @@ def process(self, instance): instance.remove(proxy_set) instance.data["setMembers"].remove(proxy_set) + # Backwards compatibility for attributes. + backwards_mapping = { + "write_color_sets": "writeColorSets", + "write_face_sets": "writeFaceSets", + "include_user_defined_attributes": "includeUserDefinedAttributes" + } + for key, value in backwards_mapping.items(): + if key in instance.data: + instance.data[value] = instance.data[key] + # Collect user defined attributes. - if not instance.data.get("includeUserDefinedAttributes", False): + if instance.data.get("includeUserDefinedAttributes", False): user_defined_attributes = set() for node in instance: attrs = cmds.listAttr(node, userDefined=True) or list() @@ -59,13 +69,3 @@ def process(self, instance): instance.data["userDefinedAttributes"] = list( user_defined_attributes ) - - # Backwards compatibility for attributes. - backwards_mapping = { - "write_color_sets": "writeColorSets", - "write_face_sets": "writeFaceSets", - "include_user_defined_attributes": "includeUserDefinedAttributes" - } - for key, value in backwards_mapping.items(): - if key in instance.data: - instance.data[value] = instance.data[key] From 99233285451378a8aea73965659527476adce65e Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Fri, 23 Jun 2023 15:09:28 +0100 Subject: [PATCH 009/163] Update openpype/hosts/maya/plugins/publish/collect_pointcache.py Co-authored-by: Roy Nieterau --- openpype/hosts/maya/plugins/publish/collect_pointcache.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 4f3ae833fd4..5946d62f9e0 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -51,7 +51,10 @@ def process(self, instance): } for key, value in backwards_mapping.items(): if key in instance.data: - instance.data[value] = instance.data[key] + self.log.debug( + "Using legacy attribute name '{}' since it exists.".format(key) + ) + instance.data[value] = instance.data.pop(key) # Collect user defined attributes. if instance.data.get("includeUserDefinedAttributes", False): From b601aa6160a085280834ac12b96898d9bae17264 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 23 Jun 2023 15:10:47 +0100 Subject: [PATCH 010/163] Hound --- openpype/hosts/maya/plugins/publish/collect_pointcache.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 5946d62f9e0..06be1be8257 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -52,7 +52,9 @@ def process(self, instance): for key, value in backwards_mapping.items(): if key in instance.data: self.log.debug( - "Using legacy attribute name '{}' since it exists.".format(key) + "Using legacy attribute name '{}' since it exists.".format( + key + ) ) instance.data[value] = instance.data.pop(key) From b63f5c5245c6956b453794b7cb9771fd9d1675d2 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 28 Jun 2023 09:57:02 +0100 Subject: [PATCH 011/163] Ensure list arguments are valid --- openpype/hosts/maya/api/lib.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index a34318f4e98..6a218e5cfec 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1213,6 +1213,12 @@ def extract_alembic(file, # Alembic Exporter requires forward slashes file = file.replace('\\', '/') + # Ensure list arguments are valid. + args = [attr, attrPrefix, root] + for arg in args: + if arg is None: + arg = [] + # Pass the start and end frame on as `frameRange` so that it # never conflicts with that argument if not frameRange: From e722fbe1c7ab5a2bf2c176aef679b57a6de0bd6a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 28 Jun 2023 11:05:23 +0100 Subject: [PATCH 012/163] BigRoy feedback --- openpype/hosts/maya/api/lib.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 6a218e5cfec..68f5d01128b 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1214,10 +1214,10 @@ def extract_alembic(file, file = file.replace('\\', '/') # Ensure list arguments are valid. - args = [attr, attrPrefix, root] - for arg in args: - if arg is None: - arg = [] + local_vars = locals() + for key in ["attr", "attrPrefix", "root"]: + if local_vars[key] is None: + local_vars[key] = [] # Pass the start and end frame on as `frameRange` so that it # never conflicts with that argument From 38be2be6298e8badfceec67e206dd620c82c31b1 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 3 Jul 2023 10:13:32 +0100 Subject: [PATCH 013/163] Fix list arguments. --- openpype/hosts/maya/api/lib.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 68f5d01128b..53bbee784a8 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -1214,10 +1214,9 @@ def extract_alembic(file, file = file.replace('\\', '/') # Ensure list arguments are valid. - local_vars = locals() - for key in ["attr", "attrPrefix", "root"]: - if local_vars[key] is None: - local_vars[key] = [] + attr = attr or [] + attrPrefix = attrPrefix or [] + root = root or [] # Pass the start and end frame on as `frameRange` so that it # never conflicts with that argument From 5aa5215b824139ea20b1bc21ba9d20911531f99c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 4 Jul 2023 10:09:14 +0100 Subject: [PATCH 014/163] Working animation family --- .../maya/plugins/create/create_animation.py | 78 ++- .../plugins/publish/extract_pointcache.py | 23 +- .../defaults/project_settings/maya.json | 122 +++- .../schemas/schema_maya_create.json | 558 ++++++++++++++---- 4 files changed, 583 insertions(+), 198 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py index 92f25566801..81bcce690cd 100644 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ b/openpype/hosts/maya/plugins/create/create_animation.py @@ -2,6 +2,8 @@ lib, plugin ) +from openpype.settings import get_project_settings +from openpype.pipeline import legacy_io class CreateAnimation(plugin.Creator): @@ -17,31 +19,7 @@ class CreateAnimation(plugin.Creator): label = "Animation" family = "animation" icon = "male" - includeUserDefinedAttributes = False - eulerFilter = True - noNormals = False - preRoll = False - renderableOnly = False - uvWrite = True - writeColorSets = False - writeFaceSets = False - wholeFrameGeo = False - worldSpace = True - writeVisibility = True - writeUVSets = True - writeCreases = False - dataFormat = "ogawa" - step = 1.0 - attr = "" - attrPrefix = "" - stripNamespaces = True - verbose = False - preRollStartFrame = 0 - farm = False - priority = 50 - includeParentHierarchy = False # Include parent groups - refresh = False # Default to suspend refresh. - visibleOnly = False # only nodes that are visible + writeNormals = True # Multiverse specific attribute. def __init__(self, *args, **kwargs): super(CreateAnimation, self).__init__(*args, **kwargs) @@ -51,31 +29,43 @@ def __init__(self, *args, **kwargs): self.data[key] = value attrs = [ - "includeUserDefinedAttributes", - "eulerFilter", - "noNormals", - "preRoll", - "renderableOnly", - "uvWrite", + "step", "writeColorSets", "writeFaceSets", - "wholeFrameGeo", + "renderableOnly", + "visibleOnly", + "includeParentHierarchy", "worldSpace", - "writeVisibility", - "writeUVSets", - "writeCreases", - "dataFormat", - "step", + "farm", + "priority", + "writeNormals", + "includeUserDefinedAttributes", "attr", "attrPrefix", - "stripNamespaces", - "verbose", + "dataFormat", + "eulerFilter", + "noNormals", + "preRoll", "preRollStartFrame", - "farm", - "priority", - "includeParentHierarchy", "refresh", - "visibleOnly" + "stripNamespaces", + "uvWrite", + "verbose", + "wholeFrameGeo", + "writeCreases", + "writeUVSets", + "writeVisibility" ] for attr in attrs: - self.data[attr] = getattr(self, attr) + value = getattr(self, attr) + if isinstance(value, dict): + if not value["enabled"]: + self.log.debug( + "Skipping \"{}\" because its disabled in " + "settings".format(attr) + ) + continue + value = value["value"] + + # Setting value from settings. + self.data[attr] = value diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index afbbc322908..1edff7d68ac 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -54,27 +54,28 @@ def process(self, instance): options = {"selection": True} option_keys = [ + "dataFormat", "eulerFilter", "noNormals", "preRoll", + "preRollStartFrame", "renderableOnly", + "step", + "stripNamespaces", "uvWrite", - "writeColorSets", - "writeFaceSets", + "verbose", "wholeFrameGeo", "worldSpace", - "writeVisibility", - "writeUVSets", + "writeColorSets", "writeCreases", - "dataFormat", - "step", - "stripNamespaces", - "verbose", - "preRollStartFrame" + "writeFaceSets", + "writeUVSets", + "writeVisibility", ] for key in option_keys: - options[key] = instance.data[key] - self.log.info(options) + if key in instance.data: + options[key] = instance.data[key] + if not instance.data.get("includeParentHierarchy", True): # Set the root nodes if we don't want to include parents # The roots are to be considered the ones that are the actual diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 7072e2d1a43..7c7c7a100a9 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -556,34 +556,106 @@ }, "CreateAnimation": { "enabled": false, - "includeUserDefinedAttributes": false, - "eulerFilter": true, - "noNormals": false, - "preRoll": false, - "renderableOnly": false, - "uvWrite": true, - "WriteColorSets": false, - "writeFaceSets": false, - "wholeFrameGeo": false, - "worldSpace": true, - "writeVisibility": true, - "writeUVSets": true, - "writeCreases": false, - "dataFormat": "ogawa", - "step": 1.0, - "attr": "", - "attrPrefix": "", - "stripNamespaces": true, - "verbose": false, - "preRollStartFrame": 0, - "farm": false, - "priority": 50, - "includeParentHierarchy": false, - "refresh": false, - "visibleOnly": false, "defaults": [ "Main" ], + "step": 1.0, + "writeColorSets": { + "enabled": true, + "value": false + }, + "writeFaceSets": { + "enabled": true, + "value": false + }, + "renderableOnly": { + "enabled": true, + "value": false + }, + "visibleOnly": { + "enabled": true, + "value": false + }, + "includeParentHierarchy": { + "enabled": true, + "value": false + }, + "worldSpace": { + "enabled": true, + "value": true + }, + "farm": { + "enabled": true, + "value": false + }, + "priority": { + "enabled": true, + "value": 50 + }, + "includeUserDefinedAttributes": { + "enabled": true, + "value": true + }, + "attr": { + "enabled": true, + "value": "" + }, + "attrPrefix": { + "enabled": true, + "value": "" + }, + "dataFormat": { + "enabled": false, + "value": "ogawa" + }, + "eulerFilter": { + "enabled": false, + "value": true + }, + "noNormals": { + "enabled": false, + "value": true + }, + "preRoll": { + "enabled": false, + "value": true + }, + "preRollStartFrame": { + "enabled": false, + "value": 0 + }, + "refresh": { + "enabled": false, + "value": false + }, + "stripNamespaces": { + "enabled": false, + "value": true + }, + "uvWrite": { + "enabled": false, + "value": true + }, + "verbose": { + "enabled": false, + "value": true + }, + "wholeFrameGeo": { + "enabled": false, + "value": true + }, + "writeCreases": { + "enabled": false, + "value": true + }, + "writeUVSets": { + "enabled": false, + "value": true + }, + "writeVisibility": { + "enabled": false, + "value": true + }, "write_color_sets": false, "write_face_sets": false, "include_parent_hierarchy": false, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index aeff809cf66..f4cfee9b329 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -128,74 +128,10 @@ "label": "Enabled" }, { - "type": "boolean", - "key": "includeUserDefinedAttributes", - "label": "Include User Defined Attributes" - }, - { - "type": "boolean", - "key": "eulerFilter", - "label": "Euler Filter" - }, - { - "type": "boolean", - "key": "noNormals", - "label": "No Normals" - }, - { - "type": "boolean", - "key": "preRoll", - "label": "Pre Roll" - }, - { - "type": "boolean", - "key": "renderableOnly", - "label": "Renderable Only" - }, - { - "type": "boolean", - "key": "uvWrite", - "label": "UV Write" - }, - { - "type": "boolean", - "key": "WriteColorSets", - "label": "Write Color Sets" - }, - { - "type": "boolean", - "key": "writeFaceSets", - "label": "Write Face Sets" - }, - { - "type": "boolean", - "key": "wholeFrameGeo", - "label": "Whole Frame Geo" - }, - { - "type": "boolean", - "key": "worldSpace", - "label": "World Space" - }, - { - "type": "boolean", - "key": "writeVisibility", - "label": "Write Visibility" - }, - { - "type": "boolean", - "key": "writeUVSets", - "label": "Write UV Sets" - }, - { - "type": "boolean", - "key": "writeCreases", - "label": "Write Creases" - }, - { - "type": "text", - "key": "dataFormat", - "label": "Data Format" + "type": "list", + "key": "defaults", + "label": "Default Subsets", + "object_type": "text" }, { "type": "number", @@ -205,60 +141,446 @@ "decimal": 4 }, { - "type": "text", + "label": "Write Color Sets", + "checkbox_key": "enabled", + "key": "writeColorSets", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Write Face Sets", + "checkbox_key": "enabled", + "key": "writeFaceSets", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Renderable Only", + "checkbox_key": "enabled", + "key": "renderableOnly", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Visible Only", + "checkbox_key": "enabled", + "key": "visibleOnly", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Include Parent Hierarchy", + "checkbox_key": "enabled", + "key": "includeParentHierarchy", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "World Space", + "checkbox_key": "enabled", + "key": "worldSpace", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Farm", + "checkbox_key": "enabled", + "key": "farm", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Priority", + "checkbox_key": "enabled", + "key": "priority", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "number", + "key": "value", + "label": "Value", + "minimum": 0 + } + ] + }, + { + "label": "Include User Defined Attributes", + "checkbox_key": "enabled", + "key": "includeUserDefinedAttributes", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Attr", + "checkbox_key": "enabled", "key": "attr", - "label": "Attr" - }, - { - "type": "text", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Attr Prefix", + "checkbox_key": "enabled", "key": "attrPrefix", - "label": "Attr Prefix" - }, - { - "type": "boolean", - "key": "stripNamespaces", - "label": "StripNamespaces" - }, - { - "type": "boolean", - "key": "verbose", - "label": "Verbose" - }, - { - "type": "number", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "text", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Data Format", + "checkbox_key": "enabled", + "key": "dataFormat", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "enum", + "key": "value", + "label": "Value", + "enum_items": [ + { + "ogawa": "ogawa" + }, + { + "HDF": "HDF" + } + ] + } + ] + }, + { + "label": "Euler Filter", + "checkbox_key": "enabled", + "key": "eulerFilter", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "No Normals", + "checkbox_key": "enabled", + "key": "noNormals", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Pre Roll", + "checkbox_key": "enabled", + "key": "preRoll", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Pre Roll Start Frame", + "checkbox_key": "enabled", "key": "preRollStartFrame", - "label": "Pre Roll Start Frame" - }, - { - "type": "boolean", - "key": "farm", - "label": "Farm" - }, - { - "type": "number", - "key": "priority", - "label": "Priority" - }, - { - "type": "boolean", - "key": "includeParentHierarchy", - "label": "Include Parent Hierarchy" - }, - { - "type": "boolean", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "number", + "key": "value", + "label": "Value", + "minimum": 0 + } + ] + }, + { + "label": "Refresh", + "checkbox_key": "enabled", "key": "refresh", - "label": "Refresh" - }, - { - "type": "boolean", - "key": "visibleOnly", - "label": "Visible Only" - }, - { - "type": "list", - "key": "defaults", - "label": "Default Subsets", - "object_type": "text" + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Strip Namespaces", + "checkbox_key": "enabled", + "key": "stripNamespaces", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "UV Write", + "checkbox_key": "enabled", + "key": "uvWrite", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Verbose", + "checkbox_key": "enabled", + "key": "verbose", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Whole Frame Geo", + "checkbox_key": "enabled", + "key": "wholeFrameGeo", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Write Creases", + "checkbox_key": "enabled", + "key": "writeCreases", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Write UV Sets", + "checkbox_key": "enabled", + "key": "writeUVSets", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] + }, + { + "label": "Write Visibility", + "checkbox_key": "enabled", + "key": "writeVisibility", + "type": "dict", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "value", + "label": "Value" + } + ] }, { "type": "boolean", From 2f8dafd2a0f3a09e33994818c46fd7b4ad5aa440 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 4 Jul 2023 10:10:06 +0100 Subject: [PATCH 015/163] Hound --- openpype/hosts/maya/plugins/create/create_animation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py index 81bcce690cd..7b01f5a1b93 100644 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ b/openpype/hosts/maya/plugins/create/create_animation.py @@ -2,8 +2,6 @@ lib, plugin ) -from openpype.settings import get_project_settings -from openpype.pipeline import legacy_io class CreateAnimation(plugin.Creator): From e447e6a894af951229374c94bff94e4c9031c20d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 6 Jul 2023 17:01:28 +0100 Subject: [PATCH 016/163] Initial working animation family --- openpype/hosts/maya/api/plugin.py | 6 +- .../maya/plugins/create/create_animation.py | 93 ++- .../publish/validate_instance_attributes.py | 7 +- .../defaults/project_settings/maya.json | 147 ++-- .../schemas/schema_maya_create.json | 705 +++++++----------- 5 files changed, 378 insertions(+), 580 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 604ff101dbb..761d98cf12e 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -1,5 +1,4 @@ import os -import re from maya import cmds @@ -97,8 +96,13 @@ def process(self): instance = cmds.sets(nodes, name=self.name) lib.imprint(instance, self.data) + self.post_imprint(instance) + return instance + def post_imprint(self, objset): + pass + class Loader(LoaderPlugin): hosts = ["maya"] diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py index 7b01f5a1b93..f1b15770f16 100644 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ b/openpype/hosts/maya/plugins/create/create_animation.py @@ -1,3 +1,5 @@ +from maya import cmds + from openpype.hosts.maya.api import ( lib, plugin @@ -17,7 +19,34 @@ class CreateAnimation(plugin.Creator): label = "Animation" family = "animation" icon = "male" - writeNormals = True # Multiverse specific attribute. + settings_attrs = [ + "step", + "writeColorSets", + "writeFaceSets", + "renderableOnly", + "visibleOnly", + "includeParentHierarchy", + "worldSpace", + "farm", + "priority", + "writeNormals", + "includeUserDefinedAttributes", + "attr", + "attrPrefix", + "dataFormat", + "eulerFilter", + "noNormals", + "preRoll", + "preRollStartFrame", + "refresh", + "stripNamespaces", + "uvWrite", + "verbose", + "wholeFrameGeo", + "writeCreases", + "writeUVSets", + "writeVisibility" + ] def __init__(self, *args, **kwargs): super(CreateAnimation, self).__init__(*args, **kwargs) @@ -26,44 +55,26 @@ def __init__(self, *args, **kwargs): for key, value in lib.collect_animation_data().items(): self.data[key] = value - attrs = [ - "step", - "writeColorSets", - "writeFaceSets", - "renderableOnly", - "visibleOnly", - "includeParentHierarchy", - "worldSpace", - "farm", - "priority", - "writeNormals", - "includeUserDefinedAttributes", - "attr", - "attrPrefix", - "dataFormat", - "eulerFilter", - "noNormals", - "preRoll", - "preRollStartFrame", - "refresh", - "stripNamespaces", - "uvWrite", - "verbose", - "wholeFrameGeo", - "writeCreases", - "writeUVSets", - "writeVisibility" - ] - for attr in attrs: - value = getattr(self, attr) - if isinstance(value, dict): - if not value["enabled"]: - self.log.debug( - "Skipping \"{}\" because its disabled in " - "settings".format(attr) - ) - continue - value = value["value"] + # Setting value from settings. + for attr in self.settings_attrs: + if not hasattr(self, attr): + continue + + self.data[attr] = getattr(self, attr) + + def post_imprint(self, objset): + for attr in self.settings_attrs: + editable = attr + "_editable" + + if not hasattr(self, editable): + continue + + if getattr(self, editable): + continue - # Setting value from settings. - self.data[attr] = value + self.log.debug( + "Locking \"{}\" because its disabled in settings".format(attr) + ) + cmds.setAttr( + objset + "." + attr, channelBox=False, lock=True + ) diff --git a/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py b/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py index f870c9f8c45..41bb6ed3bac 100644 --- a/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py @@ -57,4 +57,9 @@ def process(self, instance): @classmethod def repair(cls, instance): - imprint(instance.data["objset"], cls.get_missing_attributes(instance)) + missing_attributes = cls.get_missing_attributes(instance) + imprint(instance.data["objset"], missing_attributes) + + plugin = cls.plugins_by_family[instance.data["family"]] + if hasattr(plugin, "post_imprint"): + plugin.post_imprint(plugin, instance.data["objset"]) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 7c7c7a100a9..7a4bccee7b8 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -560,102 +560,57 @@ "Main" ], "step": 1.0, - "writeColorSets": { - "enabled": true, - "value": false - }, - "writeFaceSets": { - "enabled": true, - "value": false - }, - "renderableOnly": { - "enabled": true, - "value": false - }, - "visibleOnly": { - "enabled": true, - "value": false - }, - "includeParentHierarchy": { - "enabled": true, - "value": false - }, - "worldSpace": { - "enabled": true, - "value": true - }, - "farm": { - "enabled": true, - "value": false - }, - "priority": { - "enabled": true, - "value": 50 - }, - "includeUserDefinedAttributes": { - "enabled": true, - "value": true - }, - "attr": { - "enabled": true, - "value": "" - }, - "attrPrefix": { - "enabled": true, - "value": "" - }, - "dataFormat": { - "enabled": false, - "value": "ogawa" - }, - "eulerFilter": { - "enabled": false, - "value": true - }, - "noNormals": { - "enabled": false, - "value": true - }, - "preRoll": { - "enabled": false, - "value": true - }, - "preRollStartFrame": { - "enabled": false, - "value": 0 - }, - "refresh": { - "enabled": false, - "value": false - }, - "stripNamespaces": { - "enabled": false, - "value": true - }, - "uvWrite": { - "enabled": false, - "value": true - }, - "verbose": { - "enabled": false, - "value": true - }, - "wholeFrameGeo": { - "enabled": false, - "value": true - }, - "writeCreases": { - "enabled": false, - "value": true - }, - "writeUVSets": { - "enabled": false, - "value": true - }, - "writeVisibility": { - "enabled": false, - "value": true - }, + "step_editable": true, + "writeColorSets": false, + "writeColorSets_editable": true, + "writeFaceSets": false, + "writeFaceSets_editable": true, + "renderableOnly": false, + "renderableOnly_editable": true, + "visibleOnly": false, + "visibleOnly_editable": true, + "includeParentHierarchy": false, + "includeParentHierarchy_editable": true, + "worldSpace": true, + "worldSpace_editable": true, + "farm": false, + "farm_editable": true, + "priority": 50, + "priority_editable": true, + "writeNormals": true, + "writeNormals_editable": true, + "includeUserDefinedAttributes": true, + "includeUserDefinedAttributes_editable": true, + "attr": "", + "attr_editable": true, + "attrPrefix": "", + "attrPrefix_editable": true, + "dataFormat": "ogawa", + "dataFormat_editable": false, + "eulerFilter": false, + "eulerFilter_editable": false, + "noNormals": false, + "noNormals_editable": false, + "preRoll": false, + "preRoll_editable": false, + "preRollStartFrame": 0, + "preRollStartFrame_editable": false, + "refresh": false, + "refresh_editable": false, + "stripNamespaces": false, + "stripNamespaces_editable": false, + "uvWrite": true, + "uvWrite_editable": false, + "verbose": false, + "verbose_editable": false, + "wholeFrameGeo": false, + "wholeFrameGeo_editable": false, + "writeCreases": false, + "writeCreases_editable": false, + "writeUVSets": false, + "writeUVSets_editable": false, + "writeVisibility": true, + "writeVisibility_editable": false, "write_color_sets": false, "write_face_sets": false, "include_parent_hierarchy": false, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index f4cfee9b329..2179e562094 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -136,451 +136,274 @@ { "type": "number", "key": "step", - "label": "Step", + "label": "Step default", "minimum": 0.0, "decimal": 4 }, { - "label": "Write Color Sets", - "checkbox_key": "enabled", - "key": "writeColorSets", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Write Face Sets", - "checkbox_key": "enabled", - "key": "writeFaceSets", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Renderable Only", - "checkbox_key": "enabled", - "key": "renderableOnly", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Visible Only", - "checkbox_key": "enabled", - "key": "visibleOnly", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Include Parent Hierarchy", - "checkbox_key": "enabled", - "key": "includeParentHierarchy", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "World Space", - "checkbox_key": "enabled", - "key": "worldSpace", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Farm", - "checkbox_key": "enabled", - "key": "farm", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Priority", - "checkbox_key": "enabled", - "key": "priority", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "number", - "key": "value", - "label": "Value", - "minimum": 0 - } - ] - }, - { - "label": "Include User Defined Attributes", - "checkbox_key": "enabled", - "key": "includeUserDefinedAttributes", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Attr", - "checkbox_key": "enabled", - "key": "attr", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "text", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Attr Prefix", - "checkbox_key": "enabled", - "key": "attrPrefix", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "text", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Data Format", - "checkbox_key": "enabled", - "key": "dataFormat", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "enum", - "key": "value", - "label": "Value", - "enum_items": [ - { - "ogawa": "ogawa" - }, - { - "HDF": "HDF" - } - ] - } - ] - }, - { - "label": "Euler Filter", - "checkbox_key": "enabled", - "key": "eulerFilter", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "No Normals", - "checkbox_key": "enabled", - "key": "noNormals", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Pre Roll", - "checkbox_key": "enabled", - "key": "preRoll", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Pre Roll Start Frame", - "checkbox_key": "enabled", - "key": "preRollStartFrame", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "number", - "key": "value", - "label": "Value", - "minimum": 0 - } - ] - }, - { - "label": "Refresh", - "checkbox_key": "enabled", - "key": "refresh", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Strip Namespaces", - "checkbox_key": "enabled", - "key": "stripNamespaces", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "UV Write", - "checkbox_key": "enabled", - "key": "uvWrite", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Verbose", - "checkbox_key": "enabled", - "key": "verbose", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Whole Frame Geo", - "checkbox_key": "enabled", - "key": "wholeFrameGeo", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Write Creases", - "checkbox_key": "enabled", - "key": "writeCreases", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Write UV Sets", - "checkbox_key": "enabled", - "key": "writeUVSets", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] - }, - { - "label": "Write Visibility", - "checkbox_key": "enabled", - "key": "writeVisibility", - "type": "dict", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "type": "boolean", - "key": "value", - "label": "Value" - } - ] + "type": "boolean", + "key": "step_editable", + "label": "Step editable" + }, + { + "type": "boolean", + "key": "writeColorSets", + "label": "Write Color Sets default" + }, + { + "type": "boolean", + "key": "writeColorSets_editable", + "label": "Write Color Sets editable" + }, + { + "type": "boolean", + "key": "writeFaceSets", + "label": "Write Face Sets default" + }, + { + "type": "boolean", + "key": "writeFaceSets_editable", + "label": "Write Face Sets editable" + }, + { + "type": "boolean", + "key": "renderableOnly", + "label": "Renderable Only default" + }, + { + "type": "boolean", + "key": "renderableOnly_editable", + "label": "Renderable Only editable" + }, + { + "type": "boolean", + "key": "visibleOnly", + "label": "Visible Only default" + }, + { + "type": "boolean", + "key": "visibleOnly_editable", + "label": "Visible Only editable" + }, + { + "type": "boolean", + "key": "includeParentHierarchy", + "label": "Include Parent Hierarchy default" + }, + { + "type": "boolean", + "key": "includeParentHierarchy_editable", + "label": "Include Parent Hierarchy editable" + }, + { + "type": "boolean", + "key": "worldSpace", + "label": "World Space default" + }, + { + "type": "boolean", + "key": "worldSpace_editable", + "label": "World Space editable" + }, + { + "type": "boolean", + "key": "farm", + "label": "Farm default" + }, + { + "type": "boolean", + "key": "farm_editable", + "label": "Farm editable" + }, + { + "type": "number", + "key": "priority", + "label": "Priority default", + "minimum": 0 + }, + { + "type": "boolean", + "key": "priority_editable", + "label": "Priority editable" + }, + { + "type": "boolean", + "key": "writeNormals", + "label": "Write Normals default" + }, + { + "type": "boolean", + "key": "writeNormals_editable", + "label": "Write Normals editable" + }, + { + "type": "boolean", + "key": "includeUserDefinedAttributes", + "label": "Include User Defined Attributes default" + }, + { + "type": "boolean", + "key": "includeUserDefinedAttributes_editable", + "label": "Include User Defined Attributes editable" + }, + { + "type": "text", + "key": "attr", + "label": "Attr default" + }, + { + "type": "boolean", + "key": "attr_editable", + "label": "Attr editable" + }, + { + "type": "text", + "key": "attrPrefix", + "label": "Attr Prefix default" + }, + { + "type": "boolean", + "key": "attrPrefix_editable", + "label": "Attr Prefix editable" + }, + { + "type": "enum", + "key": "dataFormat", + "label": "Data Format default", + "enum_items": [ + { + "ogawa": "ogawa" + }, + { + "HDF": "HDF" + } + ] + }, + { + "type": "boolean", + "key": "dataFormat_editable", + "label": "Data Format editable" + }, + { + "type": "boolean", + "key": "eulerFilter", + "label": "Euler Filter default" + }, + { + "type": "boolean", + "key": "eulerFilter_editable", + "label": "Euler Filter editable" + }, + { + "type": "boolean", + "key": "noNormals", + "label": "No Normals default" + }, + { + "type": "boolean", + "key": "noNormals_editable", + "label": "No Normals editable" + }, + { + "type": "boolean", + "key": "preRoll", + "label": "Pre Roll default" + }, + { + "type": "boolean", + "key": "preRoll_editable", + "label": "Pre Roll editable" + }, + { + "type": "number", + "key": "preRollStartFrame", + "label": "Pre Roll Start Frame default", + "minimum": 0 + }, + { + "type": "boolean", + "key": "preRollStartFrame_editable", + "label": "Pre Roll Start Frame editable" + }, + { + "type": "boolean", + "key": "refresh", + "label": "Refresh default" + }, + { + "type": "boolean", + "key": "refresh_editable", + "label": "Refresh editable" + }, + { + "type": "boolean", + "key": "stripNamespaces", + "label": "Strip Namespaces default" + }, + { + "type": "boolean", + "key": "stripNamespaces_editable", + "label": "Strip Namespaces editable" + }, + { + "type": "boolean", + "key": "uvWrite", + "label": "UV Write default" + }, + { + "type": "boolean", + "key": "uvWrite_editable", + "label": "UV Write editable" + }, + { + "type": "boolean", + "key": "verbose", + "label": "Verbose default" + }, + { + "type": "boolean", + "key": "verbose_editable", + "label": "Verbose editable" + }, + { + "type": "boolean", + "key": "wholeFrameGeo", + "label": "Whole Frame Geo default" + }, + { + "type": "boolean", + "key": "wholeFrameGeo_editable", + "label": "Whole Frame Geo editable" + }, + { + "type": "boolean", + "key": "writeCreases", + "label": "Write Creases default" + }, + { + "type": "boolean", + "key": "writeCreases_editable", + "label": "Write Creases editable" + }, + { + "type": "boolean", + "key": "writeUVSets", + "label": "Write UV Sets default" + }, + { + "type": "boolean", + "key": "writeUVSets_editable", + "label": "Write UV Sets editable" + }, + { + "type": "boolean", + "key": "writeVisibility", + "label": "Write Visibility default" + }, + { + "type": "boolean", + "key": "writeVisibility_editable", + "label": "Write Visibility editable" }, { "type": "boolean", From 9ac4017d015d18bbf3aee48687e7c35b3551af7f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 14 Jul 2023 15:10:00 +0100 Subject: [PATCH 017/163] Working pointcache --- .../maya/plugins/create/create_pointcache.py | 97 +++++-- .../maya/plugins/publish/collect_animation.py | 4 + .../plugins/publish/collect_pointcache.py | 29 +- .../defaults/project_settings/maya.json | 71 +++-- .../schemas/schema_maya_create.json | 269 ++++++++++++++---- 5 files changed, 351 insertions(+), 119 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_pointcache.py b/openpype/hosts/maya/plugins/create/create_pointcache.py index f4e8cbfc9a2..8cd96cc689d 100644 --- a/openpype/hosts/maya/plugins/create/create_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_pointcache.py @@ -6,8 +6,11 @@ ) from openpype.lib import ( BoolDef, - TextDef + TextDef, + NumberDef, + EnumDef ) +from openpype.settings import get_current_project_settings#not needed if applying settings is merged. class CreatePointCache(plugin.MayaCreator): @@ -22,59 +25,95 @@ class CreatePointCache(plugin.MayaCreator): include_user_defined_attributes = False def get_instance_attr_defs(self): - defs = lib.collect_animation_defs() defs.extend([ BoolDef("writeColorSets", label="Write vertex colors", - tooltip="Write vertex colors with the geometry", - default=False), + tooltip="Write vertex colors with the geometry"), BoolDef("writeFaceSets", label="Write face sets", - tooltip="Write face sets with the geometry", - default=False), + tooltip="Write face sets with the geometry"), BoolDef("renderableOnly", label="Renderable Only", - tooltip="Only export renderable visible shapes", - default=False), + tooltip="Only export renderable visible shapes"), BoolDef("visibleOnly", label="Visible Only", tooltip="Only export dag objects visible during " - "frame range", - default=False), + "frame range"), BoolDef("includeParentHierarchy", label="Include Parent Hierarchy", tooltip="Whether to include parent hierarchy of nodes in " - "the publish instance", - default=False), + "the publish instance"), BoolDef("worldSpace", - label="World-Space Export", - default=True), - BoolDef("refresh", - label="Refresh viewport during export", - default=False), + label="World-Space Export"), + BoolDef("farm", + label="Submit to farm"), + NumberDef("priority", + label="Priority for farm"), + BoolDef("noNormals", + label="Include normals"), BoolDef("includeUserDefinedAttributes", - label="Include User Defined Attributes", - default=self.include_user_defined_attributes), + label="Include User Defined Attributes"), TextDef("attr", label="Custom Attributes", - default="", placeholder="attr1, attr2"), TextDef("attrPrefix", label="Custom Attributes Prefix", - default="", - placeholder="prefix1, prefix2") + placeholder="prefix1, prefix2"), + EnumDef("dataFormat", + label="Data Format", + items=["ogawa", "HDF"]), + BoolDef("eulerFilter", + label="Apply Euler Filter"), + BoolDef("preRoll", + label="Start from preroll start frame"), + NumberDef("preRollStartFrame", + label="Start frame for preroll"), + BoolDef("refresh", + label="Refresh viewport during export"), + BoolDef("stripNamespaces", + label="Strip namespaces on export"), + BoolDef("uvWrite", + label="Write UVs"), + BoolDef("verbose", + label="Verbose output"), + BoolDef("wholeFrameGeo", + label="Whole Frame Geo"), + BoolDef("writeCreases", + label="Write Creases"), + BoolDef("writeUVSets", + label="Write UV Sets"), + BoolDef("writeVisibility", + label="Write Visibility") ]) - # TODO: Implement these on a Deadline plug-in instead? - """ - # Default to not send to farm. - self.data["farm"] = False - self.data["priority"] = 50 - """ + # Collect editable state and default values. + settings = get_current_project_settings() + settings = settings["maya"]["create"]["CreatePointCache"] + editable_attributes = {} + for key, value in settings.items(): + if not key.endswith("_editable"): + continue + + if not value: + continue + + attribute = key.replace("_editable", "") + editable_attributes[attribute] = settings[attribute] + + resulting_defs = [] + for definition in defs: + # Remove non-editable defs. + if definition.key not in editable_attributes.keys(): + continue + + # Set default values from settings. + definition.default = editable_attributes[definition.key] + + resulting_defs.append(definition) - return defs + return resulting_defs def create(self, subset_name, instance_data, pre_create_data): diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index 5cfdb358cef..94b732cc4de 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -58,6 +58,10 @@ def process(self, instance): if instance.data.get("farm"): instance.data["families"].append("publish.farm") + # Alembic and Multiverse share the same attribute functionality but + # different names. + instance.data["writeNormals"] = not instance.data["noNormals"] + # Backwards compatibility for attributes. backwards_mapping = { "write_color_sets": "writeColorSets", diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 99e2e7ef8ed..87eecbcf3e9 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -10,6 +10,11 @@ class CollectPointcache(pyblish.api.InstancePlugin): families = ["pointcache"] label = "Collect Pointcache" hosts = ["maya"] + legacy_settings = { + "write_color_sets": "writeColorSets", + "write_face_sets": "writeFaceSets", + "include_user_defined_attributes": "includeUserDefinedAttributes" + } def process(self, instance): if instance.data.get("farm"): @@ -46,13 +51,25 @@ def process(self, instance): instance.remove(proxy_set) instance.data["setMembers"].remove(proxy_set) + # Apply default values not exposed to the user. + settings = instance.context.data["project_settings"]["maya"]["create"] + for key, value in settings["CreatePointCache"].items(): + if key.endswith("_editable"): + continue + + if key in instance.data: + continue + + if key in self.legacy_settings.keys(): + continue + + self.log.debug( + "Adding \"{}:{}\" from settings.".format(key, value) + ) + instance.data[key] = value + # Backwards compatibility for attributes. - backwards_mapping = { - "write_color_sets": "writeColorSets", - "write_face_sets": "writeFaceSets", - "include_user_defined_attributes": "includeUserDefinedAttributes" - } - for key, value in backwards_mapping.items(): + for key, value in self.legacy_settings.items(): if key in instance.data: self.log.debug( "Using legacy attribute name '{}' since it exists.".format( diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 7c479eba36b..6cb11fb401e 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -628,34 +628,61 @@ }, "CreatePointCache": { "enabled": true, - "includeUserDefinedAttributes": false, - "eulerFilter": true, - "noNormals": false, - "preRoll": false, - "renderableOnly": false, - "uvWrite": true, - "WriteColorSets": false, + "defaults": [ + "Main" + ], + "step": 1.0, + "step_editable": true, + "writeColorSets": true, + "writeColorSets_editable": true, "writeFaceSets": false, - "wholeFrameGeo": false, + "writeFaceSets_editable": true, + "renderableOnly": false, + "renderableOnly_editable": true, + "visibleOnly": false, + "visibleOnly_editable": true, + "includeParentHierarchy": false, + "includeParentHierarchy_editable": true, "worldSpace": true, - "writeVisibility": true, - "writeUVSets": true, - "writeCreases": false, - "dataFormat": "ogawa", - "step": 1.0, + "worldSpace_editable": true, + "farm": false, + "farm_editable": true, + "priority": 50, + "priority_editable": true, + "writeNormals": true, + "writeNormals_editable": true, + "includeUserDefinedAttributes": true, + "includeUserDefinedAttributes_editable": true, "attr": "", + "attr_editable": true, "attrPrefix": "", - "stripNamespaces": true, - "verbose": false, + "attrPrefix_editable": true, + "dataFormat": "ogawa", + "dataFormat_editable": false, + "eulerFilter": false, + "eulerFilter_editable": false, + "noNormals": false, + "noNormals_editable": false, + "preRoll": false, + "preRoll_editable": false, "preRollStartFrame": 0, - "farm": false, - "priority": 50, - "includeParentHierarchy": false, + "preRollStartFrame_editable": false, "refresh": false, - "visibleOnly": false, - "defaults": [ - "Main" - ], + "refresh_editable": false, + "stripNamespaces": false, + "stripNamespaces_editable": false, + "uvWrite": true, + "uvWrite_editable": false, + "verbose": false, + "verbose_editable": false, + "wholeFrameGeo": false, + "wholeFrameGeo_editable": false, + "writeCreases": false, + "writeCreases_editable": false, + "writeUVSets": false, + "writeUVSets_editable": false, + "writeVisibility": true, + "writeVisibility_editable": false, "write_color_sets": false, "write_face_sets": false, "include_user_defined_attributes": false diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index 81f23010470..8adcdcd1d7b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -469,138 +469,283 @@ "key": "enabled", "label": "Enabled" }, + { + "type": "list", + "key": "defaults", + "label": "Default Subsets", + "object_type": "text" + }, + { + "type": "number", + "key": "step", + "label": "Step default", + "minimum": 0.0, + "decimal": 4 + }, { "type": "boolean", - "key": "includeUserDefinedAttributes", - "label": "Include User Defined Attributes" + "key": "step_editable", + "label": "Step editable" + }, + { + "type": "boolean", + "key": "writeColorSets", + "label": "Write Color Sets default" }, { "type": "boolean", - "key": "eulerFilter", - "label": "Euler Filter" + "key": "writeColorSets_editable", + "label": "Write Color Sets editable" + }, + { + "type": "boolean", + "key": "writeFaceSets", + "label": "Write Face Sets default" }, { "type": "boolean", - "key": "noNormals", - "label": "No Normals" + "key": "writeFaceSets_editable", + "label": "Write Face Sets editable" + }, + { + "type": "boolean", + "key": "renderableOnly", + "label": "Renderable Only default" }, { "type": "boolean", - "key": "preRoll", - "label": "Pre Roll" + "key": "renderableOnly_editable", + "label": "Renderable Only editable" + }, + { + "type": "boolean", + "key": "visibleOnly", + "label": "Visible Only default" }, { "type": "boolean", - "key": "renderableOnly", - "label": "Renderable Only" + "key": "visibleOnly_editable", + "label": "Visible Only editable" + }, + { + "type": "boolean", + "key": "includeParentHierarchy", + "label": "Include Parent Hierarchy default" }, { "type": "boolean", - "key": "uvWrite", - "label": "UV Write" + "key": "includeParentHierarchy_editable", + "label": "Include Parent Hierarchy editable" + }, + { + "type": "boolean", + "key": "worldSpace", + "label": "World Space default" }, { "type": "boolean", - "key": "WriteColorSets", - "label": "Write Color Sets" + "key": "worldSpace_editable", + "label": "World Space editable" + }, + { + "type": "boolean", + "key": "farm", + "label": "Farm default" }, { "type": "boolean", - "key": "writeFaceSets", - "label": "Write Face Sets" + "key": "farm_editable", + "label": "Farm editable" + }, + { + "type": "number", + "key": "priority", + "label": "Priority default", + "minimum": 0 }, { "type": "boolean", - "key": "wholeFrameGeo", - "label": "Whole Frame Geo" + "key": "priority_editable", + "label": "Priority editable" + }, + { + "type": "boolean", + "key": "writeNormals", + "label": "Write Normals default" }, { "type": "boolean", - "key": "worldSpace", - "label": "World Space" + "key": "writeNormals_editable", + "label": "Write Normals editable" + }, + { + "type": "boolean", + "key": "includeUserDefinedAttributes", + "label": "Include User Defined Attributes default" }, { "type": "boolean", - "key": "writeVisibility", - "label": "Write Visibility" + "key": "includeUserDefinedAttributes_editable", + "label": "Include User Defined Attributes editable" + }, + { + "type": "text", + "key": "attr", + "label": "Attr default" }, { "type": "boolean", - "key": "writeUVSets", - "label": "Write UV Sets" + "key": "attr_editable", + "label": "Attr editable" + }, + { + "type": "text", + "key": "attrPrefix", + "label": "Attr Prefix default" }, { "type": "boolean", - "key": "writeCreases", - "label": "Write Creases" + "key": "attrPrefix_editable", + "label": "Attr Prefix editable" }, { - "type": "text", - "key": "dataFormat", - "label": "Data Format" + "type": "enum", + "key": "dataFormat", + "label": "Data Format default", + "enum_items": [ + { + "ogawa": "ogawa" + }, + { + "HDF": "HDF" + } + ] }, { - "type": "number", - "key": "step", - "label": "Step", - "minimum": 0.0, - "decimal": 4 + "type": "boolean", + "key": "dataFormat_editable", + "label": "Data Format editable" + }, + { + "type": "boolean", + "key": "eulerFilter", + "label": "Euler Filter default" }, { - "type": "text", - "key": "attr", - "label": "Attr" + "type": "boolean", + "key": "eulerFilter_editable", + "label": "Euler Filter editable" + }, + { + "type": "boolean", + "key": "noNormals", + "label": "No Normals default" + }, + { + "type": "boolean", + "key": "noNormals_editable", + "label": "No Normals editable" }, { - "type": "text", - "key": "attrPrefix", - "label": "Attr Prefix" + "type": "boolean", + "key": "preRoll", + "label": "Pre Roll default" }, { "type": "boolean", - "key": "stripNamespaces", - "label": "StripNamespaces" + "key": "preRoll_editable", + "label": "Pre Roll editable" + }, + { + "type": "number", + "key": "preRollStartFrame", + "label": "Pre Roll Start Frame default", + "minimum": 0 }, { "type": "boolean", - "key": "verbose", - "label": "Verbose" + "key": "preRollStartFrame_editable", + "label": "Pre Roll Start Frame editable" }, { - "type": "number", - "key": "preRollStartFrame", - "label": "Pre Roll Start Frame" + "type": "boolean", + "key": "refresh", + "label": "Refresh default" }, { "type": "boolean", - "key": "farm", - "label": "Farm" + "key": "refresh_editable", + "label": "Refresh editable" }, { - "type": "number", - "key": "priority", - "label": "Priority" + "type": "boolean", + "key": "stripNamespaces", + "label": "Strip Namespaces default" + }, + { + "type": "boolean", + "key": "stripNamespaces_editable", + "label": "Strip Namespaces editable" + }, + { + "type": "boolean", + "key": "uvWrite", + "label": "UV Write default" }, { "type": "boolean", - "key": "includeParentHierarchy", - "label": "Include Parent Hierarchy" + "key": "uvWrite_editable", + "label": "UV Write editable" + }, + { + "type": "boolean", + "key": "verbose", + "label": "Verbose default" }, { "type": "boolean", - "key": "refresh", - "label": "Refresh" + "key": "verbose_editable", + "label": "Verbose editable" + }, + { + "type": "boolean", + "key": "wholeFrameGeo", + "label": "Whole Frame Geo default" }, { "type": "boolean", - "key": "visibleOnly", - "label": "Visible Only" + "key": "wholeFrameGeo_editable", + "label": "Whole Frame Geo editable" }, { - "type": "list", - "key": "defaults", - "label": "Default Subsets", - "object_type": "text" + "type": "boolean", + "key": "writeCreases", + "label": "Write Creases default" + }, + { + "type": "boolean", + "key": "writeCreases_editable", + "label": "Write Creases editable" + }, + { + "type": "boolean", + "key": "writeUVSets", + "label": "Write UV Sets default" + }, + { + "type": "boolean", + "key": "writeUVSets_editable", + "label": "Write UV Sets editable" + }, + { + "type": "boolean", + "key": "writeVisibility", + "label": "Write Visibility default" + }, + { + "type": "boolean", + "key": "writeVisibility_editable", + "label": "Write Visibility editable" }, { "type": "boolean", From c1e771bedc3f3bd9f176042c924c0ccc10b6b103 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 14 Jul 2023 15:14:34 +0100 Subject: [PATCH 018/163] Revert post_imprint --- .../maya/plugins/publish/validate_instance_attributes.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py b/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py index 41bb6ed3bac..f870c9f8c45 100644 --- a/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_instance_attributes.py @@ -57,9 +57,4 @@ def process(self, instance): @classmethod def repair(cls, instance): - missing_attributes = cls.get_missing_attributes(instance) - imprint(instance.data["objset"], missing_attributes) - - plugin = cls.plugins_by_family[instance.data["family"]] - if hasattr(plugin, "post_imprint"): - plugin.post_imprint(plugin, instance.data["objset"]) + imprint(instance.data["objset"], cls.get_missing_attributes(instance)) From 98870357c7769491389f2ad6727e68d30ec3a075 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 14 Jul 2023 15:40:52 +0100 Subject: [PATCH 019/163] Combine CreateAnimation and CreatePointCache --- .../maya/plugins/create/create_animation.py | 87 ------------------- ...ache.py => create_animation_pointcache.py} | 39 +++++++-- 2 files changed, 32 insertions(+), 94 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/create/create_animation.py rename openpype/hosts/maya/plugins/create/{create_pointcache.py => create_animation_pointcache.py} (82%) diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py deleted file mode 100644 index cade8603ce3..00000000000 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ /dev/null @@ -1,87 +0,0 @@ -from openpype.hosts.maya.api import ( - lib, - plugin -) -from openpype.lib import ( - BoolDef, - TextDef -) - - -class CreateAnimation(plugin.MayaCreator): - """Animation output for character rigs""" - - # We hide the animation creator from the UI since the creation of it - # is automated upon loading a rig. There's an inventory action to recreate - # it for loaded rigs if by chance someone deleted the animation instance. - # Note: This setting is actually applied from project settings - enabled = False - - identifier = "io.openpype.creators.maya.animation" - name = "animationDefault" - label = "Animation" - family = "animation" - icon = "male" - - write_color_sets = False - write_face_sets = False - include_parent_hierarchy = False - include_user_defined_attributes = False - - # TODO: Would be great if we could visually hide this from the creator - # by default but do allow to generate it through code. - - def get_instance_attr_defs(self): - - defs = lib.collect_animation_defs() - - defs.extend([ - BoolDef("writeColorSets", - label="Write vertex colors", - tooltip="Write vertex colors with the geometry", - default=self.write_color_sets), - BoolDef("writeFaceSets", - label="Write face sets", - tooltip="Write face sets with the geometry", - default=self.write_face_sets), - BoolDef("writeNormals", - label="Write normals", - tooltip="Write normals with the deforming geometry", - default=True), - BoolDef("renderableOnly", - label="Renderable Only", - tooltip="Only export renderable visible shapes", - default=False), - BoolDef("visibleOnly", - label="Visible Only", - tooltip="Only export dag objects visible during " - "frame range", - default=False), - BoolDef("includeParentHierarchy", - label="Include Parent Hierarchy", - tooltip="Whether to include parent hierarchy of nodes in " - "the publish instance", - default=self.include_parent_hierarchy), - BoolDef("worldSpace", - label="World-Space Export", - default=True), - BoolDef("includeUserDefinedAttributes", - label="Include User Defined Attributes", - default=self.include_user_defined_attributes), - TextDef("attr", - label="Custom Attributes", - default="", - placeholder="attr1, attr2"), - TextDef("attrPrefix", - label="Custom Attributes Prefix", - placeholder="prefix1, prefix2") - ]) - - # TODO: Implement these on a Deadline plug-in instead? - """ - # Default to not send to farm. - self.data["farm"] = False - self.data["priority"] = 50 - """ - - return defs diff --git a/openpype/hosts/maya/plugins/create/create_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py similarity index 82% rename from openpype/hosts/maya/plugins/create/create_pointcache.py rename to openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 8cd96cc689d..d0d7cc9675f 100644 --- a/openpype/hosts/maya/plugins/create/create_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -13,17 +13,29 @@ from openpype.settings import get_current_project_settings#not needed if applying settings is merged. -class CreatePointCache(plugin.MayaCreator): - """Alembic pointcache for animated data""" +class CreateAnimation(plugin.MayaCreator): + """Animation output for character rigs""" + + # We hide the animation creator from the UI since the creation of it + # is automated upon loading a rig. There's an inventory action to recreate + # it for loaded rigs if by chance someone deleted the animation instance. + # Note: This setting is actually applied from project settings + enabled = False + + identifier = "io.openpype.creators.maya.animation" + name = "animationDefault" + label = "Animation" + family = "animation" + icon = "male" - identifier = "io.openpype.creators.maya.pointcache" - label = "Pointcache" - family = "pointcache" - icon = "gears" write_color_sets = False write_face_sets = False + include_parent_hierarchy = False include_user_defined_attributes = False + # TODO: Would be great if we could visually hide this from the creator + # by default but do allow to generate it through code. + def get_instance_attr_defs(self): defs = lib.collect_animation_defs() @@ -90,7 +102,7 @@ def get_instance_attr_defs(self): # Collect editable state and default values. settings = get_current_project_settings() - settings = settings["maya"]["create"]["CreatePointCache"] + settings = settings["maya"]["create"][self.__class__.__name__] editable_attributes = {} for key, value in settings.items(): if not key.endswith("_editable"): @@ -115,6 +127,19 @@ def get_instance_attr_defs(self): return resulting_defs + +class CreatePointCache(CreateAnimation): + """Alembic pointcache for animated data""" + + enabled = True + identifier = "io.openpype.creators.maya.pointcache" + label = "Pointcache" + family = "pointcache" + icon = "gears" + write_color_sets = False + write_face_sets = False + include_user_defined_attributes = False + def create(self, subset_name, instance_data, pre_create_data): instance = super(CreatePointCache, self).create( From eafe2d05a7e2c28367d458f83ba137de514c8813 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 22 Aug 2023 16:10:01 +0100 Subject: [PATCH 020/163] Working merge of develop --- .../maya/plugins/create/create_animation.py | 91 ------------------- .../create/create_animation_pointcache.py | 39 +++----- .../defaults/project_settings/maya.json | 8 +- 3 files changed, 14 insertions(+), 124 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/create/create_animation.py diff --git a/openpype/hosts/maya/plugins/create/create_animation.py b/openpype/hosts/maya/plugins/create/create_animation.py deleted file mode 100644 index 214ac18aef2..00000000000 --- a/openpype/hosts/maya/plugins/create/create_animation.py +++ /dev/null @@ -1,91 +0,0 @@ -from openpype.hosts.maya.api import ( - lib, - plugin -) -from openpype.lib import ( - BoolDef, - TextDef -) - - -class CreateAnimation(plugin.MayaHiddenCreator): - """Animation output for character rigs - - We hide the animation creator from the UI since the creation of it is - automated upon loading a rig. There's an inventory action to recreate it - for loaded rigs if by chance someone deleted the animation instance. - """ - identifier = "io.openpype.creators.maya.animation" - name = "animationDefault" - label = "Animation" - family = "animation" - icon = "male" - - write_color_sets = False - write_face_sets = False - include_parent_hierarchy = False - include_user_defined_attributes = False - - def get_instance_attr_defs(self): - - defs = lib.collect_animation_defs() - - defs.extend([ - BoolDef("writeColorSets", - label="Write vertex colors", - tooltip="Write vertex colors with the geometry", - default=self.write_color_sets), - BoolDef("writeFaceSets", - label="Write face sets", - tooltip="Write face sets with the geometry", - default=self.write_face_sets), - BoolDef("writeNormals", - label="Write normals", - tooltip="Write normals with the deforming geometry", - default=True), - BoolDef("renderableOnly", - label="Renderable Only", - tooltip="Only export renderable visible shapes", - default=False), - BoolDef("visibleOnly", - label="Visible Only", - tooltip="Only export dag objects visible during " - "frame range", - default=False), - BoolDef("includeParentHierarchy", - label="Include Parent Hierarchy", - tooltip="Whether to include parent hierarchy of nodes in " - "the publish instance", - default=self.include_parent_hierarchy), - BoolDef("worldSpace", - label="World-Space Export", - default=True), - BoolDef("includeUserDefinedAttributes", - label="Include User Defined Attributes", - default=self.include_user_defined_attributes), - TextDef("attr", - label="Custom Attributes", - default="", - placeholder="attr1, attr2"), - TextDef("attrPrefix", - label="Custom Attributes Prefix", - placeholder="prefix1, prefix2") - ]) - - # TODO: Implement these on a Deadline plug-in instead? - """ - # Default to not send to farm. - self.data["farm"] = False - self.data["priority"] = 50 - """ - - return defs - - def apply_settings(self, project_settings, system_settings): - super(CreateAnimation, self).apply_settings( - project_settings, system_settings - ) - # Hardcoding creator to be enabled due to existing settings would - # disable the creator causing the creator plugin to not be - # discoverable. - self.enabled = True diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index d0d7cc9675f..83f21eb80ff 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -10,18 +10,15 @@ NumberDef, EnumDef ) -from openpype.settings import get_current_project_settings#not needed if applying settings is merged. class CreateAnimation(plugin.MayaCreator): - """Animation output for character rigs""" - - # We hide the animation creator from the UI since the creation of it - # is automated upon loading a rig. There's an inventory action to recreate - # it for loaded rigs if by chance someone deleted the animation instance. - # Note: This setting is actually applied from project settings - enabled = False + """Animation output for character rigs + We hide the animation creator from the UI since the creation of it is + automated upon loading a rig. There's an inventory action to recreate it + for loaded rigs if by chance someone deleted the animation instance. + """ identifier = "io.openpype.creators.maya.animation" name = "animationDefault" label = "Animation" @@ -33,9 +30,6 @@ class CreateAnimation(plugin.MayaCreator): include_parent_hierarchy = False include_user_defined_attributes = False - # TODO: Would be great if we could visually hide this from the creator - # by default but do allow to generate it through code. - def get_instance_attr_defs(self): defs = lib.collect_animation_defs() @@ -101,27 +95,20 @@ def get_instance_attr_defs(self): ]) # Collect editable state and default values. - settings = get_current_project_settings() - settings = settings["maya"]["create"][self.__class__.__name__] - editable_attributes = {} - for key, value in settings.items(): - if not key.endswith("_editable"): - continue - - if not value: - continue - - attribute = key.replace("_editable", "") - editable_attributes[attribute] = settings[attribute] - resulting_defs = [] for definition in defs: + # Include by default any attributes which has no editable state + # from settings. + if not hasattr(self, definition.key + "_editable"): + resulting_defs.append(definition) + continue + # Remove non-editable defs. - if definition.key not in editable_attributes.keys(): + if not getattr(self, definition.key + "_editable"): continue # Set default values from settings. - definition.default = editable_attributes[definition.key] + definition.default = getattr(self, definition.key) resulting_defs.append(definition) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 161fac00449..90b4574f15b 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -557,10 +557,7 @@ "publish_mip_map": true }, "CreateAnimation": { - "enabled": false, - "defaults": [ - "Main" - ], + "default_variants": [], "step": 1.0, "step_editable": true, "writeColorSets": false, @@ -630,9 +627,6 @@ }, "CreatePointCache": { "enabled": true, - "write_color_sets": false, - "write_face_sets": false, - "include_user_defined_attributes": false, "default_variants": [ "Main" ], From 7d1f0098d4aa19b584fb113ed7236ea51a0ca8b8 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 22 Aug 2023 16:54:58 +0100 Subject: [PATCH 021/163] Working hidden animation creator --- openpype/hosts/maya/api/plugin.py | 47 +++-- .../create/create_animation_pointcache.py | 177 +++++++++--------- 2 files changed, 124 insertions(+), 100 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index 00d6602ef92..ecbbece2204 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -65,6 +65,25 @@ def get_reference_node_parents(*args, **kwargs): return lib.get_reference_node_parents(*args, **kwargs) +def apply_settings(cls, project_settings, system_settings): + """Method called on initialization of plugin to apply settings.""" + + settings_name = cls.settings_name + if settings_name is None: + settings_name = cls.__class__.__name__ + + settings = project_settings["maya"]["create"] + settings = settings.get(settings_name) + if settings is None: + cls.log.debug( + "No settings found for {}".format(cls.__class__.__name__) + ) + return + + for key, value in settings.items(): + setattr(cls, key, value) + + class Creator(LegacyCreator): defaults = ['Main'] @@ -262,21 +281,7 @@ def get_pre_create_attr_defs(self): def apply_settings(self, project_settings, system_settings): """Method called on initialization of plugin to apply settings.""" - - settings_name = self.settings_name - if settings_name is None: - settings_name = self.__class__.__name__ - - settings = project_settings["maya"]["create"] - settings = settings.get(settings_name) - if settings is None: - self.log.debug( - "No settings found for {}".format(self.__class__.__name__) - ) - return - - for key, value in settings.items(): - setattr(self, key, value) + apply_settings(self, project_settings, system_settings) class MayaAutoCreator(AutoCreator, MayaCreatorBase): @@ -286,6 +291,8 @@ class MayaAutoCreator(AutoCreator, MayaCreatorBase): any arguments. """ + settings_name = None + def collect_instances(self): return self._default_collect_instances() @@ -295,6 +302,10 @@ def update_instances(self, update_list): def remove_instances(self, instances): return self._default_remove_instances(instances) + def apply_settings(self, project_settings, system_settings): + """Method called on initialization of plugin to apply settings.""" + apply_settings(self, project_settings, system_settings) + class MayaHiddenCreator(HiddenCreator, MayaCreatorBase): """Hidden creator for Maya. @@ -303,6 +314,8 @@ class MayaHiddenCreator(HiddenCreator, MayaCreatorBase): arguments for 'create' method. """ + settings_name = None + def create(self, *args, **kwargs): return MayaCreator.create(self, *args, **kwargs) @@ -315,6 +328,10 @@ def update_instances(self, update_list): def remove_instances(self, instances): return self._default_remove_instances(instances) + def apply_settings(self, project_settings, system_settings): + """Method called on initialization of plugin to apply settings.""" + apply_settings(self, project_settings, system_settings) + def ensure_namespace(namespace): """Make sure the namespace exists. diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 83f21eb80ff..d9fd0fff864 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -12,7 +12,93 @@ ) -class CreateAnimation(plugin.MayaCreator): +def get_instance_attr_defs(cls): + defs = lib.collect_animation_defs() + + defs.extend([ + BoolDef("writeColorSets", + label="Write vertex colors", + tooltip="Write vertex colors with the geometry"), + BoolDef("writeFaceSets", + label="Write face sets", + tooltip="Write face sets with the geometry"), + BoolDef("renderableOnly", + label="Renderable Only", + tooltip="Only export renderable visible shapes"), + BoolDef("visibleOnly", + label="Visible Only", + tooltip="Only export dag objects visible during " + "frame range"), + BoolDef("includeParentHierarchy", + label="Include Parent Hierarchy", + tooltip="Whether to include parent hierarchy of nodes in " + "the publish instance"), + BoolDef("worldSpace", + label="World-Space Export"), + BoolDef("farm", + label="Submit to farm"), + NumberDef("priority", + label="Priority for farm"), + BoolDef("noNormals", + label="Include normals"), + BoolDef("includeUserDefinedAttributes", + label="Include User Defined Attributes"), + TextDef("attr", + label="Custom Attributes", + placeholder="attr1, attr2"), + TextDef("attrPrefix", + label="Custom Attributes Prefix", + placeholder="prefix1, prefix2"), + EnumDef("dataFormat", + label="Data Format", + items=["ogawa", "HDF"]), + BoolDef("eulerFilter", + label="Apply Euler Filter"), + BoolDef("preRoll", + label="Start from preroll start frame"), + NumberDef("preRollStartFrame", + label="Start frame for preroll"), + BoolDef("refresh", + label="Refresh viewport during export"), + BoolDef("stripNamespaces", + label="Strip namespaces on export"), + BoolDef("uvWrite", + label="Write UVs"), + BoolDef("verbose", + label="Verbose output"), + BoolDef("wholeFrameGeo", + label="Whole Frame Geo"), + BoolDef("writeCreases", + label="Write Creases"), + BoolDef("writeUVSets", + label="Write UV Sets"), + BoolDef("writeVisibility", + label="Write Visibility") + ]) + + # Collect editable state and default values. + resulting_defs = [] + for definition in defs: + # Include by default any attributes which has no editable state + # from settings. + if not hasattr(cls, definition.key + "_editable"): + print("{} was not found.".format(definition.key + "_editable")) + resulting_defs.append(definition) + continue + + # Remove non-editable defs. + if not getattr(cls, definition.key + "_editable"): + continue + + # Set default values from settings. + definition.default = getattr(cls, definition.key) + + resulting_defs.append(definition) + + return resulting_defs + + +class CreateAnimation(plugin.MayaHiddenCreator): """Animation output for character rigs We hide the animation creator from the UI since the creation of it is @@ -31,94 +117,12 @@ class CreateAnimation(plugin.MayaCreator): include_user_defined_attributes = False def get_instance_attr_defs(self): - defs = lib.collect_animation_defs() - - defs.extend([ - BoolDef("writeColorSets", - label="Write vertex colors", - tooltip="Write vertex colors with the geometry"), - BoolDef("writeFaceSets", - label="Write face sets", - tooltip="Write face sets with the geometry"), - BoolDef("renderableOnly", - label="Renderable Only", - tooltip="Only export renderable visible shapes"), - BoolDef("visibleOnly", - label="Visible Only", - tooltip="Only export dag objects visible during " - "frame range"), - BoolDef("includeParentHierarchy", - label="Include Parent Hierarchy", - tooltip="Whether to include parent hierarchy of nodes in " - "the publish instance"), - BoolDef("worldSpace", - label="World-Space Export"), - BoolDef("farm", - label="Submit to farm"), - NumberDef("priority", - label="Priority for farm"), - BoolDef("noNormals", - label="Include normals"), - BoolDef("includeUserDefinedAttributes", - label="Include User Defined Attributes"), - TextDef("attr", - label="Custom Attributes", - placeholder="attr1, attr2"), - TextDef("attrPrefix", - label="Custom Attributes Prefix", - placeholder="prefix1, prefix2"), - EnumDef("dataFormat", - label="Data Format", - items=["ogawa", "HDF"]), - BoolDef("eulerFilter", - label="Apply Euler Filter"), - BoolDef("preRoll", - label="Start from preroll start frame"), - NumberDef("preRollStartFrame", - label="Start frame for preroll"), - BoolDef("refresh", - label="Refresh viewport during export"), - BoolDef("stripNamespaces", - label="Strip namespaces on export"), - BoolDef("uvWrite", - label="Write UVs"), - BoolDef("verbose", - label="Verbose output"), - BoolDef("wholeFrameGeo", - label="Whole Frame Geo"), - BoolDef("writeCreases", - label="Write Creases"), - BoolDef("writeUVSets", - label="Write UV Sets"), - BoolDef("writeVisibility", - label="Write Visibility") - ]) - - # Collect editable state and default values. - resulting_defs = [] - for definition in defs: - # Include by default any attributes which has no editable state - # from settings. - if not hasattr(self, definition.key + "_editable"): - resulting_defs.append(definition) - continue - - # Remove non-editable defs. - if not getattr(self, definition.key + "_editable"): - continue - - # Set default values from settings. - definition.default = getattr(self, definition.key) - - resulting_defs.append(definition) + return get_instance_attr_defs(self) - return resulting_defs - -class CreatePointCache(CreateAnimation): +class CreatePointCache(plugin.MayaCreator): """Alembic pointcache for animated data""" - enabled = True identifier = "io.openpype.creators.maya.pointcache" label = "Pointcache" family = "pointcache" @@ -127,6 +131,9 @@ class CreatePointCache(CreateAnimation): write_face_sets = False include_user_defined_attributes = False + def get_instance_attr_defs(self): + return get_instance_attr_defs(self) + def create(self, subset_name, instance_data, pre_create_data): instance = super(CreatePointCache, self).create( From fcf1c1ec8a5e668014f1e16cc1073952631f1b80 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 1 Sep 2023 10:07:15 +0100 Subject: [PATCH 022/163] Fix writeNormals --- .../maya/plugins/create/create_animation_pointcache.py | 2 ++ openpype/hosts/maya/plugins/publish/collect_animation.py | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index d9fd0fff864..f8577b1e879 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -41,6 +41,8 @@ def get_instance_attr_defs(cls): label="Priority for farm"), BoolDef("noNormals", label="Include normals"), + BoolDef("writeNormals", + label="Write Normals"), BoolDef("includeUserDefinedAttributes", label="Include User Defined Attributes"), TextDef("attr", diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index 94b732cc4de..25747c7a453 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -60,7 +60,11 @@ def process(self, instance): # Alembic and Multiverse share the same attribute functionality but # different names. - instance.data["writeNormals"] = not instance.data["noNormals"] + instance.data["writeNormals"] = ( + instance.data.get("writeNormals") or + not instance.data.get("noNormals") or + False + ) # Backwards compatibility for attributes. backwards_mapping = { From ee8b2302735775be59432ee705f731bcc29d6747 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 1 Sep 2023 10:07:29 +0100 Subject: [PATCH 023/163] Remove redundant code. --- openpype/hosts/maya/plugins/publish/collect_animation.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index 25747c7a453..46facffce03 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -82,12 +82,6 @@ def process(self, instance): user_defined_attributes = set() for node in hierarchy: attrs = cmds.listAttr(node, userDefined=True) or list() - shapes = cmds.listRelatives(node, shapes=True) or list() - for shape in shapes: - attrs.extend( - cmds.listAttr(shape, userDefined=True) or list() - ) - user_defined_attributes.update(attrs) instance.data["userDefinedAttributes"] = list( From de5c1ceb50ca59ce4e4d2254ab734c428e013aec Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 1 Sep 2023 14:19:35 +0100 Subject: [PATCH 024/163] Add tooltip to preRollStartFrame --- .../plugins/create/create_animation_pointcache.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index f8577b1e879..6a9a8caa037 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -58,8 +58,15 @@ def get_instance_attr_defs(cls): label="Apply Euler Filter"), BoolDef("preRoll", label="Start from preroll start frame"), - NumberDef("preRollStartFrame", - label="Start frame for preroll"), + NumberDef( + "preRollStartFrame", + label="Start frame for preroll", + tooltip=( + "The frame to start scene evaluation at. This is used to set" + " the starting frame for time dependent translations and can" + " be used to evaluate run-up that isn't actually translated." + ) + ), BoolDef("refresh", label="Refresh viewport during export"), BoolDef("stripNamespaces", From 7f152b9175835b71a4dc9385e7b9a88f353be92c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 18 Sep 2023 10:25:01 +0100 Subject: [PATCH 025/163] Sync with develop plugin.py --- openpype/hosts/maya/api/plugin.py | 47 ++++++++++--------------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/openpype/hosts/maya/api/plugin.py b/openpype/hosts/maya/api/plugin.py index b9591e60b24..4032618afbf 100644 --- a/openpype/hosts/maya/api/plugin.py +++ b/openpype/hosts/maya/api/plugin.py @@ -65,25 +65,6 @@ def get_reference_node_parents(*args, **kwargs): return lib.get_reference_node_parents(*args, **kwargs) -def apply_settings(cls, project_settings, system_settings): - """Method called on initialization of plugin to apply settings.""" - - settings_name = cls.settings_name - if settings_name is None: - settings_name = cls.__class__.__name__ - - settings = project_settings["maya"]["create"] - settings = settings.get(settings_name) - if settings is None: - cls.log.debug( - "No settings found for {}".format(cls.__class__.__name__) - ) - return - - for key, value in settings.items(): - setattr(cls, key, value) - - class Creator(LegacyCreator): defaults = ['Main'] @@ -281,7 +262,21 @@ def get_pre_create_attr_defs(self): def apply_settings(self, project_settings): """Method called on initialization of plugin to apply settings.""" - apply_settings(self, project_settings, system_settings) + + settings_name = self.settings_name + if settings_name is None: + settings_name = self.__class__.__name__ + + settings = project_settings["maya"]["create"] + settings = settings.get(settings_name) + if settings is None: + self.log.debug( + "No settings found for {}".format(self.__class__.__name__) + ) + return + + for key, value in settings.items(): + setattr(self, key, value) class MayaAutoCreator(AutoCreator, MayaCreatorBase): @@ -291,8 +286,6 @@ class MayaAutoCreator(AutoCreator, MayaCreatorBase): any arguments. """ - settings_name = None - def collect_instances(self): return self._default_collect_instances() @@ -302,10 +295,6 @@ def update_instances(self, update_list): def remove_instances(self, instances): return self._default_remove_instances(instances) - def apply_settings(self, project_settings, system_settings): - """Method called on initialization of plugin to apply settings.""" - apply_settings(self, project_settings, system_settings) - class MayaHiddenCreator(HiddenCreator, MayaCreatorBase): """Hidden creator for Maya. @@ -314,8 +303,6 @@ class MayaHiddenCreator(HiddenCreator, MayaCreatorBase): arguments for 'create' method. """ - settings_name = None - def create(self, *args, **kwargs): return MayaCreator.create(self, *args, **kwargs) @@ -328,10 +315,6 @@ def update_instances(self, update_list): def remove_instances(self, instances): return self._default_remove_instances(instances) - def apply_settings(self, project_settings, system_settings): - """Method called on initialization of plugin to apply settings.""" - apply_settings(self, project_settings, system_settings) - def ensure_namespace(namespace): """Make sure the namespace exists. From bb8b4c1881056d0cf052bea89757de83dbccb432 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Mon, 13 Nov 2023 17:55:26 +0000 Subject: [PATCH 026/163] `maya` Define Alembic attributes as enums This commit converts most of the Alembic attributes into an EnumDef. A new setting (also an enum) allows the person to select which of the Alembic attributes are modifiable by the person at publish time. --- .../create/create_animation_pointcache.py | 254 ++++++--- .../defaults/project_settings/maya.json | 108 ++-- .../schemas/schema_maya_create.json | 494 ++++-------------- 3 files changed, 319 insertions(+), 537 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 658c10ae628..b91994c65d9 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -1,5 +1,7 @@ from maya import cmds +from pprint import pprint + from openpype.hosts.maya.api import ( lib, plugin @@ -8,103 +10,173 @@ BoolDef, TextDef, NumberDef, - EnumDef + EnumDef, + UISeparatorDef, + UILabelDef ) - -def get_instance_attr_defs(cls): +def _get_animation_attr_defs(cls): + """Get Animation generic ddefinitions. + """ defs = lib.collect_animation_defs() - defs.extend([ - BoolDef("writeColorSets", - label="Write vertex colors", - tooltip="Write vertex colors with the geometry"), - BoolDef("writeFaceSets", - label="Write face sets", - tooltip="Write face sets with the geometry"), - BoolDef("renderableOnly", - label="Renderable Only", - tooltip="Only export renderable visible shapes"), - BoolDef("visibleOnly", - label="Visible Only", - tooltip="Only export dag objects visible during " - "frame range"), - BoolDef("includeParentHierarchy", - label="Include Parent Hierarchy", - tooltip="Whether to include parent hierarchy of nodes in " - "the publish instance"), - BoolDef("worldSpace", - label="World-Space Export"), - BoolDef("farm", - label="Submit to farm"), - NumberDef("priority", - label="Priority for farm"), - BoolDef("noNormals", - label="Include normals"), - BoolDef("writeNormals", - label="Write Normals"), - BoolDef("includeUserDefinedAttributes", - label="Include User Defined Attributes"), - TextDef("attr", - label="Custom Attributes", - placeholder="attr1, attr2"), - TextDef("attrPrefix", - label="Custom Attributes Prefix", - placeholder="prefix1, prefix2"), - EnumDef("dataFormat", - label="Data Format", - items=["ogawa", "HDF"]), - BoolDef("eulerFilter", - label="Apply Euler Filter"), - BoolDef("preRoll", - label="Start from preroll start frame"), - NumberDef( + BoolDef("farm", label="Submit to Farm"), + NumberDef("priority", label="Farm job Priority", default=50), + BoolDef("refresh", label="Refresh viewport during export"), + BoolDef("includeParentHierarchy", label="Include Parent Hierarchy"), + BoolDef("writeNormals", label="Write Normals"), + BoolDef("writeCreases", label="Write Creases") + ]) + + return defs + +def _get_animation_abc_attr_defs(cls): + """ Get definitions relating to Alembic. + """ + # List of arguments extracted from AbcExport -h + # Them being here doesn't imply we support them or that we need them at this + # point, it's a convininece list to populate the UI defaults. + alembic_attributes = [ + "preRollStartFrame", + "dontSkipUnwrittenFrames", + "verbose", + "attr", + "autoSubd", + "attrPrefix", + "dataFormat", + "eulerFilter", + "frameRange", + "frameRelativeSample", + "noNormals", + "preRoll", + "renderableOnly", + "root", + "step", + "selection", + "stripNamespaces", + "userAttr", + "userAttrPrefix", + "uvWrite", + "uvsOnly", + "writeColorSets", + "writeFaceSets", + "wholeFrameGeo", + "worldSpace", + "writeVisibility", + "writeUVSets", + "melPerFrameCallback", + "melPostJobCallback", + "pythonPerFrameCallback", + "pythonPostJobCallback" + ] + + abc_defs = [ + UISeparatorDef(), + UILabelDef("Alembic Options") + ] + + print("Processing editable Alembic attributes...") + alembic_editable_attributes = getattr(cls, "abc_editable_flags", None) + + if not alembic_editable_attributes: + return None + + print(alembic_editable_attributes) + + abc_boolean_defs = [ + "writeColorSets", + "writeFaceSets", + "renderableOnly", + "visibleOnly", + "worldSpace", + "noNormals", + "includeUserDefinedAttributes", + "eulerFilter", + "preRoll", + "stripNamespaces", + "uvWrite", + "verbose", + "wholeFrameGeo", + "writeUVSets", + "writeVisibility", + ] + + abc_boolean_defaults = [ + "uvWrite", + "worldSpace", + "writeVisibility", + ] + + enabled_boolean_attributes = [ + attrib + for attrib in abc_boolean_defs + if attrib in alembic_editable_attributes + ] + + if enabled_boolean_attributes: + abc_defs.extend([EnumDef( + "abcExportFlags", + enabled_boolean_attributes, + default=abc_boolean_defaults, + multiselection=True, + label="Alembic Export Flags" + )]) + + + abc_defs.append(TextDef( + "attr", + label="Alembic Custom Attributes", + placeholder="attr1, attr2", + disabled=True if "attr" not in alembic_editable_attributes else False + )) + + abc_defs.append(TextDef( + "attrPrefix", + label="Alembic Custom Attributes Prefix", + placeholder="prefix1, prefix2", + disabled=True if "attrPrefix" not in alembic_editable_attributes else False + )) + + abc_defs.append(EnumDef( + "dataFormat", + label="Alembic Data Format", + items=["ogawa", "HDF"], + disabled=True if "dataFormat" not in alembic_editable_attributes else False + )) + + abc_defs.append(NumberDef( "preRollStartFrame", - label="Start frame for preroll", + label="Start frame for preroll (Alembic)", tooltip=( "The frame to start scene evaluation at. This is used to set" " the starting frame for time dependent translations and can" " be used to evaluate run-up that isn't actually translated." - ) - ), - BoolDef("refresh", - label="Refresh viewport during export"), - BoolDef("stripNamespaces", - label="Strip namespaces on export"), - BoolDef("uvWrite", - label="Write UVs"), - BoolDef("verbose", - label="Verbose output"), - BoolDef("wholeFrameGeo", - label="Whole Frame Geo"), - BoolDef("writeCreases", - label="Write Creases"), - BoolDef("writeUVSets", - label="Write UV Sets"), - BoolDef("writeVisibility", - label="Write Visibility") - ]) + ), + disabled=True if "preRollStartFrame" not in alembic_editable_attributes else False + )) + #['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_add_instance_to_context', '_cached_group_label', '_default_collect_instances', '_default_remove_instances', '_default_update_instances', '_log', '_remove_instance_from_context', 'apply_settings', 'cache_subsets', 'collect_instances', 'collection_shared_data', 'create', 'create_context', 'enabled', 'family', 'get_dynamic_data', 'get_group_label', 'get_icon', 'get_instance_attr_defs', 'get_next_versions_for_instances', 'get_publish_families', 'get_subset_name', 'group_label', 'headless', 'host', 'host_name', 'icon', 'identifier', 'imprint_instance_node', 'include_parent_hierarchy', 'include_user_defined_attributes', 'instance_attr_defs', 'label', 'log', 'name', 'order', 'project_anatomy', 'project_name', 'project_settings', 'read_instance_node', 'remove_instances', 'set_instance_thumbnail_path', 'update_instances', 'write_color_sets', 'write_face_sets'] + #for # Collect editable state and default values. - resulting_defs = [] - for definition in defs: - # Include by default any attributes which has no editable state - # from settings. - if not hasattr(cls, definition.key + "_editable"): - print("{} was not found.".format(definition.key + "_editable")) - resulting_defs.append(definition) - continue + # resulting_defs = [] + # for definition in defs: + # # Include by default any attributes which has no editable state + # # from settings. + # if not hasattr(cls, definition.key + "_editable"): + # print("{} was not found.".format(definition.key + "_editable")) + # resulting_defs.append(definition) + # continue - # Remove non-editable defs. - if not getattr(cls, definition.key + "_editable"): - continue + # # Remove non-editable defs. + # if not getattr(cls, definition.key + "_editable"): + # continue - # Set default values from settings. - definition.default = getattr(cls, definition.key) + # # Set default values from settings. + # definition.default = getattr(cls, definition.key) - resulting_defs.append(definition) + # resulting_defs.append(definition) - return resulting_defs + return abc_defs class CreateAnimation(plugin.MayaHiddenCreator): @@ -126,7 +198,14 @@ class CreateAnimation(plugin.MayaHiddenCreator): include_user_defined_attributes = False def get_instance_attr_defs(self): - return get_instance_attr_defs(self) + defs = _get_animation_attr_defs(self) + + abc_defs = _get_animation_abc_attr_defs(self) + + if abc_defs: + defs.extend(abc_defs) + + return defs def apply_settings(self, project_settings): super(CreateAnimation, self).apply_settings(project_settings) @@ -144,7 +223,14 @@ class CreatePointCache(plugin.MayaCreator): include_user_defined_attributes = False def get_instance_attr_defs(self): - return get_instance_attr_defs(self) + defs = _get_animation_attr_defs(self) + + abc_defs = _get_animation_abc_attr_defs(self) + + if abc_defs: + defs.extend(abc_defs) + + return defs def create(self, subset_name, instance_data, pre_create_data): diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index d993365a8aa..aaa7afdf45f 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -559,57 +559,34 @@ "CreateAnimation": { "default_variants": [], "step": 1.0, - "step_editable": true, - "writeColorSets": false, - "writeColorSets_editable": true, - "writeFaceSets": false, - "writeFaceSets_editable": true, - "renderableOnly": false, - "renderableOnly_editable": true, - "visibleOnly": false, - "visibleOnly_editable": true, + "abc_flags": [ + "writeColorSets", + "visibleOnly", + "worldSpace", + "writeNormals", + "includeUserDefinedAttributes", + "attr", + "attrPrefix" + ], + "abc_editable_flags": [ + "step", + "includeParentHierarchy", + "writeNormals", + "includeUserDefinedAttributes", + "attr", + "attrPrefix" + ], "includeParentHierarchy": false, - "includeParentHierarchy_editable": true, - "worldSpace": true, - "worldSpace_editable": true, "farm": false, - "farm_editable": true, "priority": 50, - "priority_editable": true, "writeNormals": true, - "writeNormals_editable": true, - "includeUserDefinedAttributes": true, - "includeUserDefinedAttributes_editable": true, "attr": "", - "attr_editable": true, "attrPrefix": "", - "attrPrefix_editable": true, "dataFormat": "ogawa", - "dataFormat_editable": false, - "eulerFilter": false, - "eulerFilter_editable": false, - "noNormals": false, - "noNormals_editable": false, - "preRoll": false, - "preRoll_editable": false, "preRollStartFrame": 0, - "preRollStartFrame_editable": false, "refresh": false, - "refresh_editable": false, "stripNamespaces": false, - "stripNamespaces_editable": false, - "uvWrite": true, - "uvWrite_editable": false, - "verbose": false, - "verbose_editable": false, - "wholeFrameGeo": false, - "wholeFrameGeo_editable": false, "writeCreases": false, - "writeCreases_editable": false, - "writeUVSets": false, - "writeUVSets_editable": false, - "writeVisibility": true, - "writeVisibility_editable": false, "write_color_sets": false, "write_face_sets": false, "include_parent_hierarchy": false, @@ -631,57 +608,44 @@ "Main" ], "step": 1.0, - "step_editable": true, + "abc_flags": [ + "writeColorSets", + "visibleOnly", + "worldSpace", + "writeNormals", + "includeUserDefinedAttributes", + "attr", + "attrPrefix" + ], + "abc_editable_flags": [ + "step", + "writeColorSets", + "writeFaceSets", + "renderableOnly", + "visibleOnly", + "worldSpace", + "attr", + "attrPrefix" + ], "writeColorSets": true, - "writeColorSets_editable": true, "writeFaceSets": false, - "writeFaceSets_editable": true, "renderableOnly": false, - "renderableOnly_editable": true, "visibleOnly": false, - "visibleOnly_editable": true, "includeParentHierarchy": false, - "includeParentHierarchy_editable": true, "worldSpace": true, - "worldSpace_editable": true, "farm": false, - "farm_editable": true, "priority": 50, - "priority_editable": true, "writeNormals": true, - "writeNormals_editable": true, "includeUserDefinedAttributes": true, - "includeUserDefinedAttributes_editable": true, "attr": "", - "attr_editable": true, "attrPrefix": "", - "attrPrefix_editable": true, "dataFormat": "ogawa", - "dataFormat_editable": false, - "eulerFilter": false, - "eulerFilter_editable": false, "noNormals": false, - "noNormals_editable": false, - "preRoll": false, - "preRoll_editable": false, "preRollStartFrame": 0, - "preRollStartFrame_editable": false, "refresh": false, - "refresh_editable": false, "stripNamespaces": false, - "stripNamespaces_editable": false, "uvWrite": true, - "uvWrite_editable": false, - "verbose": false, - "verbose_editable": false, - "wholeFrameGeo": false, - "wholeFrameGeo_editable": false, "writeCreases": false, - "writeCreases_editable": false, - "writeUVSets": false, - "writeUVSets_editable": false, - "writeVisibility": true, - "writeVisibility_editable": false, "write_color_sets": false, "write_face_sets": false, "include_user_defined_attributes": false diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index 705ddfa1ff3..c5bfb18e008 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -137,143 +137,109 @@ "label": "Default Variants", "object_type": "text" }, - { - "type": "number", - "key": "step", - "label": "Step default", - "minimum": 0.0, - "decimal": 4 - }, - { - "type": "boolean", - "key": "step_editable", - "label": "Step editable" - }, - { - "type": "boolean", - "key": "writeColorSets", - "label": "Write Color Sets default" - }, - { - "type": "boolean", - "key": "writeColorSets_editable", - "label": "Write Color Sets editable" - }, - { - "type": "boolean", - "key": "writeFaceSets", - "label": "Write Face Sets default" - }, - { - "type": "boolean", - "key": "writeFaceSets_editable", - "label": "Write Face Sets editable" - }, - { - "type": "boolean", - "key": "renderableOnly", - "label": "Renderable Only default" - }, - { - "type": "boolean", - "key": "renderableOnly_editable", - "label": "Renderable Only editable" - }, - { - "type": "boolean", - "key": "visibleOnly", - "label": "Visible Only default" - }, - { - "type": "boolean", - "key": "visibleOnly_editable", - "label": "Visible Only editable" - }, { "type": "boolean", "key": "includeParentHierarchy", - "label": "Include Parent Hierarchy default" - }, - { - "type": "boolean", - "key": "includeParentHierarchy_editable", - "label": "Include Parent Hierarchy editable" - }, - { - "type": "boolean", - "key": "worldSpace", - "label": "World Space default" - }, - { - "type": "boolean", - "key": "worldSpace_editable", - "label": "World Space editable" + "label": "Include Parent Hierarchy" }, { "type": "boolean", "key": "farm", - "label": "Farm default" - }, - { - "type": "boolean", - "key": "farm_editable", - "label": "Farm editable" + "label": "Submit to the Farm" }, { "type": "number", "key": "priority", - "label": "Priority default", + "label": "Farm Job Priority", "minimum": 0 }, { - "type": "boolean", - "key": "priority_editable", - "label": "Priority editable" + "type": "boolean", + "key": "writeNormals", + "label": "Write Normals" }, { "type": "boolean", - "key": "writeNormals", - "label": "Write Normals default" + "key": "refresh", + "label": "Refresh" }, { - "type": "boolean", - "key": "writeNormals_editable", - "label": "Write Normals editable" + "type": "boolean", + "key": "stripNamespaces", + "label": "Strip Namespaces" }, { "type": "boolean", - "key": "includeUserDefinedAttributes", - "label": "Include User Defined Attributes default" + "key": "writeCreases", + "label": "Write Creases" }, { - "type": "boolean", - "key": "includeUserDefinedAttributes_editable", - "label": "Include User Defined Attributes editable" + "type": "enum", + "key": "abc_flags", + "multiselection": true, + "label": "Alembic Flags", + "enum_items": [ + {"writeColorSets": "writeColorSets"}, + {"writeFaceSets": "writeFaceSets"}, + {"renderableOnly": "renderableOnly"}, + {"visibleOnly": "visibleOnly"}, + {"worldSpace": "worldSpace"}, + {"noNormals": "noNormals"}, + {"eulerFilter": "eulerFilter"}, + {"preRoll": "preRoll"}, + {"uvWrite": "uvWrite"}, + {"verbose": "verbose"}, + {"wholeFrameGeo": "wholeFrameGeo"}, + {"writeUVSets": "writeUVSets"}, + {"writeVisibility": "writeVisibility"} + ] }, { - "type": "text", - "key": "attr", - "label": "Attr default" + "type": "enum", + "key": "abc_editable_flags", + "multiselection": true, + "label": "Alembic Editable Flags", + "enum_items": [ + {"step": "step"}, + {"writeColorSets": "writeColorSets"}, + {"writeFaceSets": "writeFaceSets"}, + {"renderableOnly": "renderableOnly"}, + {"visibleOnly": "visibleOnly"}, + {"worldSpace": "worldSpace"}, + {"noNormals": "noNormals"}, + {"attr": "attr"}, + {"attrPrefix": "attrPrefix"}, + {"eulerFilter": "eulerFilter"}, + {"preRoll": "preRoll"}, + {"preRollStartFrame": "preRollStartFrame"}, + {"uvWrite": "uvWrite"}, + {"verbose": "verbose"}, + {"wholeFrameGeo": "wholeFrameGeo"}, + {"writeUVSets": "writeUVSets"}, + {"writeVisibility": "writeVisibility"} + ] }, { - "type": "boolean", - "key": "attr_editable", - "label": "Attr editable" + "type": "number", + "key": "step", + "label": "Step", + "minimum": 0.0, + "decimal": 4 }, { "type": "text", - "key": "attrPrefix", - "label": "Attr Prefix default" + "key": "attr", + "label": "Attr" }, { - "type": "boolean", - "key": "attrPrefix_editable", - "label": "Attr Prefix editable" + "type": "text", + "key": "attrPrefix", + "label": "Attr Prefix" }, { "type": "enum", "key": "dataFormat", - "label": "Data Format default", + "label": "Data Format", "enum_items": [ { "ogawa": "ogawa" @@ -283,132 +249,12 @@ } ] }, - { - "type": "boolean", - "key": "dataFormat_editable", - "label": "Data Format editable" - }, - { - "type": "boolean", - "key": "eulerFilter", - "label": "Euler Filter default" - }, - { - "type": "boolean", - "key": "eulerFilter_editable", - "label": "Euler Filter editable" - }, - { - "type": "boolean", - "key": "noNormals", - "label": "No Normals default" - }, - { - "type": "boolean", - "key": "noNormals_editable", - "label": "No Normals editable" - }, - { - "type": "boolean", - "key": "preRoll", - "label": "Pre Roll default" - }, - { - "type": "boolean", - "key": "preRoll_editable", - "label": "Pre Roll editable" - }, { "type": "number", "key": "preRollStartFrame", - "label": "Pre Roll Start Frame default", + "label": "Pre Roll Start Frame", "minimum": 0 }, - { - "type": "boolean", - "key": "preRollStartFrame_editable", - "label": "Pre Roll Start Frame editable" - }, - { - "type": "boolean", - "key": "refresh", - "label": "Refresh default" - }, - { - "type": "boolean", - "key": "refresh_editable", - "label": "Refresh editable" - }, - { - "type": "boolean", - "key": "stripNamespaces", - "label": "Strip Namespaces default" - }, - { - "type": "boolean", - "key": "stripNamespaces_editable", - "label": "Strip Namespaces editable" - }, - { - "type": "boolean", - "key": "uvWrite", - "label": "UV Write default" - }, - { - "type": "boolean", - "key": "uvWrite_editable", - "label": "UV Write editable" - }, - { - "type": "boolean", - "key": "verbose", - "label": "Verbose default" - }, - { - "type": "boolean", - "key": "verbose_editable", - "label": "Verbose editable" - }, - { - "type": "boolean", - "key": "wholeFrameGeo", - "label": "Whole Frame Geo default" - }, - { - "type": "boolean", - "key": "wholeFrameGeo_editable", - "label": "Whole Frame Geo editable" - }, - { - "type": "boolean", - "key": "writeCreases", - "label": "Write Creases default" - }, - { - "type": "boolean", - "key": "writeCreases_editable", - "label": "Write Creases editable" - }, - { - "type": "boolean", - "key": "writeUVSets", - "label": "Write UV Sets default" - }, - { - "type": "boolean", - "key": "writeUVSets_editable", - "label": "Write UV Sets editable" - }, - { - "type": "boolean", - "key": "writeVisibility", - "label": "Write Visibility default" - }, - { - "type": "boolean", - "key": "writeVisibility_editable", - "label": "Write Visibility editable" - }, { "type": "boolean", "key": "write_color_sets", @@ -479,6 +325,52 @@ "label": "Default Variants", "object_type": "text" }, + { + "type": "enum", + "key": "abc_flags", + "multiselection": true, + "label": "Alembic Flags", + "enum_items": [ + {"writeColorSets": "writeColorSets"}, + {"writeFaceSets": "writeFaceSets"}, + {"renderableOnly": "renderableOnly"}, + {"visibleOnly": "visibleOnly"}, + {"worldSpace": "worldSpace"}, + {"noNormals": "noNormals"}, + {"eulerFilter": "eulerFilter"}, + {"preRoll": "preRoll"}, + {"uvWrite": "uvWrite"}, + {"verbose": "verbose"}, + {"wholeFrameGeo": "wholeFrameGeo"}, + {"writeUVSets": "writeUVSets"}, + {"writeVisibility": "writeVisibility"} + ] + }, + { + "type": "enum", + "key": "abc_editable_flags", + "multiselection": true, + "label": "Alembic Editable Flags", + "enum_items": [ + {"step": "step"}, + {"writeColorSets": "writeColorSets"}, + {"writeFaceSets": "writeFaceSets"}, + {"renderableOnly": "renderableOnly"}, + {"visibleOnly": "visibleOnly"}, + {"worldSpace": "worldSpace"}, + {"noNormals": "noNormals"}, + {"attr": "attr"}, + {"attrPrefix": "attrPrefix"}, + {"eulerFilter": "eulerFilter"}, + {"preRoll": "preRoll"}, + {"preRollStartFrame": "preRollStartFrame"}, + {"uvWrite": "uvWrite"}, + {"verbose": "verbose"}, + {"wholeFrameGeo": "wholeFrameGeo"}, + {"writeUVSets": "writeUVSets"}, + {"writeVisibility": "writeVisibility"} + ] + }, { "type": "number", "key": "step", @@ -486,132 +378,67 @@ "minimum": 0.0, "decimal": 4 }, - { - "type": "boolean", - "key": "step_editable", - "label": "Step editable" - }, { "type": "boolean", "key": "writeColorSets", "label": "Write Color Sets default" }, - { - "type": "boolean", - "key": "writeColorSets_editable", - "label": "Write Color Sets editable" - }, { "type": "boolean", "key": "writeFaceSets", "label": "Write Face Sets default" }, - { - "type": "boolean", - "key": "writeFaceSets_editable", - "label": "Write Face Sets editable" - }, { "type": "boolean", "key": "renderableOnly", "label": "Renderable Only default" }, - { - "type": "boolean", - "key": "renderableOnly_editable", - "label": "Renderable Only editable" - }, { "type": "boolean", "key": "visibleOnly", "label": "Visible Only default" }, - { - "type": "boolean", - "key": "visibleOnly_editable", - "label": "Visible Only editable" - }, { "type": "boolean", "key": "includeParentHierarchy", "label": "Include Parent Hierarchy default" }, - { - "type": "boolean", - "key": "includeParentHierarchy_editable", - "label": "Include Parent Hierarchy editable" - }, { "type": "boolean", "key": "worldSpace", "label": "World Space default" }, - { - "type": "boolean", - "key": "worldSpace_editable", - "label": "World Space editable" - }, { "type": "boolean", "key": "farm", "label": "Farm default" }, - { - "type": "boolean", - "key": "farm_editable", - "label": "Farm editable" - }, { "type": "number", "key": "priority", "label": "Priority default", "minimum": 0 }, - { - "type": "boolean", - "key": "priority_editable", - "label": "Priority editable" - }, { "type": "boolean", "key": "writeNormals", "label": "Write Normals default" }, - { - "type": "boolean", - "key": "writeNormals_editable", - "label": "Write Normals editable" - }, { "type": "boolean", "key": "includeUserDefinedAttributes", "label": "Include User Defined Attributes default" }, - { - "type": "boolean", - "key": "includeUserDefinedAttributes_editable", - "label": "Include User Defined Attributes editable" - }, { "type": "text", "key": "attr", "label": "Attr default" }, - { - "type": "boolean", - "key": "attr_editable", - "label": "Attr editable" - }, { "type": "text", "key": "attrPrefix", "label": "Attr Prefix default" }, - { - "type": "boolean", - "key": "attrPrefix_editable", - "label": "Attr Prefix editable" - }, { "type": "enum", "key": "dataFormat", @@ -625,132 +452,37 @@ } ] }, - { - "type": "boolean", - "key": "dataFormat_editable", - "label": "Data Format editable" - }, - { - "type": "boolean", - "key": "eulerFilter", - "label": "Euler Filter default" - }, - { - "type": "boolean", - "key": "eulerFilter_editable", - "label": "Euler Filter editable" - }, { "type": "boolean", "key": "noNormals", "label": "No Normals default" }, - { - "type": "boolean", - "key": "noNormals_editable", - "label": "No Normals editable" - }, - { - "type": "boolean", - "key": "preRoll", - "label": "Pre Roll default" - }, - { - "type": "boolean", - "key": "preRoll_editable", - "label": "Pre Roll editable" - }, { "type": "number", "key": "preRollStartFrame", "label": "Pre Roll Start Frame default", "minimum": 0 }, - { - "type": "boolean", - "key": "preRollStartFrame_editable", - "label": "Pre Roll Start Frame editable" - }, { "type": "boolean", "key": "refresh", "label": "Refresh default" }, - { - "type": "boolean", - "key": "refresh_editable", - "label": "Refresh editable" - }, { "type": "boolean", "key": "stripNamespaces", "label": "Strip Namespaces default" }, - { - "type": "boolean", - "key": "stripNamespaces_editable", - "label": "Strip Namespaces editable" - }, { "type": "boolean", "key": "uvWrite", "label": "UV Write default" }, - { - "type": "boolean", - "key": "uvWrite_editable", - "label": "UV Write editable" - }, - { - "type": "boolean", - "key": "verbose", - "label": "Verbose default" - }, - { - "type": "boolean", - "key": "verbose_editable", - "label": "Verbose editable" - }, - { - "type": "boolean", - "key": "wholeFrameGeo", - "label": "Whole Frame Geo default" - }, - { - "type": "boolean", - "key": "wholeFrameGeo_editable", - "label": "Whole Frame Geo editable" - }, { "type": "boolean", "key": "writeCreases", "label": "Write Creases default" }, - { - "type": "boolean", - "key": "writeCreases_editable", - "label": "Write Creases editable" - }, - { - "type": "boolean", - "key": "writeUVSets", - "label": "Write UV Sets default" - }, - { - "type": "boolean", - "key": "writeUVSets_editable", - "label": "Write UV Sets editable" - }, - { - "type": "boolean", - "key": "writeVisibility", - "label": "Write Visibility default" - }, - { - "type": "boolean", - "key": "writeVisibility_editable", - "label": "Write Visibility editable" - }, { "type": "boolean", "key": "write_color_sets", From 14fc3ab8264162758d5e430fadfb71254a070fc2 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 14 Nov 2023 16:08:11 +0000 Subject: [PATCH 027/163] `maya` Display Alembic settings for Animation Previous commit wasn't displaying the settings for the `CreateAnimation` creator since it's a "HiddenCreator", this commit ensures we display all the enabled Alembic settins in the Publisher. --- .../create/create_animation_pointcache.py | 143 +++++++++--------- 1 file changed, 71 insertions(+), 72 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index b91994c65d9..335d054ab39 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -2,37 +2,36 @@ from pprint import pprint -from openpype.hosts.maya.api import ( - lib, - plugin -) +from openpype.hosts.maya.api import lib, plugin from openpype.lib import ( BoolDef, TextDef, NumberDef, EnumDef, UISeparatorDef, - UILabelDef + UILabelDef, ) + def _get_animation_attr_defs(cls): - """Get Animation generic ddefinitions. - """ + """Get Animation generic ddefinitions.""" defs = lib.collect_animation_defs() - defs.extend([ - BoolDef("farm", label="Submit to Farm"), - NumberDef("priority", label="Farm job Priority", default=50), - BoolDef("refresh", label="Refresh viewport during export"), - BoolDef("includeParentHierarchy", label="Include Parent Hierarchy"), - BoolDef("writeNormals", label="Write Normals"), - BoolDef("writeCreases", label="Write Creases") - ]) + defs.extend( + [ + BoolDef("farm", label="Submit to Farm"), + NumberDef("priority", label="Farm job Priority", default=50), + BoolDef("refresh", label="Refresh viewport during export"), + BoolDef("includeParentHierarchy", label="Include Parent Hierarchy"), + BoolDef("writeNormals", label="Write Normals"), + BoolDef("writeCreases", label="Write Creases"), + ] + ) return defs + def _get_animation_abc_attr_defs(cls): - """ Get definitions relating to Alembic. - """ + """Get definitions relating to Alembic.""" # List of arguments extracted from AbcExport -h # Them being here doesn't imply we support them or that we need them at this # point, it's a convininece list to populate the UI defaults. @@ -67,21 +66,18 @@ def _get_animation_abc_attr_defs(cls): "melPerFrameCallback", "melPostJobCallback", "pythonPerFrameCallback", - "pythonPostJobCallback" + "pythonPostJobCallback", ] - abc_defs = [ - UISeparatorDef(), - UILabelDef("Alembic Options") - ] + abc_defs = [UISeparatorDef(), UILabelDef("Alembic Options")] - print("Processing editable Alembic attributes...") alembic_editable_attributes = getattr(cls, "abc_editable_flags", None) if not alembic_editable_attributes: + print("No Almbic attributes found in settings.") return None - print(alembic_editable_attributes) + print("Processing editable Alembic attributes...") abc_boolean_defs = [ "writeColorSets", @@ -108,43 +104,51 @@ def _get_animation_abc_attr_defs(cls): ] enabled_boolean_attributes = [ - attrib - for attrib in abc_boolean_defs - if attrib in alembic_editable_attributes + attrib for attrib in abc_boolean_defs if attrib in alembic_editable_attributes ] if enabled_boolean_attributes: - abc_defs.extend([EnumDef( - "abcExportFlags", - enabled_boolean_attributes, - default=abc_boolean_defaults, - multiselection=True, - label="Alembic Export Flags" - )]) - + abc_defs.extend( + [ + EnumDef( + "abcExportFlags", + enabled_boolean_attributes, + default=abc_boolean_defaults, + multiselection=True, + label="Alembic Export Flags", + ) + ] + ) - abc_defs.append(TextDef( + abc_defs.append( + TextDef( "attr", label="Alembic Custom Attributes", placeholder="attr1, attr2", - disabled=True if "attr" not in alembic_editable_attributes else False - )) + disabled=True if "attr" not in alembic_editable_attributes else False, + ) + ) - abc_defs.append(TextDef( + abc_defs.append( + TextDef( "attrPrefix", label="Alembic Custom Attributes Prefix", placeholder="prefix1, prefix2", - disabled=True if "attrPrefix" not in alembic_editable_attributes else False - )) + disabled=True if "attrPrefix" not in alembic_editable_attributes else False, + ) + ) - abc_defs.append(EnumDef( + abc_defs.append( + EnumDef( "dataFormat", label="Alembic Data Format", items=["ogawa", "HDF"], - disabled=True if "dataFormat" not in alembic_editable_attributes else False - )) + disabled=True if "dataFormat" not in alembic_editable_attributes else False, + ) + ) - abc_defs.append(NumberDef( + abc_defs.append( + NumberDef( "preRollStartFrame", label="Start frame for preroll (Alembic)", tooltip=( @@ -152,29 +156,11 @@ def _get_animation_abc_attr_defs(cls): " the starting frame for time dependent translations and can" " be used to evaluate run-up that isn't actually translated." ), - disabled=True if "preRollStartFrame" not in alembic_editable_attributes else False - )) - - #['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_add_instance_to_context', '_cached_group_label', '_default_collect_instances', '_default_remove_instances', '_default_update_instances', '_log', '_remove_instance_from_context', 'apply_settings', 'cache_subsets', 'collect_instances', 'collection_shared_data', 'create', 'create_context', 'enabled', 'family', 'get_dynamic_data', 'get_group_label', 'get_icon', 'get_instance_attr_defs', 'get_next_versions_for_instances', 'get_publish_families', 'get_subset_name', 'group_label', 'headless', 'host', 'host_name', 'icon', 'identifier', 'imprint_instance_node', 'include_parent_hierarchy', 'include_user_defined_attributes', 'instance_attr_defs', 'label', 'log', 'name', 'order', 'project_anatomy', 'project_name', 'project_settings', 'read_instance_node', 'remove_instances', 'set_instance_thumbnail_path', 'update_instances', 'write_color_sets', 'write_face_sets'] - #for - # Collect editable state and default values. - # resulting_defs = [] - # for definition in defs: - # # Include by default any attributes which has no editable state - # # from settings. - # if not hasattr(cls, definition.key + "_editable"): - # print("{} was not found.".format(definition.key + "_editable")) - # resulting_defs.append(definition) - # continue - - # # Remove non-editable defs. - # if not getattr(cls, definition.key + "_editable"): - # continue - - # # Set default values from settings. - # definition.default = getattr(cls, definition.key) - - # resulting_defs.append(definition) + disabled=True + if "preRollStartFrame" not in alembic_editable_attributes + else False, + ) + ) return abc_defs @@ -186,6 +172,7 @@ class CreateAnimation(plugin.MayaHiddenCreator): automated upon loading a rig. There's an inventory action to recreate it for loaded rigs if by chance someone deleted the animation instance. """ + identifier = "io.openpype.creators.maya.animation" name = "animationDefault" label = "Animation" @@ -198,6 +185,19 @@ class CreateAnimation(plugin.MayaHiddenCreator): include_user_defined_attributes = False def get_instance_attr_defs(self): + super(CreateAnimation, self).get_instance_attr_defs() + # adding project settings, since MayaHiddenCreator does not + # handle this for us (yet?) + settings = ( + getattr(self, "project_settings", {}) + .get("maya", {}) + .get("create", {}) + .get("CreateAnimation") + ) + + for key, value in settings.items(): + setattr(self, key, value) + defs = _get_animation_attr_defs(self) abc_defs = _get_animation_abc_attr_defs(self) @@ -207,9 +207,6 @@ def get_instance_attr_defs(self): return defs - def apply_settings(self, project_settings): - super(CreateAnimation, self).apply_settings(project_settings) - class CreatePointCache(plugin.MayaCreator): """Alembic pointcache for animated data""" @@ -223,6 +220,9 @@ class CreatePointCache(plugin.MayaCreator): include_user_defined_attributes = False def get_instance_attr_defs(self): + super(CreatePointCache, self).get_instance_attr_defs() + # defs = self.get_instance_attr_defs() + print(self.instance_attr_defs) defs = _get_animation_attr_defs(self) abc_defs = _get_animation_abc_attr_defs(self) @@ -233,7 +233,6 @@ def get_instance_attr_defs(self): return defs def create(self, subset_name, instance_data, pre_create_data): - instance = super(CreatePointCache, self).create( subset_name, instance_data, pre_create_data ) From f4be711748dcd81439a7788a00d857b48622dbf9 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 14 Nov 2023 16:39:21 +0000 Subject: [PATCH 028/163] `maya.api.alembic` Move all the Alembic methods into it's own Following the `fbx` and `gltf` formats, a new files has been created for `abc` files, for a better organization and re-use of common parts. --- openpype/hosts/maya/api/alembic.py | 290 +++++++++++++++++ openpype/hosts/maya/api/lib.py | 292 ------------------ .../plugins/publish/extract_pointcache.py | 3 +- .../maya/plugins/publish/extract_proxy_abc.py | 2 +- .../extract_unreal_skeletalmesh_abc.py | 2 +- .../plugins/publish/extract_workfile_xgen.py | 2 +- 6 files changed, 295 insertions(+), 296 deletions(-) create mode 100644 openpype/hosts/maya/api/alembic.py diff --git a/openpype/hosts/maya/api/alembic.py b/openpype/hosts/maya/api/alembic.py new file mode 100644 index 00000000000..f5684256cc3 --- /dev/null +++ b/openpype/hosts/maya/api/alembic.py @@ -0,0 +1,290 @@ +# The maya alembic export types +_alembic_options = { + "startFrame": float, + "endFrame": float, + "frameRange": str, # "start end"; overrides startFrame & endFrame + "eulerFilter": bool, + "frameRelativeSample": float, + "noNormals": bool, + "renderableOnly": bool, + "step": float, + "stripNamespaces": bool, + "verbose": bool, + "uvWrite": bool, + "wholeFrameGeo": bool, + "worldSpace": bool, + "writeVisibility": bool, + "writeColorSets": bool, + "writeFaceSets": bool, + "writeCreases": bool, # Maya 2015 Ext1+ + "writeUVSets": bool, # Maya 2017+ + "dataFormat": str, + "root": (list, tuple), + "attr": (list, tuple), + "attrPrefix": (list, tuple), + "userAttr": (list, tuple), + "melPerFrameCallback": str, + "melPostJobCallback": str, + "pythonPerFrameCallback": str, + "pythonPostJobCallback": str, + "selection": bool, + "preRoll": bool, + "preRollStartFrame": int +} + + +def extract_alembic(file, + startFrame=None, + endFrame=None, + frameRange="", + eulerFilter=True, + noNormals=False, + preRoll=False, + renderableOnly=False, + selection=True, + uvWrite=True, + writeColorSets=False, + writeFaceSets=False, + wholeFrameGeo=False, + worldSpace=False, + writeVisibility=False, + writeUVSets=False, + writeCreases=False, + dataFormat="ogawa", + step=1.0, + attr=None, + attrPrefix=None, + root=None, + stripNamespaces=True, + verbose=False, + preRollStartFrame=0): + """Extract a single Alembic Cache. + + This extracts an Alembic cache using the `-selection` flag to minimize + the extracted content to solely what was Collected into the instance. + + Arguments: + + startFrame (float): Start frame of output. Ignored if `frameRange` + provided. + + endFrame (float): End frame of output. Ignored if `frameRange` + provided. + + frameRange (tuple or str): Two-tuple with start and end frame or a + string formatted as: "startFrame endFrame". This argument + overrides `startFrame` and `endFrame` arguments. + + eulerFilter (bool): When on, X, Y, and Z rotation data is filtered with + an Euler filter. Euler filtering helps resolve irregularities in + rotations especially if X, Y, and Z rotations exceed 360 degrees. + Defaults to True. + + noNormals (bool): When on, normal data from the original polygon + objects is not included in the exported Alembic cache file. + + preRoll (bool): This frame range will not be sampled. + Defaults to False. + + renderableOnly (bool): When on, any non-renderable nodes or hierarchy, + such as hidden objects, are not included in the Alembic file. + Defaults to False. + + selection (bool): Write out all all selected nodes from the + active selection list that are descendents of the roots specified + with -root. Defaults to False. + + uvWrite (bool): When on, UV data from polygon meshes and subdivision + objects are written to the Alembic file. Only the current UV map is + included. + + writeColorSets (bool): Write all color sets on MFnMeshes as + color 3 or color 4 indexed geometry parameters with face varying + scope. Defaults to False. + + writeFaceSets (bool): Write all Face sets on MFnMeshes. + Defaults to False. + + wholeFrameGeo (bool): Data for geometry will only be written + out on whole frames. Defaults to False. + + worldSpace (bool): When on, the top node in the node hierarchy is + stored as world space. By default, these nodes are stored as local + space. Defaults to False. + + writeVisibility (bool): Visibility state will be stored in + the Alembic file. Otherwise everything written out is treated as + visible. Defaults to False. + + writeUVSets (bool): Write all uv sets on MFnMeshes as vector + 2 indexed geometry parameters with face varying scope. Defaults to + False. + + writeCreases (bool): If the mesh has crease edges or crease + vertices, the mesh (OPolyMesh) would now be written out as an OSubD + and crease info will be stored in the Alembic file. Otherwise, + creases info won't be preserved in Alembic file unless a custom + Boolean attribute SubDivisionMesh has been added to mesh node and + its value is true. Defaults to False. + + dataFormat (str): The data format to use for the cache, + defaults to "ogawa" + + step (float): The time interval (expressed in frames) at + which the frame range is sampled. Additional samples around each + frame can be specified with -frs. Defaults to 1.0. + + attr (list of str, optional): A specific geometric attribute to write + out. Defaults to []. + + attrPrefix (list of str, optional): Prefix filter for determining which + geometric attributes to write out. Defaults to ["ABC_"]. + + root (list of str): Maya dag path which will be parented to + the root of the Alembic file. Defaults to [], which means the + entire scene will be written out. + + stripNamespaces (bool): When on, any namespaces associated with the + exported objects are removed from the Alembic file. For example, an + object with the namespace taco:foo:bar appears as bar in the + Alembic file. + + verbose (bool): When on, outputs frame number information to the + Script Editor or output window during extraction. + + preRollStartFrame (float): The frame to start scene + evaluation at. This is used to set the starting frame for time + dependent translations and can be used to evaluate run-up that + isn't actually translated. Defaults to 0. + """ + + # Ensure alembic exporter is loaded + cmds.loadPlugin('AbcExport', quiet=True) + + # Alembic Exporter requires forward slashes + file = file.replace('\\', '/') + + # Ensure list arguments are valid. + attr = attr or [] + attrPrefix = attrPrefix or [] + root = root or [] + + # Pass the start and end frame on as `frameRange` so that it + # never conflicts with that argument + if not frameRange: + # Fallback to maya timeline if no start or end frame provided. + if startFrame is None: + startFrame = cmds.playbackOptions(query=True, + animationStartTime=True) + if endFrame is None: + endFrame = cmds.playbackOptions(query=True, + animationEndTime=True) + + # Ensure valid types are converted to frame range + assert isinstance(startFrame, _alembic_options["startFrame"]) + assert isinstance(endFrame, _alembic_options["endFrame"]) + frameRange = "{0} {1}".format(startFrame, endFrame) + else: + # Allow conversion from tuple for `frameRange` + if isinstance(frameRange, (list, tuple)): + assert len(frameRange) == 2 + frameRange = "{0} {1}".format(frameRange[0], frameRange[1]) + + # Assemble options + options = { + "selection": selection, + "frameRange": frameRange, + "eulerFilter": eulerFilter, + "noNormals": noNormals, + "preRoll": preRoll, + "renderableOnly": renderableOnly, + "selection": selection, + "uvWrite": uvWrite, + "writeColorSets": writeColorSets, + "writeFaceSets": writeFaceSets, + "wholeFrameGeo": wholeFrameGeo, + "worldSpace": worldSpace, + "writeVisibility": writeVisibility, + "writeUVSets": writeUVSets, + "writeCreases": writeCreases, + "dataFormat": dataFormat, + "step": step, + "attr": attr, + "attrPrefix": attrPrefix, + "stripNamespaces": stripNamespaces, + "verbose": verbose, + "preRollStartFrame": preRollStartFrame + } + + # Validate options + for key, value in options.copy().items(): + + # Discard unknown options + if key not in _alembic_options: + log.warning("extract_alembic() does not support option '%s'. " + "Flag will be ignored..", key) + options.pop(key) + continue + + # Validate value type + valid_types = _alembic_options[key] + if not isinstance(value, valid_types): + raise TypeError("Alembic option unsupported type: " + "{0} (expected {1})".format(value, valid_types)) + + # Ignore empty values, like an empty string, since they mess up how + # job arguments are built + if isinstance(value, (list, tuple)): + value = [x for x in value if x.strip()] + + # Ignore option completely if no values remaining + if not value: + options.pop(key) + continue + + options[key] = value + + # The `writeCreases` argument was changed to `autoSubd` in Maya 2018+ + maya_version = int(cmds.about(version=True)) + if maya_version >= 2018: + options['autoSubd'] = options.pop('writeCreases', False) + + # Format the job string from options + job_args = list() + for key, value in options.items(): + if isinstance(value, (list, tuple)): + for entry in value: + job_args.append("-{} {}".format(key, entry)) + elif isinstance(value, bool): + # Add only when state is set to True + if value: + job_args.append("-{0}".format(key)) + else: + job_args.append("-{0} {1}".format(key, value)) + + job_str = " ".join(job_args) + job_str += ' -file "%s"' % file + + # Ensure output directory exists + parent_dir = os.path.dirname(file) + if not os.path.exists(parent_dir): + os.makedirs(parent_dir) + + if verbose: + log.debug("Preparing Alembic export with options: %s", + json.dumps(options, indent=4)) + log.debug("Extracting Alembic with job arguments: %s", job_str) + + # Perform extraction + print("Alembic Job Arguments : {}".format(job_str)) + + # Disable the parallel evaluation temporarily to ensure no buggy + # exports are made. (PLN-31) + # TODO: Make sure this actually fixes the issues + with evaluation("off"): + cmds.AbcExport(j=job_str, verbose=verbose) + + if verbose: + log.debug("Extracted Alembic to: %s", file) + + return file diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 8b74319563a..4058cc446d2 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -81,40 +81,6 @@ 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0] -# The maya alembic export types -_alembic_options = { - "startFrame": float, - "endFrame": float, - "frameRange": str, # "start end"; overrides startFrame & endFrame - "eulerFilter": bool, - "frameRelativeSample": float, - "noNormals": bool, - "renderableOnly": bool, - "step": float, - "stripNamespaces": bool, - "verbose": bool, - "uvWrite": bool, - "wholeFrameGeo": bool, - "worldSpace": bool, - "writeVisibility": bool, - "writeColorSets": bool, - "writeFaceSets": bool, - "writeCreases": bool, # Maya 2015 Ext1+ - "writeUVSets": bool, # Maya 2017+ - "dataFormat": str, - "root": (list, tuple), - "attr": (list, tuple), - "attrPrefix": (list, tuple), - "userAttr": (list, tuple), - "melPerFrameCallback": str, - "melPostJobCallback": str, - "pythonPerFrameCallback": str, - "pythonPostJobCallback": str, - "selection": bool, - "preRoll": bool, - "preRollStartFrame": int -} - INT_FPS = {15, 24, 25, 30, 48, 50, 60, 44100, 48000} FLOAT_FPS = {23.98, 23.976, 29.97, 47.952, 59.94} @@ -1152,264 +1118,6 @@ def is_visible(node, return True - -def extract_alembic(file, - startFrame=None, - endFrame=None, - frameRange="", - eulerFilter=True, - noNormals=False, - preRoll=False, - renderableOnly=False, - selection=True, - uvWrite=True, - writeColorSets=False, - writeFaceSets=False, - wholeFrameGeo=False, - worldSpace=False, - writeVisibility=False, - writeUVSets=False, - writeCreases=False, - dataFormat="ogawa", - step=1.0, - attr=None, - attrPrefix=None, - root=None, - stripNamespaces=True, - verbose=False, - preRollStartFrame=0): - """Extract a single Alembic Cache. - - This extracts an Alembic cache using the `-selection` flag to minimize - the extracted content to solely what was Collected into the instance. - - Arguments: - - startFrame (float): Start frame of output. Ignored if `frameRange` - provided. - - endFrame (float): End frame of output. Ignored if `frameRange` - provided. - - frameRange (tuple or str): Two-tuple with start and end frame or a - string formatted as: "startFrame endFrame". This argument - overrides `startFrame` and `endFrame` arguments. - - eulerFilter (bool): When on, X, Y, and Z rotation data is filtered with - an Euler filter. Euler filtering helps resolve irregularities in - rotations especially if X, Y, and Z rotations exceed 360 degrees. - Defaults to True. - - noNormals (bool): When on, normal data from the original polygon - objects is not included in the exported Alembic cache file. - - preRoll (bool): This frame range will not be sampled. - Defaults to False. - - renderableOnly (bool): When on, any non-renderable nodes or hierarchy, - such as hidden objects, are not included in the Alembic file. - Defaults to False. - - selection (bool): Write out all all selected nodes from the - active selection list that are descendents of the roots specified - with -root. Defaults to False. - - uvWrite (bool): When on, UV data from polygon meshes and subdivision - objects are written to the Alembic file. Only the current UV map is - included. - - writeColorSets (bool): Write all color sets on MFnMeshes as - color 3 or color 4 indexed geometry parameters with face varying - scope. Defaults to False. - - writeFaceSets (bool): Write all Face sets on MFnMeshes. - Defaults to False. - - wholeFrameGeo (bool): Data for geometry will only be written - out on whole frames. Defaults to False. - - worldSpace (bool): When on, the top node in the node hierarchy is - stored as world space. By default, these nodes are stored as local - space. Defaults to False. - - writeVisibility (bool): Visibility state will be stored in - the Alembic file. Otherwise everything written out is treated as - visible. Defaults to False. - - writeUVSets (bool): Write all uv sets on MFnMeshes as vector - 2 indexed geometry parameters with face varying scope. Defaults to - False. - - writeCreases (bool): If the mesh has crease edges or crease - vertices, the mesh (OPolyMesh) would now be written out as an OSubD - and crease info will be stored in the Alembic file. Otherwise, - creases info won't be preserved in Alembic file unless a custom - Boolean attribute SubDivisionMesh has been added to mesh node and - its value is true. Defaults to False. - - dataFormat (str): The data format to use for the cache, - defaults to "ogawa" - - step (float): The time interval (expressed in frames) at - which the frame range is sampled. Additional samples around each - frame can be specified with -frs. Defaults to 1.0. - - attr (list of str, optional): A specific geometric attribute to write - out. Defaults to []. - - attrPrefix (list of str, optional): Prefix filter for determining which - geometric attributes to write out. Defaults to ["ABC_"]. - - root (list of str): Maya dag path which will be parented to - the root of the Alembic file. Defaults to [], which means the - entire scene will be written out. - - stripNamespaces (bool): When on, any namespaces associated with the - exported objects are removed from the Alembic file. For example, an - object with the namespace taco:foo:bar appears as bar in the - Alembic file. - - verbose (bool): When on, outputs frame number information to the - Script Editor or output window during extraction. - - preRollStartFrame (float): The frame to start scene - evaluation at. This is used to set the starting frame for time - dependent translations and can be used to evaluate run-up that - isn't actually translated. Defaults to 0. - """ - - # Ensure alembic exporter is loaded - cmds.loadPlugin('AbcExport', quiet=True) - - # Alembic Exporter requires forward slashes - file = file.replace('\\', '/') - - # Ensure list arguments are valid. - attr = attr or [] - attrPrefix = attrPrefix or [] - root = root or [] - - # Pass the start and end frame on as `frameRange` so that it - # never conflicts with that argument - if not frameRange: - # Fallback to maya timeline if no start or end frame provided. - if startFrame is None: - startFrame = cmds.playbackOptions(query=True, - animationStartTime=True) - if endFrame is None: - endFrame = cmds.playbackOptions(query=True, - animationEndTime=True) - - # Ensure valid types are converted to frame range - assert isinstance(startFrame, _alembic_options["startFrame"]) - assert isinstance(endFrame, _alembic_options["endFrame"]) - frameRange = "{0} {1}".format(startFrame, endFrame) - else: - # Allow conversion from tuple for `frameRange` - if isinstance(frameRange, (list, tuple)): - assert len(frameRange) == 2 - frameRange = "{0} {1}".format(frameRange[0], frameRange[1]) - - # Assemble options - options = { - "selection": selection, - "frameRange": frameRange, - "eulerFilter": eulerFilter, - "noNormals": noNormals, - "preRoll": preRoll, - "renderableOnly": renderableOnly, - "selection": selection, - "uvWrite": uvWrite, - "writeColorSets": writeColorSets, - "writeFaceSets": writeFaceSets, - "wholeFrameGeo": wholeFrameGeo, - "worldSpace": worldSpace, - "writeVisibility": writeVisibility, - "writeUVSets": writeUVSets, - "writeCreases": writeCreases, - "dataFormat": dataFormat, - "step": step, - "attr": attr, - "attrPrefix": attrPrefix, - "stripNamespaces": stripNamespaces, - "verbose": verbose, - "preRollStartFrame": preRollStartFrame - } - - # Validate options - for key, value in options.copy().items(): - - # Discard unknown options - if key not in _alembic_options: - log.warning("extract_alembic() does not support option '%s'. " - "Flag will be ignored..", key) - options.pop(key) - continue - - # Validate value type - valid_types = _alembic_options[key] - if not isinstance(value, valid_types): - raise TypeError("Alembic option unsupported type: " - "{0} (expected {1})".format(value, valid_types)) - - # Ignore empty values, like an empty string, since they mess up how - # job arguments are built - if isinstance(value, (list, tuple)): - value = [x for x in value if x.strip()] - - # Ignore option completely if no values remaining - if not value: - options.pop(key) - continue - - options[key] = value - - # The `writeCreases` argument was changed to `autoSubd` in Maya 2018+ - maya_version = int(cmds.about(version=True)) - if maya_version >= 2018: - options['autoSubd'] = options.pop('writeCreases', False) - - # Format the job string from options - job_args = list() - for key, value in options.items(): - if isinstance(value, (list, tuple)): - for entry in value: - job_args.append("-{} {}".format(key, entry)) - elif isinstance(value, bool): - # Add only when state is set to True - if value: - job_args.append("-{0}".format(key)) - else: - job_args.append("-{0} {1}".format(key, value)) - - job_str = " ".join(job_args) - job_str += ' -file "%s"' % file - - # Ensure output directory exists - parent_dir = os.path.dirname(file) - if not os.path.exists(parent_dir): - os.makedirs(parent_dir) - - if verbose: - log.debug("Preparing Alembic export with options: %s", - json.dumps(options, indent=4)) - log.debug("Extracting Alembic with job arguments: %s", job_str) - - # Perform extraction - print("Alembic Job Arguments : {}".format(job_str)) - - # Disable the parallel evaluation temporarily to ensure no buggy - # exports are made. (PLN-31) - # TODO: Make sure this actually fixes the issues - with evaluation("off"): - cmds.AbcExport(j=job_str, verbose=verbose) - - if verbose: - log.debug("Extracted Alembic to: %s", file) - - return file - - # region ID def get_id_required_nodes(referenced_nodes=False, nodes=None): """Filter out any node which are locked (reference) or readOnly diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 0cc802fa7aa..8ee723098ca 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -3,8 +3,8 @@ from maya import cmds from openpype.pipeline import publish +from openpype.hosts.maya.api.alembic import extract_alembic from openpype.hosts.maya.api.lib import ( - extract_alembic, suspended_refresh, maintained_selection, iter_visible_nodes_in_range @@ -45,6 +45,7 @@ def process(self, instance): attr_prefixes = instance.data.get("attrPrefix", "").split(";") attr_prefixes = [value for value in attr_prefixes if value.strip()] + self.log.debug("Extracting pointcache..") dirname = self.staging_dir(instance) diff --git a/openpype/hosts/maya/plugins/publish/extract_proxy_abc.py b/openpype/hosts/maya/plugins/publish/extract_proxy_abc.py index d9bec87cfd3..b54c91f05af 100644 --- a/openpype/hosts/maya/plugins/publish/extract_proxy_abc.py +++ b/openpype/hosts/maya/plugins/publish/extract_proxy_abc.py @@ -3,8 +3,8 @@ from maya import cmds from openpype.pipeline import publish +from openpype.hosts.maya.api.alembic import extract_alembic from openpype.hosts.maya.api.lib import ( - extract_alembic, suspended_refresh, maintained_selection, iter_visible_nodes_in_range diff --git a/openpype/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py b/openpype/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py index 9c2f55a1ef1..af82c246837 100644 --- a/openpype/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py +++ b/openpype/hosts/maya/plugins/publish/extract_unreal_skeletalmesh_abc.py @@ -6,8 +6,8 @@ from maya import cmds # noqa from openpype.pipeline import publish +from openpype.hosts.maya.api.alembic import extract_alembic from openpype.hosts.maya.api.lib import ( - extract_alembic, suspended_refresh, maintained_selection ) diff --git a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py index 4bd01c2df2b..bfcf65c6521 100644 --- a/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py +++ b/openpype/hosts/maya/plugins/publish/extract_workfile_xgen.py @@ -5,7 +5,7 @@ from maya import cmds import pyblish.api -from openpype.hosts.maya.api.lib import extract_alembic +from openpype.hosts.maya.api.alembic import extract_alembic from openpype.pipeline import publish from openpype.lib import StringTemplate From 38a3ea45e1cc52039f7c5cd555daf6eb98038476 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 14 Nov 2023 18:50:50 +0000 Subject: [PATCH 029/163] `maya.api.alembic` Alphabetically order arguments --- openpype/hosts/maya/api/alembic.py | 101 +++++++++++++++-------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/openpype/hosts/maya/api/alembic.py b/openpype/hosts/maya/api/alembic.py index f5684256cc3..75dad62ae15 100644 --- a/openpype/hosts/maya/api/alembic.py +++ b/openpype/hosts/maya/api/alembic.py @@ -1,63 +1,69 @@ # The maya alembic export types -_alembic_options = { - "startFrame": float, +ALEMBIC_ARGS = { + "attr": (list, tuple), + "attrPrefix": (list, tuple), + "autoSubd": bool, + "dataFormat": str, + "dontSkipUnwrittenFrames": bool, "endFrame": float, - "frameRange": str, # "start end"; overrides startFrame & endFrame "eulerFilter": bool, + "frameRange": str, # "start end"; overrides startFrame & endFrame "frameRelativeSample": float, + "melPerFrameCallback": str, + "melPostJobCallback": str, "noNormals": bool, + "preRoll": bool, + "preRollStartFrame": int, + "pythonPerFrameCallback": str, + "pythonPostJobCallback": str, "renderableOnly": bool, + "root": (list, tuple), + "selection": bool, + "startFrame": float, "step": float, "stripNamespaces": bool, - "verbose": bool, + "userAttr": (list, tuple), + "userAttrPrefix": (list, tuple), "uvWrite": bool, + "uvsOnly": bool, + "verbose": bool, "wholeFrameGeo": bool, "worldSpace": bool, - "writeVisibility": bool, "writeColorSets": bool, - "writeFaceSets": bool, "writeCreases": bool, # Maya 2015 Ext1+ + "writeFaceSets": bool, "writeUVSets": bool, # Maya 2017+ - "dataFormat": str, - "root": (list, tuple), - "attr": (list, tuple), - "attrPrefix": (list, tuple), - "userAttr": (list, tuple), - "melPerFrameCallback": str, - "melPostJobCallback": str, - "pythonPerFrameCallback": str, - "pythonPostJobCallback": str, - "selection": bool, - "preRoll": bool, - "preRollStartFrame": int + "writeVisibility": bool, } -def extract_alembic(file, - startFrame=None, - endFrame=None, - frameRange="", - eulerFilter=True, - noNormals=False, - preRoll=False, - renderableOnly=False, - selection=True, - uvWrite=True, - writeColorSets=False, - writeFaceSets=False, - wholeFrameGeo=False, - worldSpace=False, - writeVisibility=False, - writeUVSets=False, - writeCreases=False, - dataFormat="ogawa", - step=1.0, - attr=None, - attrPrefix=None, - root=None, - stripNamespaces=True, - verbose=False, - preRollStartFrame=0): +def extract_alembic( + file, + attr=None, + attrPrefix=None, + dataFormat="ogawa", + endFrame=None, + eulerFilter=True, + frameRange="", + noNormals=False, + preRoll=False, + preRollStartFrame=0, + renderableOnly=False, + root=None, + selection=True, + startFrame=None, + step=1.0, + stripNamespaces=True, + uvWrite=True, + verbose=False, + wholeFrameGeo=False, + worldSpace=False, + writeColorSets=False, + writeCreases=False, + writeFaceSets=False, + writeUVSets=False, + writeVisibility=False +): """Extract a single Alembic Cache. This extracts an Alembic cache using the `-selection` flag to minimize @@ -181,8 +187,8 @@ def extract_alembic(file, animationEndTime=True) # Ensure valid types are converted to frame range - assert isinstance(startFrame, _alembic_options["startFrame"]) - assert isinstance(endFrame, _alembic_options["endFrame"]) + assert isinstance(startFrame, ALEMBIC_ARGS["startFrame"]) + assert isinstance(endFrame, ALEMBIC_ARGS["endFrame"]) frameRange = "{0} {1}".format(startFrame, endFrame) else: # Allow conversion from tuple for `frameRange` @@ -198,7 +204,6 @@ def extract_alembic(file, "noNormals": noNormals, "preRoll": preRoll, "renderableOnly": renderableOnly, - "selection": selection, "uvWrite": uvWrite, "writeColorSets": writeColorSets, "writeFaceSets": writeFaceSets, @@ -220,14 +225,14 @@ def extract_alembic(file, for key, value in options.copy().items(): # Discard unknown options - if key not in _alembic_options: + if key not in ALEMBIC_ARGS: log.warning("extract_alembic() does not support option '%s'. " "Flag will be ignored..", key) options.pop(key) continue # Validate value type - valid_types = _alembic_options[key] + valid_types = ALEMBIC_ARGS[key] if not isinstance(value, valid_types): raise TypeError("Alembic option unsupported type: " "{0} (expected {1})".format(value, valid_types)) From 6363064f927356b0ae21b803577bff5bd3977136 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 14 Nov 2023 18:52:51 +0000 Subject: [PATCH 030/163] `maya` Rename Alemebic settings, update `pointcache` creator The previous naming was a bit confusing, and now all the possible arguments are made available to the overrides setting, and all the booleans ones added to the corresponding key. --- .../create/create_animation_pointcache.py | 131 ++++++------------ .../defaults/project_settings/maya.json | 18 +-- .../schemas/schema_maya_create.json | 112 ++++++++++----- 3 files changed, 126 insertions(+), 135 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 335d054ab39..a497ace4966 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -1,8 +1,7 @@ from maya import cmds -from pprint import pprint - from openpype.hosts.maya.api import lib, plugin +from openpype.hosts.maya.api.alembic import ALEMBIC_ARGS from openpype.lib import ( BoolDef, TextDef, @@ -35,129 +34,83 @@ def _get_animation_abc_attr_defs(cls): # List of arguments extracted from AbcExport -h # Them being here doesn't imply we support them or that we need them at this # point, it's a convininece list to populate the UI defaults. - alembic_attributes = [ - "preRollStartFrame", - "dontSkipUnwrittenFrames", - "verbose", - "attr", - "autoSubd", - "attrPrefix", - "dataFormat", - "eulerFilter", - "frameRange", - "frameRelativeSample", - "noNormals", - "preRoll", - "renderableOnly", - "root", - "step", - "selection", - "stripNamespaces", - "userAttr", - "userAttrPrefix", - "uvWrite", - "uvsOnly", - "writeColorSets", - "writeFaceSets", - "wholeFrameGeo", - "worldSpace", - "writeVisibility", - "writeUVSets", - "melPerFrameCallback", - "melPostJobCallback", - "pythonPerFrameCallback", - "pythonPostJobCallback", - ] abc_defs = [UISeparatorDef(), UILabelDef("Alembic Options")] - alembic_editable_attributes = getattr(cls, "abc_editable_flags", None) - - if not alembic_editable_attributes: - print("No Almbic attributes found in settings.") - return None - - print("Processing editable Alembic attributes...") - - abc_boolean_defs = [ - "writeColorSets", - "writeFaceSets", - "renderableOnly", - "visibleOnly", - "worldSpace", - "noNormals", - "includeUserDefinedAttributes", - "eulerFilter", - "preRoll", - "stripNamespaces", - "uvWrite", - "verbose", - "wholeFrameGeo", - "writeUVSets", - "writeVisibility", - ] + # The Arguments that can be modified by the Publisher + abc_args_overrides = getattr(cls, "abc_args_overrides", None) - abc_boolean_defaults = [ - "uvWrite", - "worldSpace", - "writeVisibility", - ] + # What we have set in the Settings as default. + abc_boolean_args = getattr(cls, "abc_boolean_args", []) - enabled_boolean_attributes = [ - attrib for attrib in abc_boolean_defs if attrib in alembic_editable_attributes + # Default Flags set in Settings; unless they are editable. + abc_settings_boolean_arguments = [ + arg + for arg in abc_boolean_args + if arg not in abc_args_overrides + ] + # We display them to the user + abc_defs.append(EnumDef( + "abcDefaultExportBooleanArguments", + abc_settings_boolean_arguments, + default=abc_settings_boolean_arguments, + multiselection=True, + label="Settings set Arguments", + disabled=True + )) + + abc_boolean_overrides = [ + arg + for arg in abc_args_overrides + if arg in abc_boolean_args ] - if enabled_boolean_attributes: - abc_defs.extend( - [ - EnumDef( - "abcExportFlags", - enabled_boolean_attributes, - default=abc_boolean_defaults, - multiselection=True, - label="Alembic Export Flags", - ) - ] - ) + if abc_boolean_overrides: + abc_defs.append(EnumDef( + "abcExportBooleanArguments", + abc_boolean_overrides, + multiselection=True, + label="Arguments Overrides" + )) abc_defs.append( TextDef( "attr", - label="Alembic Custom Attributes", - placeholder="attr1, attr2", - disabled=True if "attr" not in alembic_editable_attributes else False, + label="Custom Attributes", + placeholder="attr1, attr2, ...", + disabled=True if "attr" not in abc_args_overrides else False, ) ) abc_defs.append( TextDef( "attrPrefix", - label="Alembic Custom Attributes Prefix", - placeholder="prefix1, prefix2", - disabled=True if "attrPrefix" not in alembic_editable_attributes else False, + label="Custom Attributes Prefix", + placeholder="prefix1, prefix2, ...", + disabled=True if "attrPrefix" not in abc_args_overrides else False, ) ) abc_defs.append( EnumDef( "dataFormat", - label="Alembic Data Format", + label="Data Format", items=["ogawa", "HDF"], - disabled=True if "dataFormat" not in alembic_editable_attributes else False, + disabled=True if "dataFormat" not in abc_args_overrides else False, ) ) abc_defs.append( NumberDef( "preRollStartFrame", - label="Start frame for preroll (Alembic)", + label="Start frame for preroll", tooltip=( "The frame to start scene evaluation at. This is used to set" " the starting frame for time dependent translations and can" " be used to evaluate run-up that isn't actually translated." ), disabled=True - if "preRollStartFrame" not in alembic_editable_attributes + if "preRollStartFrame" not in abc_args_overrides else False, ) ) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index aaa7afdf45f..4f6cf68daa1 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -559,16 +559,13 @@ "CreateAnimation": { "default_variants": [], "step": 1.0, - "abc_flags": [ + "abc_boolean_args": [ "writeColorSets", "visibleOnly", "worldSpace", - "writeNormals", - "includeUserDefinedAttributes", - "attr", - "attrPrefix" + "writeNormals" ], - "abc_editable_flags": [ + "abc_args_overrides": [ "step", "includeParentHierarchy", "writeNormals", @@ -608,16 +605,13 @@ "Main" ], "step": 1.0, - "abc_flags": [ + "abc_boolean_args": [ "writeColorSets", "visibleOnly", "worldSpace", - "writeNormals", - "includeUserDefinedAttributes", - "attr", - "attrPrefix" + "writeNormals" ], - "abc_editable_flags": [ + "abc_args_overrides": [ "step", "writeColorSets", "writeFaceSets", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index c5bfb18e008..3317fe7d71b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -175,46 +175,68 @@ }, { "type": "enum", - "key": "abc_flags", + "key": "abc_boolean_args", "multiselection": true, - "label": "Alembic Flags", + "label": "Alembic Boolean Args", "enum_items": [ - {"writeColorSets": "writeColorSets"}, - {"writeFaceSets": "writeFaceSets"}, - {"renderableOnly": "renderableOnly"}, - {"visibleOnly": "visibleOnly"}, - {"worldSpace": "worldSpace"}, - {"noNormals": "noNormals"}, + {"autoSubd": "autoSubd"}, + {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, {"eulerFilter": "eulerFilter"}, + {"noNormals": "noNormals"}, {"preRoll": "preRoll"}, + {"renderableOnly": "renderableOnly"}, + {"selection": "selection"}, + {"stripNamespaces": "stripNamespaces"}, {"uvWrite": "uvWrite"}, + {"uvsOnly": "uvsOnly"}, {"verbose": "verbose"}, {"wholeFrameGeo": "wholeFrameGeo"}, + {"worldSpace": "worldSpace"}, + {"writeColorSets": "writeColorSets"}, + {"writeCreases": "writeCreases"}, + {"writeFaceSets": "writeFaceSets"}, {"writeUVSets": "writeUVSets"}, {"writeVisibility": "writeVisibility"} ] }, { "type": "enum", - "key": "abc_editable_flags", + "key": "abc_args_overrides", "multiselection": true, - "label": "Alembic Editable Flags", + "label": "Alembic Arguments Overrides: Choose which arguments the user can modify.", "enum_items": [ - {"step": "step"}, - {"writeColorSets": "writeColorSets"}, - {"writeFaceSets": "writeFaceSets"}, - {"renderableOnly": "renderableOnly"}, - {"visibleOnly": "visibleOnly"}, - {"worldSpace": "worldSpace"}, - {"noNormals": "noNormals"}, {"attr": "attr"}, {"attrPrefix": "attrPrefix"}, + {"autoSubd": "autoSubd"}, + {"dataFormat": "dataFormat"}, + {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, + {"endFrame": "endFrame"}, {"eulerFilter": "eulerFilter"}, + {"frameRange": "frameRange"}, + {"frameRelativeSample": "frameRelativeSample"}, + {"melPerFrameCallback": "melPerFrameCallback"}, + {"melPostJobCallback": "melPostJobCallback"}, + {"noNormals": "noNormals"}, {"preRoll": "preRoll"}, {"preRollStartFrame": "preRollStartFrame"}, + {"pythonPerFrameCallback": "pythonPerFrameCallback"}, + {"pythonPostJobCallback": "pythonPostJobCallback"}, + {"renderableOnly": "renderableOnly"}, + {"root": "root"}, + {"selection": "selection"}, + {"startFrame": "startFrame"}, + {"step": "step"}, + {"stripNamespaces": "stripNamespaces"}, + {"userAttr": "userAttr"}, + {"userAttrPrefix": "userAttrPrefix"}, {"uvWrite": "uvWrite"}, + {"uvsOnly": "uvsOnly"}, {"verbose": "verbose"}, {"wholeFrameGeo": "wholeFrameGeo"}, + {"worldSpace": "worldSpace"}, + {"writeColorSets": "writeColorSets"}, + {"writeCreases": "writeCreases"}, + {"writeFaceSets": "writeFaceSets"}, {"writeUVSets": "writeUVSets"}, {"writeVisibility": "writeVisibility"} ] @@ -327,46 +349,68 @@ }, { "type": "enum", - "key": "abc_flags", + "key": "abc_boolean_args", "multiselection": true, - "label": "Alembic Flags", + "label": "Alembic Arguments (passed to AbcExport)", "enum_items": [ - {"writeColorSets": "writeColorSets"}, - {"writeFaceSets": "writeFaceSets"}, - {"renderableOnly": "renderableOnly"}, - {"visibleOnly": "visibleOnly"}, - {"worldSpace": "worldSpace"}, - {"noNormals": "noNormals"}, + {"autoSubd": "autoSubd"}, + {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, {"eulerFilter": "eulerFilter"}, + {"noNormals": "noNormals"}, {"preRoll": "preRoll"}, + {"renderableOnly": "renderableOnly"}, + {"selection": "selection"}, + {"stripNamespaces": "stripNamespaces"}, {"uvWrite": "uvWrite"}, + {"uvsOnly": "uvsOnly"}, {"verbose": "verbose"}, {"wholeFrameGeo": "wholeFrameGeo"}, + {"worldSpace": "worldSpace"}, + {"writeColorSets": "writeColorSets"}, + {"writeCreases": "writeCreases"}, + {"writeFaceSets": "writeFaceSets"}, {"writeUVSets": "writeUVSets"}, {"writeVisibility": "writeVisibility"} ] }, { "type": "enum", - "key": "abc_editable_flags", + "key": "abc_args_overrides", "multiselection": true, - "label": "Alembic Editable Flags", + "label": "Alembic Arguments Overrides: Choose which arguments the user can modify.", "enum_items": [ - {"step": "step"}, - {"writeColorSets": "writeColorSets"}, - {"writeFaceSets": "writeFaceSets"}, - {"renderableOnly": "renderableOnly"}, - {"visibleOnly": "visibleOnly"}, - {"worldSpace": "worldSpace"}, - {"noNormals": "noNormals"}, {"attr": "attr"}, {"attrPrefix": "attrPrefix"}, + {"autoSubd": "autoSubd"}, + {"dataFormat": "dataFormat"}, + {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, + {"endFrame": "endFrame"}, {"eulerFilter": "eulerFilter"}, + {"frameRange": "frameRange"}, + {"frameRelativeSample": "frameRelativeSample"}, + {"melPerFrameCallback": "melPerFrameCallback"}, + {"melPostJobCallback": "melPostJobCallback"}, + {"noNormals": "noNormals"}, {"preRoll": "preRoll"}, {"preRollStartFrame": "preRollStartFrame"}, + {"pythonPerFrameCallback": "pythonPerFrameCallback"}, + {"pythonPostJobCallback": "pythonPostJobCallback"}, + {"renderableOnly": "renderableOnly"}, + {"root": "root"}, + {"selection": "selection"}, + {"startFrame": "startFrame"}, + {"step": "step"}, + {"stripNamespaces": "stripNamespaces"}, + {"userAttr": "userAttr"}, + {"userAttrPrefix": "userAttrPrefix"}, {"uvWrite": "uvWrite"}, + {"uvsOnly": "uvsOnly"}, {"verbose": "verbose"}, {"wholeFrameGeo": "wholeFrameGeo"}, + {"worldSpace": "worldSpace"}, + {"writeColorSets": "writeColorSets"}, + {"writeCreases": "writeCreases"}, + {"writeFaceSets": "writeFaceSets"}, {"writeUVSets": "writeUVSets"}, {"writeVisibility": "writeVisibility"} ] From 3a14c8ebc813039ed32bb5acdcd8a7641ccc8cbf Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 14 Nov 2023 18:54:49 +0000 Subject: [PATCH 031/163] `maya.publish.pointcache` Use the `api.alembic` instead of `lib` --- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 8ee723098ca..8780a1e8b34 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -3,7 +3,7 @@ from maya import cmds from openpype.pipeline import publish -from openpype.hosts.maya.api.alembic import extract_alembic +from openpype.hosts.maya.api.alembic import ALEMBIC_ARGS, extract_alembic from openpype.hosts.maya.api.lib import ( suspended_refresh, maintained_selection, From 33784f765c73502ff8be34671252bdbf7f143467 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 15 Nov 2023 14:21:10 +0000 Subject: [PATCH 032/163] `maya.api.alembic` Fix missing `cmds` import --- openpype/hosts/maya/api/alembic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/maya/api/alembic.py b/openpype/hosts/maya/api/alembic.py index 75dad62ae15..0fde1cdfb51 100644 --- a/openpype/hosts/maya/api/alembic.py +++ b/openpype/hosts/maya/api/alembic.py @@ -1,3 +1,5 @@ +from maya import cmds # noqa + # The maya alembic export types ALEMBIC_ARGS = { "attr": (list, tuple), From fca2442a8b055848bdfb056b8f306db6775683fa Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 15 Nov 2023 14:22:33 +0000 Subject: [PATCH 033/163] `maya.plugin.pointcache` Rework settings names Improve settings names, remove unnecessary ones and fix the `create` plugin to use the `maya.api.alembic` to find out boolean arguments. --- .../create/create_animation_pointcache.py | 71 +++++++++++++------ .../defaults/project_settings/maya.json | 25 +++---- .../schemas/schema_maya_create.json | 40 ----------- 3 files changed, 57 insertions(+), 79 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index a497ace4966..01bb95f2221 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -30,54 +30,75 @@ def _get_animation_attr_defs(cls): def _get_animation_abc_attr_defs(cls): - """Get definitions relating to Alembic.""" - # List of arguments extracted from AbcExport -h - # Them being here doesn't imply we support them or that we need them at this - # point, it's a convininece list to populate the UI defaults. + """Get definitions relating to Alembic. + Most of the Alembic Arguments are booleans, those are stored in a + `abc_boolean_args` attribute, the other ones are their own attriubte. + + An admin can define in settings the default arguments, which are then not + modifiable by the person publishing, unless they are added to the Alembic + Overrides setting, which is mapped to `abc_args_overrides`. + + We use a combination of the two above to only show a muiltiselection dropdown + for booleans, and disabling the non-boolean arguments on the interface. + + There's also a new separator so it's clearer what belongs to common Animation + publishes versus what is Almebic specific. + """ + abc_defs = None abc_defs = [UISeparatorDef(), UILabelDef("Alembic Options")] # The Arguments that can be modified by the Publisher abc_args_overrides = getattr(cls, "abc_args_overrides", None) - # What we have set in the Settings as default. - abc_boolean_args = getattr(cls, "abc_boolean_args", []) + # All the Boolean Arguments that Alembic Export accepts + abc_boolean_args = [ + arg + for arg, arg_type in ALEMBIC_ARGS.items() + if arg_type is bool + ] + + # What we have set in the Settings as defaults. + abc_settings_boolean_args = getattr(cls, "abc_boolean_args", []) - # Default Flags set in Settings; unless they are editable. + # Default Flags set in Settings; minus the overrideable ones. abc_settings_boolean_arguments = [ arg - for arg in abc_boolean_args + for arg in abc_settings_boolean_args if arg not in abc_args_overrides ] - # We display them to the user + + # We display them to the user, but disable it abc_defs.append(EnumDef( "abcDefaultExportBooleanArguments", abc_settings_boolean_arguments, default=abc_settings_boolean_arguments, multiselection=True, - label="Settings set Arguments", - disabled=True + label="Settings Defined Arguments", + disabled=False )) + # Only display Boolan flags that the Admin defined as overrideable abc_boolean_overrides = [ arg - for arg in abc_args_overrides - if arg in abc_boolean_args + for arg in abc_boolean_args + if arg in abc_args_overrides ] - if abc_boolean_overrides: - abc_defs.append(EnumDef( - "abcExportBooleanArguments", - abc_boolean_overrides, - multiselection=True, - label="Arguments Overrides" - )) + abc_defs.append(EnumDef( + "abcExportBooleanArguments", + abc_boolean_overrides if abc_boolean_overrides else [""], + multiselection=True, + label="Arguments Overrides", + disabled=True if not abc_boolean_overrides else False + )) abc_defs.append( TextDef( "attr", label="Custom Attributes", - placeholder="attr1, attr2, ...", + default=getattr(cls, "attr", None), + placeholder="attr1; attr2; ...", disabled=True if "attr" not in abc_args_overrides else False, ) ) @@ -86,7 +107,8 @@ def _get_animation_abc_attr_defs(cls): TextDef( "attrPrefix", label="Custom Attributes Prefix", - placeholder="prefix1, prefix2, ...", + default=getattr(cls, "attrPrefix", None), + placeholder="prefix1; prefix2; ...", disabled=True if "attrPrefix" not in abc_args_overrides else False, ) ) @@ -95,6 +117,7 @@ def _get_animation_abc_attr_defs(cls): EnumDef( "dataFormat", label="Data Format", + default=getattr(cls, "dataFormat", None), items=["ogawa", "HDF"], disabled=True if "dataFormat" not in abc_args_overrides else False, ) @@ -104,6 +127,7 @@ def _get_animation_abc_attr_defs(cls): NumberDef( "preRollStartFrame", label="Start frame for preroll", + default=getattr(cls, "preRollStartFrame", None), tooltip=( "The frame to start scene evaluation at. This is used to set" " the starting frame for time dependent translations and can" @@ -137,6 +161,9 @@ class CreateAnimation(plugin.MayaHiddenCreator): include_parent_hierarchy = False include_user_defined_attributes = False + def collect_instances(self): + pass + def get_instance_attr_defs(self): super(CreateAnimation, self).get_instance_attr_defs() # adding project settings, since MayaHiddenCreator does not diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 4f6cf68daa1..54721757e7d 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -606,40 +606,31 @@ ], "step": 1.0, "abc_boolean_args": [ - "writeColorSets", - "visibleOnly", - "worldSpace", - "writeNormals" + "selection", + "uvWrite", + "writeCreases", + "writeVisibility" ], "abc_args_overrides": [ + "attr", + "attrPrefix", "step", "writeColorSets", "writeFaceSets", "renderableOnly", - "visibleOnly", - "worldSpace", - "attr", - "attrPrefix" + "worldSpace" ], - "writeColorSets": true, - "writeFaceSets": false, "renderableOnly": false, "visibleOnly": false, "includeParentHierarchy": false, - "worldSpace": true, "farm": false, "priority": 50, "writeNormals": true, - "includeUserDefinedAttributes": true, - "attr": "", + "attr": "cbId", "attrPrefix": "", "dataFormat": "ogawa", - "noNormals": false, "preRollStartFrame": 0, "refresh": false, - "stripNamespaces": false, - "uvWrite": true, - "writeCreases": false, "write_color_sets": false, "write_face_sets": false, "include_user_defined_attributes": false diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index 3317fe7d71b..97125691902 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -422,16 +422,6 @@ "minimum": 0.0, "decimal": 4 }, - { - "type": "boolean", - "key": "writeColorSets", - "label": "Write Color Sets default" - }, - { - "type": "boolean", - "key": "writeFaceSets", - "label": "Write Face Sets default" - }, { "type": "boolean", "key": "renderableOnly", @@ -447,11 +437,6 @@ "key": "includeParentHierarchy", "label": "Include Parent Hierarchy default" }, - { - "type": "boolean", - "key": "worldSpace", - "label": "World Space default" - }, { "type": "boolean", "key": "farm", @@ -468,11 +453,6 @@ "key": "writeNormals", "label": "Write Normals default" }, - { - "type": "boolean", - "key": "includeUserDefinedAttributes", - "label": "Include User Defined Attributes default" - }, { "type": "text", "key": "attr", @@ -496,11 +476,6 @@ } ] }, - { - "type": "boolean", - "key": "noNormals", - "label": "No Normals default" - }, { "type": "number", "key": "preRollStartFrame", @@ -512,21 +487,6 @@ "key": "refresh", "label": "Refresh default" }, - { - "type": "boolean", - "key": "stripNamespaces", - "label": "Strip Namespaces default" - }, - { - "type": "boolean", - "key": "uvWrite", - "label": "UV Write default" - }, - { - "type": "boolean", - "key": "writeCreases", - "label": "Write Creases default" - }, { "type": "boolean", "key": "write_color_sets", From 608c7ebcdcf55dad737c26586e12224e631d0512 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 15 Nov 2023 14:24:26 +0000 Subject: [PATCH 034/163] `maya.extract_pointcache` Adapt to new settings Adapted the extractor to get the settings from the new schema, and simplified the way we build the `alembic_export` arguments dict. --- .../plugins/publish/extract_pointcache.py | 90 +++++++++++-------- 1 file changed, 54 insertions(+), 36 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 8780a1e8b34..96b1438511a 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -37,14 +37,23 @@ def process(self, instance): start = float(instance.data.get("frameStartHandle", 1)) end = float(instance.data.get("frameEndHandle", 1)) - attrs = instance.data.get("attr", "").split(";") - attrs = [value for value in attrs if value.strip()] - attrs += instance.data.get("userDefinedAttributes", []) - attrs += ["cbId"] - - attr_prefixes = instance.data.get("attrPrefix", "").split(";") - attr_prefixes = [value for value in attr_prefixes if value.strip()] - + # Collect Alembic Arguments + creator_attributes = instance.data.get("creator_attributes") + abc_flags = creator_attributes.get( + "abcDefaultExportBooleanArguments" + ) + creator_attributes.get( + "abcExportBooleanArguments" + ) + + abc_attrs = [ + attr.strip() + for attr in creator_attributes.get("attr", "").split(";") + ] + + abc_attr_prefixes = [ + attr_prefix.strip() + for attr_prefix in instance.data.get("attrPrefix", "").split(";") + ] self.log.debug("Extracting pointcache..") dirname = self.staging_dir(instance) @@ -53,28 +62,46 @@ def process(self, instance): filename = "{name}.abc".format(**instance.data) path = os.path.join(parent_dir, filename) - options = { - "step": instance.data.get("step", 1.0), - "attr": attrs, - "attrPrefix": attr_prefixes, - "writeVisibility": True, - "writeCreases": True, - "writeColorSets": instance.data.get("writeColorSets", False), - "writeFaceSets": instance.data.get("writeFaceSets", False), - "uvWrite": True, - "selection": True, - "worldSpace": instance.data.get("worldSpace", True) - } - + abc_root = None if not instance.data.get("includeParentHierarchy", True): # Set the root nodes if we don't want to include parents # The roots are to be considered the ones that are the actual # direct members of the set - options["root"] = roots + abc_root = roots + + abc_writeUVSets = False if int(cmds.about(version=True)) >= 2017: # Since Maya 2017 alembic supports multiple uv sets - write them. - options["writeUVSets"] = True + if "writeUVSets" in abc_flags: + abc_writeUVSets = True + + extract_abc_args = { + "file": path, + "attr": abc_attrs, + "attrPrefix": abc_attr_prefixes, + "dataFormat": creator_attributes.get("dataFormat", "ogawa"), + "endFrame": end, + "eulerFilter": True if "eulerFilter" in abc_flags else False, + "noNormals": True if "noNormals" in abc_flags else False, + "preRoll": True if "preRoll" in abc_flags else False, + "preRollStartFrame": creator_attributes.get("preRollStartFrame", 0), + "renderableOnly": True if "renderableOnly" in abc_flags else False, + "root": abc_root, + "selection": True, # Should this stay like so? + "startFrame": start, + "step": creator_attributes.get("step", 1.0), + "stripNamespaces": True, + "uvWrite": True if "uvWrite" in abc_flags else False, + "verbose": True if "verbose" in abc_flags else False, + "wholeFrameGeo": True if "wholeFrameGeo" in abc_flags else False, + "worldSpace": True if "worldSpace" in abc_flags else False, + "writeColorSets": True if "writeColorSets" in abc_flags else False, + "writeCreases": True if "writeCreases" in abc_flags else False, + "writeFaceSets": True if "writeFaceSets" in abc_flags else False, + "writeUVSets": abc_writeUVSets, + "writeVisibility": True if "writeVisibility" in abc_flags else False, + } if instance.data.get("visibleOnly", False): # If we only want to include nodes that are visible in the frame @@ -90,12 +117,7 @@ def process(self, instance): with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(nodes, noExpand=True) - extract_alembic( - file=path, - startFrame=start, - endFrame=end, - **options - ) + extract_alembic(**extract_abc_args) if "representations" not in instance.data: instance.data["representations"] = [] @@ -119,21 +141,17 @@ def process(self, instance): return path = path.replace(".abc", "_proxy.abc") + extract_abc_args["file"] = path if not instance.data.get("includeParentHierarchy", True): # Set the root nodes if we don't want to include parents # The roots are to be considered the ones that are the actual # direct members of the set - options["root"] = instance.data["proxyRoots"] + extract_abc_args["root"] = instance.data["proxyRoots"] with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(instance.data["proxy"]) - extract_alembic( - file=path, - startFrame=start, - endFrame=end, - **options - ) + extract_alembic(**extract_abc_args) representation = { "name": "proxy", From 70e48831501485b371e35bddd24a4dd1a7065a6e Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 15 Nov 2023 18:18:00 +0000 Subject: [PATCH 035/163] `maya.api.alembic` Fix missing imports --- openpype/hosts/maya/api/alembic.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openpype/hosts/maya/api/alembic.py b/openpype/hosts/maya/api/alembic.py index 0fde1cdfb51..f4bdc18bfa5 100644 --- a/openpype/hosts/maya/api/alembic.py +++ b/openpype/hosts/maya/api/alembic.py @@ -1,5 +1,12 @@ +import os +import logging + from maya import cmds # noqa +from openpype.hosts.maya.api.lib import evaluation + +log = logging.getLogger(__name__) + # The maya alembic export types ALEMBIC_ARGS = { "attr": (list, tuple), From c379e7f4c4def35fca74a0975df96958f67106a9 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 15 Nov 2023 18:20:33 +0000 Subject: [PATCH 036/163] `maya.create_animation_pointcache` Fix publisher widgets population Fix widgets not being properly drawn for each of the Creators. Also removed unnecessary settings, that are now handled by the EnumDef. --- .../create/create_animation_pointcache.py | 1 - .../defaults/project_settings/maya.json | 9 +--- .../schemas/schema_maya_create.json | 43 ++----------------- 3 files changed, 5 insertions(+), 48 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 01bb95f2221..90c89090c62 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -22,7 +22,6 @@ def _get_animation_attr_defs(cls): BoolDef("refresh", label="Refresh viewport during export"), BoolDef("includeParentHierarchy", label="Include Parent Hierarchy"), BoolDef("writeNormals", label="Write Normals"), - BoolDef("writeCreases", label="Write Creases"), ] ) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 54721757e7d..fc380705730 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -576,18 +576,12 @@ "includeParentHierarchy": false, "farm": false, "priority": 50, - "writeNormals": true, "attr": "", "attrPrefix": "", "dataFormat": "ogawa", "preRollStartFrame": 0, "refresh": false, - "stripNamespaces": false, - "writeCreases": false, - "write_color_sets": false, - "write_face_sets": false, - "include_parent_hierarchy": false, - "include_user_defined_attributes": false + "stripNamespaces": false }, "CreateModel": { "enabled": true, @@ -625,7 +619,6 @@ "includeParentHierarchy": false, "farm": false, "priority": 50, - "writeNormals": true, "attr": "cbId", "attrPrefix": "", "dataFormat": "ogawa", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index 97125691902..f30120db8c5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -153,11 +153,6 @@ "label": "Farm Job Priority", "minimum": 0 }, - { - "type": "boolean", - "key": "writeNormals", - "label": "Write Normals" - }, { "type": "boolean", "key": "refresh", @@ -168,11 +163,6 @@ "key": "stripNamespaces", "label": "Strip Namespaces" }, - { - "type": "boolean", - "key": "writeCreases", - "label": "Write Creases" - }, { "type": "enum", "key": "abc_boolean_args", @@ -203,7 +193,7 @@ "type": "enum", "key": "abc_args_overrides", "multiselection": true, - "label": "Alembic Arguments Overrides: Choose which arguments the user can modify.", + "label": "Alembic Arguments Overrides:\nChoose which arguments\nthe user can modify.", "enum_items": [ {"attr": "attr"}, {"attrPrefix": "attrPrefix"}, @@ -251,7 +241,7 @@ { "type": "text", "key": "attr", - "label": "Attr" + "label": "Attributes" }, { "type": "text", @@ -276,26 +266,6 @@ "key": "preRollStartFrame", "label": "Pre Roll Start Frame", "minimum": 0 - }, - { - "type": "boolean", - "key": "write_color_sets", - "label": "DEPRECATED! Write Color Sets" - }, - { - "type": "boolean", - "key": "write_face_sets", - "label": "DEPRECATED! Write Face Sets" - }, - { - "type": "boolean", - "key": "include_parent_hierarchy", - "label": "DEPRECATED! Include Parent Hierarchy" - }, - { - "type": "boolean", - "key": "include_user_defined_attributes", - "label": "DEPRECATED! Include User Defined Attributes" } ] }, @@ -351,7 +321,7 @@ "type": "enum", "key": "abc_boolean_args", "multiselection": true, - "label": "Alembic Arguments (passed to AbcExport)", + "label": "Alembic Boolean Args", "enum_items": [ {"autoSubd": "autoSubd"}, {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, @@ -377,7 +347,7 @@ "type": "enum", "key": "abc_args_overrides", "multiselection": true, - "label": "Alembic Arguments Overrides: Choose which arguments the user can modify.", + "label": "Alembic Arguments Overrides:\nChoose which arguments\nthe user can modify.", "enum_items": [ {"attr": "attr"}, {"attrPrefix": "attrPrefix"}, @@ -448,11 +418,6 @@ "label": "Priority default", "minimum": 0 }, - { - "type": "boolean", - "key": "writeNormals", - "label": "Write Normals default" - }, { "type": "text", "key": "attr", From e700f2878d543ea2564a578e8260374ebf951790 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Mon, 20 Nov 2023 17:34:55 +0000 Subject: [PATCH 037/163] `maya.publish.pointcache` Log out the Alembic argumetns used. --- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 96b1438511a..7eb69da151f 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -55,7 +55,7 @@ def process(self, instance): for attr_prefix in instance.data.get("attrPrefix", "").split(";") ] - self.log.debug("Extracting pointcache..") + self.log.debug("Extracting pointcache...") dirname = self.staging_dir(instance) parent_dir = self.staging_dir(instance) @@ -117,6 +117,7 @@ def process(self, instance): with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(nodes, noExpand=True) + self.log.debug("Running `extract_alembic` with the arguments: {}".format(extract_abc_args)) extract_alembic(**extract_abc_args) if "representations" not in instance.data: From 5935ef5947fe6b39c7b2d2c2fbb9746abe1dc959 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Mon, 20 Nov 2023 17:35:56 +0000 Subject: [PATCH 038/163] `maya.create_animation_pointcache` Ensure we set defaults When attributes change from overridable to not overrideable, we need to discard any value that was set previously by the user and ensure we one feed the defaults to the extractor. --- .../create/create_animation_pointcache.py | 198 ++++++++++++------ 1 file changed, 131 insertions(+), 67 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 90c89090c62..70f2142a730 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -1,7 +1,6 @@ from maya import cmds from openpype.hosts.maya.api import lib, plugin -from openpype.hosts.maya.api.alembic import ALEMBIC_ARGS from openpype.lib import ( BoolDef, TextDef, @@ -10,17 +9,25 @@ UISeparatorDef, UILabelDef, ) +from openpype.pipeline import CreatedInstance def _get_animation_attr_defs(cls): - """Get Animation generic ddefinitions.""" + """Get Animation generic definitions. + + The line is blurry between what's "Animation" generic and "Alembic" is + blurry, but the rule of thumb is that whatever "AlembicExport -h" accepts + is "Alembic" and the other ones are "Animation". + """ defs = lib.collect_animation_defs() defs.extend( [ BoolDef("farm", label="Submit to Farm"), NumberDef("priority", label="Farm job Priority", default=50), BoolDef("refresh", label="Refresh viewport during export"), - BoolDef("includeParentHierarchy", label="Include Parent Hierarchy"), + BoolDef( + "includeParentHierarchy", label="Include Parent Hierarchy" + ), BoolDef("writeNormals", label="Write Normals"), ] ) @@ -28,69 +35,89 @@ def _get_animation_attr_defs(cls): return defs +def _get_alembic_boolean_arguments(cls): + """Get two lists with the Alembic flags. + + Alembic flags are treted as booleans, so here we get all the possible + options, and work out a list with all the ones that can be toggled and the + list of defaults (un-toggleable.) + """ + + # The Arguments that can be modified by the Publisher + abc_args_overrides = getattr(cls, "abc_args_overrides", []) + + # What we have set in the Settings as defaults. + abc_settings_boolean_args = getattr(cls, "abc_boolean_args", []) + + abc_defaults = [ + arg + for arg in abc_settings_boolean_args + if arg not in abc_args_overrides + ] + + abc_overrideable = [ + arg for arg in abc_settings_boolean_args if arg in abc_args_overrides + ] + + return abc_defaults, abc_overrideable + + def _get_animation_abc_attr_defs(cls): """Get definitions relating to Alembic. - Most of the Alembic Arguments are booleans, those are stored in a - `abc_boolean_args` attribute, the other ones are their own attriubte. - An admin can define in settings the default arguments, which are then not modifiable by the person publishing, unless they are added to the Alembic Overrides setting, which is mapped to `abc_args_overrides`. - We use a combination of the two above to only show a muiltiselection dropdown - for booleans, and disabling the non-boolean arguments on the interface. + Most of the Alembic Arguments are flags, treated as booleans, and there are + two possible lists: the defaults (from settings) and the the toggleable by + the user, these two define an EnumDef respectively. + + We use a combination of the two above to only show a muiltiselection + dropdown for booleans, and disabling the non-boolean arguments on the + interface. - There's also a new separator so it's clearer what belongs to common Animation - publishes versus what is Almebic specific. + There's also a new separator so it's clearer what belongs to common + Animation publishes versus what is Almebic specific, the line is blurry, + but the rule of thumb is that whatever "AlembicExport -h" accepts is + "Alembic" and the other ones are "Animation". """ abc_defs = None - abc_defs = [UISeparatorDef(), UILabelDef("Alembic Options")] + abc_defs = [ + UISeparatorDef("sep_alembic_options"), + UILabelDef("Alembic Options"), + ] # The Arguments that can be modified by the Publisher abc_args_overrides = getattr(cls, "abc_args_overrides", None) - # All the Boolean Arguments that Alembic Export accepts - abc_boolean_args = [ - arg - for arg, arg_type in ALEMBIC_ARGS.items() - if arg_type is bool - ] - - # What we have set in the Settings as defaults. - abc_settings_boolean_args = getattr(cls, "abc_boolean_args", []) - - # Default Flags set in Settings; minus the overrideable ones. - abc_settings_boolean_arguments = [ - arg - for arg in abc_settings_boolean_args - if arg not in abc_args_overrides - ] + ( + abc_boolean_defaults, + abc_boolean_overrides, + ) = _get_alembic_boolean_arguments(cls) # We display them to the user, but disable it - abc_defs.append(EnumDef( - "abcDefaultExportBooleanArguments", - abc_settings_boolean_arguments, - default=abc_settings_boolean_arguments, - multiselection=True, - label="Settings Defined Arguments", - disabled=False - )) + abc_defs.append( + EnumDef( + "abcDefaultExportBooleanArguments", + abc_boolean_defaults, + default=abc_boolean_defaults, + multiselection=True, + label="Settings Defined Arguments", + disabled=True, + ) + ) # Only display Boolan flags that the Admin defined as overrideable - abc_boolean_overrides = [ - arg - for arg in abc_boolean_args - if arg in abc_args_overrides - ] - - abc_defs.append(EnumDef( - "abcExportBooleanArguments", - abc_boolean_overrides if abc_boolean_overrides else [""], - multiselection=True, - label="Arguments Overrides", - disabled=True if not abc_boolean_overrides else False - )) + abc_defs.append( + EnumDef( + "abcExportBooleanArguments", + abc_boolean_overrides if abc_boolean_overrides else [""], + multiselection=True, + label="Arguments Overrides", + disabled=True if not abc_boolean_overrides else False, + ) + ) abc_defs.append( TextDef( @@ -141,6 +168,46 @@ def _get_animation_abc_attr_defs(cls): return abc_defs +def _ensure_defaults(cls, instance_data): + """Ensure we get default values when an attribute is not overrideable. + + In instances where an attribute used to be modifiable, and then was locked + again, we want to make sure that we pass the default (what's on the + settings) instead of any value that might have been stored in the scene + when the attribute was modifiable. + """ + abc_args_overrides = getattr(cls, "abc_args_overrides", []) + creator_attr = instance_data["creator_attributes"] + attr_default = getattr(cls, "attr", "") + + if "attr" not in abc_args_overrides: + creator_attr["attr"] = attr_default + + if "attrPrefix" not in abc_args_overrides: + creator_attr["attrPrefix"] = getattr(cls, "attrPrefix", "") + + if "dataFormat" not in abc_args_overrides: + creator_attr["dataFormat"] = getattr(cls, "dataFormat", "") + + if "preRollStartFrame" not in abc_args_overrides: + creator_attr["preRollStartFrame"] = getattr( + cls, "preRollStartFrame", "" + ) + + ( + abc_boolean_defaults, + abc_boolean_overrides, + ) = _get_alembic_boolean_arguments(cls) + + creator_attr["abcDefaultExportBooleanArguments"] = abc_boolean_defaults + + for idx, arg in enumerate(creator_attr["abcExportBooleanArguments"]): + if arg not in abc_boolean_overrides: + del creator_attr["abcExportBooleanArguments"][idx] + + return instance_data + + class CreateAnimation(plugin.MayaHiddenCreator): """Animation output for character rigs @@ -161,26 +228,18 @@ class CreateAnimation(plugin.MayaHiddenCreator): include_user_defined_attributes = False def collect_instances(self): - pass + cached_subsets = self.collection_shared_data["maya_cached_subsets"] + for node in cached_subsets.get(self.identifier, []): + node_data = self.read_instance_node(node) + _ensure_defaults(self, node_data) + + created_instance = CreatedInstance.from_existing(node_data, self) + self._add_instance_to_context(created_instance) def get_instance_attr_defs(self): super(CreateAnimation, self).get_instance_attr_defs() - # adding project settings, since MayaHiddenCreator does not - # handle this for us (yet?) - settings = ( - getattr(self, "project_settings", {}) - .get("maya", {}) - .get("create", {}) - .get("CreateAnimation") - ) - - for key, value in settings.items(): - setattr(self, key, value) - defs = _get_animation_attr_defs(self) - abc_defs = _get_animation_abc_attr_defs(self) - if abc_defs: defs.extend(abc_defs) @@ -198,14 +257,19 @@ class CreatePointCache(plugin.MayaCreator): write_face_sets = False include_user_defined_attributes = False + def collect_instances(self): + cached_subsets = self.collection_shared_data["maya_cached_subsets"] + for node in cached_subsets.get(self.identifier, []): + node_data = self.read_instance_node(node) + _ensure_defaults(self, node_data) + + created_instance = CreatedInstance.from_existing(node_data, self) + self._add_instance_to_context(created_instance) + def get_instance_attr_defs(self): super(CreatePointCache, self).get_instance_attr_defs() - # defs = self.get_instance_attr_defs() - print(self.instance_attr_defs) defs = _get_animation_attr_defs(self) - abc_defs = _get_animation_abc_attr_defs(self) - if abc_defs: defs.extend(abc_defs) From 326c2106cafe5ff9a0617711c5bc6f303b6d83dc Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 21 Nov 2023 17:11:02 +0000 Subject: [PATCH 039/163] `schemas.maya.create` Remove `stripNameSpaces` setting --- .../schemas/projects_schema/schemas/schema_maya_create.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index f30120db8c5..b101a4c0c53 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -158,11 +158,6 @@ "key": "refresh", "label": "Refresh" }, - { - "type": "boolean", - "key": "stripNamespaces", - "label": "Strip Namespaces" - }, { "type": "enum", "key": "abc_boolean_args", From ca7998e2a107d8316cd7a7a9bbbaee4fa9655ca5 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 21 Nov 2023 17:12:10 +0000 Subject: [PATCH 040/163] `maya.settings` Add AYON settings for Animation and PointCache publishes --- server_addon/maya/server/settings/creators.py | 175 +++++++++++++++++- 1 file changed, 166 insertions(+), 9 deletions(-) diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 34a54832af0..7719ee469cf 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -1,3 +1,5 @@ +from typing import Literal + from pydantic import Field from ayon_server.settings import BaseSettingsModel @@ -56,30 +58,141 @@ class BasicExportMeshModel(BaseSettingsModel): ) +def alembic_booleans_enum(): + return [ + "autoSubd", + "dontSkipUnwritten", + "eulerFilter", + "noNormals", + "preRoll", + "renderableOnly", + "selection", + "stripNamespaces", + "uvWrite", + "uvsOnly", + "verbose", + "wholeFrameGeo", + "worldSpace", + "writeColorSets", + "writeCreases", + "writeFaceSets", + "writeUVSets", + "writeVisibility", + ] + +def alembic_arguments_enum(): + return [ + "attr", + "attrPrefix", + "autoSubd", + "dataFormat", + "dontSkipUnwrittenFrames", + "endFrame", + "eulerFilter", + "frameRange", + "frameRelativeSample", + "melPerFrameCallback", + "melPostJobCallback", + "noNormals", + "preRoll", + "preRollStartFrame", + "pythonPerFrameCallback", + "pythonPostJobCallback", + "renderableOnly", + "root", + "selection", + "startFrame", + "step", + "stripNamespaces", + "userAttr", + "userAttrPrefix", + "uvWrite", + "uvsOnly", + "verbose", + "wholeFrameGeo", + "worldSpace", + "writeColorSets", + "writeCreases", + "writeFaceSets", + "writeUVSets", + "writeVisibility", + ] + +AlembicDataFormat = Literal["ogawa", "hdf5"] + +def alembic_data_formats(): + return [ + "ogawa", + "hdf5" + ] + class CreateAnimationModel(BaseSettingsModel): write_color_sets: bool = Field(title="Write Color Sets") write_face_sets: bool = Field(title="Write Face Sets") include_parent_hierarchy: bool = Field( title="Include Parent Hierarchy") - include_user_defined_attributes: bool = Field( - title="Include User Defined Attributes") default_variants: list[str] = Field( default_factory=list, title="Default Products" ) + priority: int = Field( + title="Farm Job Priority") + pre_roll_start_frame: int = Field(title="Pre Roll Start Frame") + refresh: bool = Field( + title="Refresh") + step: int = Field(title="Step") + farm: bool = Field( + title="Submit to the Farm") + attr: str = Field(title="Attributes") + attr_prefix: str = Field(title="Attributes Prefix") + data_format: AlembicDataFormat = Field( + enum_resolver=alembic_data_formats, + title="Data Format", + ) + abc_boolean_args: list[str] = Field( + default_factory=list, + enum_resolver=alembic_booleans_enum, + title="Alembic Boolean Args", + ) + abc_args_overrides: list[str] = Field( + default_factory=list, + enum_resolver=alembic_arguments_enum, + title="Alembic Arguments Overrides", + ) class CreatePointCacheModel(BaseSettingsModel): enabled: bool = Field(title="Enabled") write_color_sets: bool = Field(title="Write Color Sets") write_face_sets: bool = Field(title="Write Face Sets") - include_user_defined_attributes: bool = Field( - title="Include User Defined Attributes" - ) default_variants: list[str] = Field( default_factory=list, title="Default Products" ) + priority: int = Field( + title="Farm Job Priority") + pre_roll_start_frame: int = Field(title="Pre Roll Start Frame") + refresh: bool = Field( + title="Refresh") + step: int = Field(title="Step") + farm: bool = Field( + title="Submit to the Farm") + attr: str = Field(title="Attributes") + attr_prefix: str = Field(title="Attributes Prefix") + data_format: AlembicDataFormat = Field( + enum_resolver=alembic_data_formats, + title="Data Format", + ) + abc_boolean_args: list[str] = Field( + default_factory=list, + enum_resolver=alembic_booleans_enum, + title="Alembic Boolean Args", + ) + abc_args_overrides: list[str] = Field( + default_factory=list, + enum_resolver=alembic_arguments_enum, + title="Alembic Arguments Overrides", + ) class CreateProxyAlembicModel(BaseSettingsModel): @@ -276,10 +389,31 @@ class CreatorsModel(BaseSettingsModel): "write_color_sets": False, "write_face_sets": False, "include_parent_hierarchy": False, - "include_user_defined_attributes": False, "default_variants": [ "Main" - ] + ], + "step": 1.0, + "abc_boolean_args": [ + "writeColorSets", + "visibleOnly", + "worldSpace", + "writeNormals" + ], + "abc_args_overrides": [ + "step", + "includeParentHierarchy", + "writeNormals", + "includeUserDefinedAttributes", + "attr", + "attrPrefix" + ], + "farm": False, + "priority": 50, + "attr": "", + "attr_prefix": "", + "data_format": "ogawa", + "pre_roll_start_frame": 0, + "refresh": False, }, "CreateModel": { "enabled": True, @@ -295,10 +429,33 @@ class CreatorsModel(BaseSettingsModel): "enabled": True, "write_color_sets": False, "write_face_sets": False, - "include_user_defined_attributes": False, "default_variants": [ "Main" - ] + ], + "step": 1.0, + "abc_boolean_args": [ + "selection", + "uvWrite", + "writeCreases", + "writeVisibility" + ], + "abc_args_overrides": [ + "attr", + "attrPrefix", + "step", + "writeColorSets", + "writeFaceSets", + "renderableOnly", + "worldSpace" + ], + "include_parent_hierarchy": False, + "farm": False, + "priority": 50, + "attr": "cbId", + "attr_prefix": "", + "data_format": "ogawa", + "pre_roll_start_frame": 0, + "refresh": False }, "CreateProxyAlembic": { "enabled": True, From 0e905f373f34f8767e73575ef2368d1dad035550 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Tue, 12 Dec 2023 10:00:30 +0000 Subject: [PATCH 041/163] `maya` Fix faulty logic in pointcache creator --- openpype/hosts/maya/api/alembic.py | 3 ++- .../create/create_animation_pointcache.py | 24 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/openpype/hosts/maya/api/alembic.py b/openpype/hosts/maya/api/alembic.py index f4bdc18bfa5..63f826299f8 100644 --- a/openpype/hosts/maya/api/alembic.py +++ b/openpype/hosts/maya/api/alembic.py @@ -1,5 +1,6 @@ -import os +import json import logging +import os from maya import cmds # noqa diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 70f2142a730..4c8f81c7839 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -44,20 +44,20 @@ def _get_alembic_boolean_arguments(cls): """ # The Arguments that can be modified by the Publisher - abc_args_overrides = getattr(cls, "abc_args_overrides", []) + abc_args_overrides = set(getattr(cls, "abc_args_overrides", [])) # What we have set in the Settings as defaults. - abc_settings_boolean_args = getattr(cls, "abc_boolean_args", []) + abc_settings_boolean_args = set(getattr(cls, "abc_boolean_args", [])) - abc_defaults = [ + abc_defaults = { arg for arg in abc_settings_boolean_args if arg not in abc_args_overrides - ] + } - abc_overrideable = [ + abc_overrideable = { arg for arg in abc_settings_boolean_args if arg in abc_args_overrides - ] + } return abc_defaults, abc_overrideable @@ -96,7 +96,6 @@ def _get_animation_abc_attr_defs(cls): abc_boolean_overrides, ) = _get_alembic_boolean_arguments(cls) - # We display them to the user, but disable it abc_defs.append( EnumDef( "abcDefaultExportBooleanArguments", @@ -105,6 +104,7 @@ def _get_animation_abc_attr_defs(cls): multiselection=True, label="Settings Defined Arguments", disabled=True, + hidden=True ) ) @@ -201,9 +201,10 @@ def _ensure_defaults(cls, instance_data): creator_attr["abcDefaultExportBooleanArguments"] = abc_boolean_defaults - for idx, arg in enumerate(creator_attr["abcExportBooleanArguments"]): - if arg not in abc_boolean_overrides: - del creator_attr["abcExportBooleanArguments"][idx] + creator_attr["abcExportBooleanArguments"] = [ + arg for arg in creator_attr["abcExportBooleanArguments"] + if arg not in abc_boolean_overrides + ] return instance_data @@ -284,6 +285,3 @@ def create(self, subset_name, instance_data, pre_create_data): # For Arnold standin proxy proxy_set = cmds.sets(name=instance_node + "_proxy_SET", empty=True) cmds.sets(proxy_set, forceElement=instance_node) - - def apply_settings(self, project_settings): - super(CreatePointCache, self).apply_settings(project_settings) From 6c04a6df1a120eead69fb90b192e26017e95f334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= Date: Thu, 21 Dec 2023 11:53:35 +0100 Subject: [PATCH 042/163] :bug: fix default render product name --- openpype/hosts/houdini/plugins/publish/collect_vray_rop.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py index a1f45547268..f60d8c664cf 100644 --- a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py +++ b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py @@ -67,7 +67,7 @@ def process(self, instance): beauty_product = self.get_render_product_name(default_prefix) render_products.append(beauty_product) files_by_aov = { - "RGB Color": self.generate_expected_files(instance, + "": self.generate_expected_files(instance, beauty_product)} if instance.data.get("RenderElement", True): @@ -75,7 +75,9 @@ def process(self, instance): if render_element: for aov, renderpass in render_element.items(): render_products.append(renderpass) - files_by_aov[aov] = self.generate_expected_files(instance, renderpass) # noqa + files_by_aov[aov] = self.generate_expected_files( + instance, renderpass) + for product in render_products: self.log.debug("Found render product: %s" % product) From f2f83454b56c755567526c64618c43808cf5660b Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Thu, 4 Jan 2024 17:07:51 +0000 Subject: [PATCH 043/163] `maya.create_animation_pointcache` Ensure there are `maya_cached_subsets` in `collection_shared_data` `maya_cached_subsets` might not exist at this time. It is run in `collect_instances()` in `MayaHiddenCreator` but not here. --- .../plugins/create/create_animation_pointcache.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 4c8f81c7839..3c782de88b9 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -229,7 +229,12 @@ class CreateAnimation(plugin.MayaHiddenCreator): include_user_defined_attributes = False def collect_instances(self): - cached_subsets = self.collection_shared_data["maya_cached_subsets"] + try: + cached_subsets = self.collection_shared_data["maya_cached_subsets"] + except KeyError: + self.cache_subsets(self.collection_shared_data) + cached_subsets = self.collection_shared_data["maya_cached_subsets"] + for node in cached_subsets.get(self.identifier, []): node_data = self.read_instance_node(node) _ensure_defaults(self, node_data) @@ -259,7 +264,12 @@ class CreatePointCache(plugin.MayaCreator): include_user_defined_attributes = False def collect_instances(self): - cached_subsets = self.collection_shared_data["maya_cached_subsets"] + try: + cached_subsets = self.collection_shared_data["maya_cached_subsets"] + except KeyError: + self.cache_subsets(self.collection_shared_data) + cached_subsets = self.collection_shared_data["maya_cached_subsets"] + for node in cached_subsets.get(self.identifier, []): node_data = self.read_instance_node(node) _ensure_defaults(self, node_data) From 04dfe1863f267b210f6a3dc57b8f9890957e8d3c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 8 Jan 2024 20:46:27 +0800 Subject: [PATCH 044/163] Implement the validator for camera attributes in Max host --- openpype/hosts/max/api/action.py | 39 +++++++++++ .../publish/validate_camera_attributes.py | 59 +++++++++++++++++ openpype/pipeline/publish/lib.py | 2 +- .../defaults/project_settings/max.json | 10 +++ .../schemas/schema_max_publish.json | 66 +++++++++++++++++++ .../max/server/settings/publishers.py | 26 +++++++- server_addon/max/server/version.py | 2 +- 7 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 openpype/hosts/max/api/action.py create mode 100644 openpype/hosts/max/plugins/publish/validate_camera_attributes.py diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py new file mode 100644 index 00000000000..506847652bb --- /dev/null +++ b/openpype/hosts/max/api/action.py @@ -0,0 +1,39 @@ +from pymxs import runtime as rt + +import pyblish.api + +from openpype.pipeline.publish import get_errored_instances_from_context + + +class SelectInvalidAction(pyblish.api.Action): + """Select invalid objects in Blender when a publish plug-in failed.""" + label = "Select Invalid" + on = "failed" + icon = "search" + + def process(self, context, plugin): + errored_instances = get_errored_instances_from_context(context, + plugin=plugin) + + # Get the invalid nodes for the plug-ins + self.log.info("Finding invalid nodes...") + invalid = list() + for instance in errored_instances: + invalid_nodes = plugin.get_invalid(instance) + if invalid_nodes: + if isinstance(invalid_nodes, (list, tuple)): + invalid.extend(invalid_nodes) + else: + self.log.warning( + "Failed plug-in doesn't have any selectable objects." + ) + + if not invalid: + self.log.info("No invalid nodes found.") + return + invalid_names = [obj.name for obj in invalid] + self.log.info( + "Selecting invalid objects: %s", ", ".join(invalid_names) + ) + + rt.Select(invalid) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py new file mode 100644 index 00000000000..e279b414757 --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -0,0 +1,59 @@ +import pyblish.api +from pymxs import runtime as rt + +from openpype.pipeline.publish import ( + OptionalPyblishPluginMixin, + PublishValidationError +) +from openpype.hosts.max.api.action import SelectInvalidAction + + +class ValidateCameraAttributes(OptionalPyblishPluginMixin, + pyblish.api.InstancePlugin): + """Validates Camera has no invalid attribute properties + or values.(For 3dsMax Cameras only) + + """ + + order = pyblish.api.ValidatorOrder + families = ['camera'] + hosts = ['max'] + label = 'Camera Attributes' + actions = [SelectInvalidAction] + optional = True + + DEFAULTS = ["fov", "nearrange", "farrange", + "nearclip","farclip"] + CAM_TYPE = ["Freecamera", "Targetcamera", + "Physical"] + + @classmethod + def get_invalid(cls, instance): + invalid = [] + cameras = instance.data["members"] + project_settings = instance.context.data["project_settings"].get("max") + cam_attr_settings = project_settings["publish"]["ValidateCameraAttributes"] + for camera in cameras: + if str(rt.ClassOf(camera)) not in cls.CAM_TYPE: + cls.log.debug( + "Skipping camera created from external plugin..") + continue + for attr in cls.DEFAULTS: + default_value = cam_attr_settings.get(attr) + if rt.getProperty(camera, attr) != default_value: + cls.log.error( + f"Invalid attribute value: {attr} " + f"(should be: {default_value}))") + invalid.append(camera) + + return invalid + + def process(self, instance): + if not self.is_active(instance.data): + self.log.debug("Skipping Validate Camera Attributes...") + return + invalid = self.get_invalid(instance) + + if invalid: + raise PublishValidationError( + f"Invalid camera attributes: {invalid}") diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py index 4ea2f932f14..87ca3323cbd 100644 --- a/openpype/pipeline/publish/lib.py +++ b/openpype/pipeline/publish/lib.py @@ -74,7 +74,7 @@ def get_template_name_profiles( project_settings ["global"] ["publish"] - ["IntegrateAssetNew"] + ["IntegrateHeroVersion"] ["template_name_profiles"] ) if legacy_profiles: diff --git a/openpype/settings/defaults/project_settings/max.json b/openpype/settings/defaults/project_settings/max.json index d1610610dc6..a0a4fcf83d8 100644 --- a/openpype/settings/defaults/project_settings/max.json +++ b/openpype/settings/defaults/project_settings/max.json @@ -56,6 +56,16 @@ "enabled": false, "attributes": {} }, + "ValidateCameraAttributes": { + "enabled": true, + "optional": true, + "active": false, + "fov": 45.0, + "nearrange": 0.0, + "farrange": 1000.0, + "nearclip": 1.0, + "farclip": 1000.0 + }, "ValidateLoadedPlugin": { "enabled": false, "optional": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index b4d85bda988..90a5d2fc18d 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -48,6 +48,72 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "ValidateCameraAttributes", + "label": "Validate Camera Attributes", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "optional", + "label": "Optional" + }, + { + "type": "boolean", + "key": "active", + "label": "Active" + }, + { + "type": "number", + "key": "fov", + "label": "Focal Length", + "decimal": 1, + "minimum": 0, + "maximum": 100.0 + }, + { + "type": "number", + "key": "nearrange", + "label": "Near Range", + "decimal": 1, + "minimum": 0, + "maximum": 100.0 + }, + { + "type": "number", + "key": "farrange", + "label": "Far Range", + "decimal": 1, + "minimum": 0, + "maximum": 2000.0 + }, + { + "type": "number", + "key": "nearclip", + "label": "Near Clip", + "decimal": 1, + "minimum": 0, + "maximum": 100.0 + }, + { + "type": "number", + "key": "farclip", + "label": "Far Clip", + "decimal": 1, + "minimum": 0, + "maximum": 2000.0 + } + ] + }, + { "type": "dict", "collapsible": true, diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index d40d85a99be..340f344a9f9 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -27,6 +27,17 @@ def validate_json(cls, value): return value +class ValidateCameraAttributesModel(BaseSettingsModel): + enabled: bool = Field(title="Enabled") + optional: bool = Field(title="Optional") + active: bool = Field(title="Active") + fov: float = Field(0.0, title="Focal Length") + nearrange: float = Field(0.0, title="Near Range") + farrange: float = Field(0.0, title="Far Range") + nearclip: float = Field(0.0, title="Near Clip") + farclip: float = Field(0.0, title="Far Clip") + + class FamilyMappingItemModel(BaseSettingsModel): product_types: list[str] = Field( default_factory=list, @@ -63,7 +74,10 @@ class PublishersModel(BaseSettingsModel): default_factory=ValidateAttributesModel, title="Validate Attributes" ) - + ValidateCameraAttributes: ValidateCameraAttributesModel = Field( + default_factory=ValidateCameraAttributesModel, + title="Validate Camera Attributes" + ) ValidateLoadedPlugin: ValidateLoadedPluginModel = Field( default_factory=ValidateLoadedPluginModel, title="Validate Loaded Plugin" @@ -101,6 +115,16 @@ class PublishersModel(BaseSettingsModel): "enabled": False, "attributes": "{}" }, + "ValidateCameraAttributes": { + "enabled": True, + "optional": True, + "active": False, + "fov": 45.0, + "nearrange": 0.0, + "farrange": 1000.0, + "nearclip": 1.0, + "farclip": 1000.0 + }, "ValidateLoadedPlugin": { "enabled": False, "optional": True, diff --git a/server_addon/max/server/version.py b/server_addon/max/server/version.py index bbab0242f6a..1276d0254ff 100644 --- a/server_addon/max/server/version.py +++ b/server_addon/max/server/version.py @@ -1 +1 @@ -__version__ = "0.1.4" +__version__ = "0.1.5" From c29a4fdfd64dccb22375749b766b9e01aca9d815 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 8 Jan 2024 21:08:51 +0800 Subject: [PATCH 045/163] hound shut --- .../hosts/max/plugins/publish/validate_camera_attributes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index e279b414757..d97d5529e3d 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -23,7 +23,7 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, optional = True DEFAULTS = ["fov", "nearrange", "farrange", - "nearclip","farclip"] + "nearclip", "farclip"] CAM_TYPE = ["Freecamera", "Targetcamera", "Physical"] @@ -32,7 +32,9 @@ def get_invalid(cls, instance): invalid = [] cameras = instance.data["members"] project_settings = instance.context.data["project_settings"].get("max") - cam_attr_settings = project_settings["publish"]["ValidateCameraAttributes"] + cam_attr_settings = ( + project_settings["publish"]["ValidateCameraAttributes"] + ) for camera in cameras: if str(rt.ClassOf(camera)) not in cls.CAM_TYPE: cls.log.debug( From 3c878ccc3e34d8bb6884e731cc035881a0449947 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 11 Jan 2024 13:27:32 +0800 Subject: [PATCH 046/163] make sure the validator skips checking when the value of the attribute sets to zero --- .../max/plugins/publish/validate_camera_attributes.py | 11 ++++++++--- .../projects_schema/schemas/schema_max_publish.json | 4 ++++ server_addon/max/server/settings/publishers.py | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index d97d5529e3d..36f0beab65d 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -18,7 +18,7 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, order = pyblish.api.ValidatorOrder families = ['camera'] hosts = ['max'] - label = 'Camera Attributes' + label = 'Validate Camera Attributes' actions = [SelectInvalidAction] optional = True @@ -42,9 +42,14 @@ def get_invalid(cls, instance): continue for attr in cls.DEFAULTS: default_value = cam_attr_settings.get(attr) + if default_value == 0: + cls.log.debug( + f"the value of {attr} in setting set to" + " zero. Skipping the check..") + continue if rt.getProperty(camera, attr) != default_value: cls.log.error( - f"Invalid attribute value: {attr} " + f"Invalid attribute value for {camera.name}:{attr} " f"(should be: {default_value}))") invalid.append(camera) @@ -58,4 +63,4 @@ def process(self, instance): if invalid: raise PublishValidationError( - f"Invalid camera attributes: {invalid}") + "Invalid camera attributes found. See log.") diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json index 90a5d2fc18d..1e7a7c0c739 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_max_publish.json @@ -79,6 +79,10 @@ "minimum": 0, "maximum": 100.0 }, + { + "type": "label", + "label": "If the value of the camera attributes set to 0, the system automatically skips checking it" + }, { "type": "number", "key": "nearrange", diff --git a/server_addon/max/server/settings/publishers.py b/server_addon/max/server/settings/publishers.py index 340f344a9f9..a092f54a62b 100644 --- a/server_addon/max/server/settings/publishers.py +++ b/server_addon/max/server/settings/publishers.py @@ -76,7 +76,11 @@ class PublishersModel(BaseSettingsModel): ) ValidateCameraAttributes: ValidateCameraAttributesModel = Field( default_factory=ValidateCameraAttributesModel, - title="Validate Camera Attributes" + title="Validate Camera Attributes", + description=( + "If the value of the camera attributes set to 0, " + "the system automatically skips checking it" + ) ) ValidateLoadedPlugin: ValidateLoadedPluginModel = Field( default_factory=ValidateLoadedPluginModel, From 9baff5eccfea5e2c7a5dc8c15efc4b75a916799f Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 11 Jan 2024 18:04:19 +0800 Subject: [PATCH 047/163] add repair actions for validate camera attributes --- .../publish/validate_camera_attributes.py | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index 36f0beab65d..94aa5742d82 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -2,6 +2,7 @@ from pymxs import runtime as rt from openpype.pipeline.publish import ( + RepairAction, OptionalPyblishPluginMixin, PublishValidationError ) @@ -19,7 +20,7 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, families = ['camera'] hosts = ['max'] label = 'Validate Camera Attributes' - actions = [SelectInvalidAction] + actions = [SelectInvalidAction, RepairAction] optional = True DEFAULTS = ["fov", "nearrange", "farrange", @@ -42,10 +43,11 @@ def get_invalid(cls, instance): continue for attr in cls.DEFAULTS: default_value = cam_attr_settings.get(attr) - if default_value == 0: + cls.log.debug(f"default value: {default_value}") + if default_value == float(0): cls.log.debug( f"the value of {attr} in setting set to" - " zero. Skipping the check..") + " zero. Skipping the check.") continue if rt.getProperty(camera, attr) != default_value: cls.log.error( @@ -57,10 +59,26 @@ def get_invalid(cls, instance): def process(self, instance): if not self.is_active(instance.data): - self.log.debug("Skipping Validate Camera Attributes...") + self.log.debug("Skipping Validate Camera Attributes.") return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( "Invalid camera attributes found. See log.") + + @classmethod + def repair(cls, instance): + invalid_cameras = cls.get_invalid(instance) + project_settings = instance.context.data["project_settings"].get("max") + cam_attr_settings = ( + project_settings["publish"]["ValidateCameraAttributes"] + ) + for camera in invalid_cameras: + for attr in cls.DEFAULTS: + expected_value = cam_attr_settings.get(attr) + if expected_value == float(0): + cls.log.debug( + f"the value of {attr} in setting set to zero.") + continue + rt.setProperty(camera, attr, expected_value) From 15167fa0b805b03657857097db52dd554c163620 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 11 Jan 2024 22:46:21 +0800 Subject: [PATCH 048/163] the validator posts on warning message if the users dont use generic types & round 1 decimal places for all check values --- .../max/plugins/publish/validate_camera_attributes.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py index 94aa5742d82..4eec1951e5d 100644 --- a/openpype/hosts/max/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/max/plugins/publish/validate_camera_attributes.py @@ -31,6 +31,11 @@ class ValidateCameraAttributes(OptionalPyblishPluginMixin, @classmethod def get_invalid(cls, instance): invalid = [] + if rt.units.DisplayType != rt.Name("Generic"): + cls.log.warning( + "Generic Type is not used as a scene unit\n\n" + "sure you tweak the settings with your own values\n\n" + "before validation.") cameras = instance.data["members"] project_settings = instance.context.data["project_settings"].get("max") cam_attr_settings = ( @@ -43,13 +48,12 @@ def get_invalid(cls, instance): continue for attr in cls.DEFAULTS: default_value = cam_attr_settings.get(attr) - cls.log.debug(f"default value: {default_value}") if default_value == float(0): cls.log.debug( f"the value of {attr} in setting set to" " zero. Skipping the check.") continue - if rt.getProperty(camera, attr) != default_value: + if round(rt.getProperty(camera, attr), 1) != default_value: cls.log.error( f"Invalid attribute value for {camera.name}:{attr} " f"(should be: {default_value}))") From e0e2cb06dd8d85ec73482285a75606bcf26b99ea Mon Sep 17 00:00:00 2001 From: jrsndlr Date: Fri, 12 Jan 2024 11:20:19 +0100 Subject: [PATCH 049/163] Slate support, duration fix, fps fix --- openpype/hosts/resolve/api/lib.py | 4 ++++ openpype/hosts/resolve/api/plugin.py | 16 +++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/resolve/api/lib.py b/openpype/hosts/resolve/api/lib.py index 3866477c77b..f7150e945b5 100644 --- a/openpype/hosts/resolve/api/lib.py +++ b/openpype/hosts/resolve/api/lib.py @@ -279,6 +279,10 @@ def create_timeline_item( # timing variables if all([timeline_in, source_start, source_end]): fps = timeline.GetSetting("timelineFrameRate") + # Strangely, Resolve seem to output '23' instead of 23.976 + if fps == '23': + fps = 23.976 + duration = source_end - source_start timecode_in = frames_to_timecode(timeline_in, fps) timecode_out = frames_to_timecode(timeline_in + duration, fps) diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index a00933405f1..a6123ed5d00 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -408,7 +408,14 @@ def load(self, files): _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) source_out = int(_clip_property("End")) - source_duration = int(_clip_property("Frames")) + + # Trim clip start if slate is present + if "slate" in self.data["versionData"]["families"]: + print("LOAD: slate found ") + source_in += 1 + print("LOAD media pool item source in slate : {}".format(source_in)) + # Calculate source duration excluding slate + source_duration = source_out - source_in + 1 if not self.with_handles: # Load file without the handles of the source media @@ -435,13 +442,12 @@ def load(self, files): handle_start = version_data.get("handleStart", 0) handle_end = version_data.get("handleEnd", 0) frame_start_handle = frame_start - handle_start - frame_end_handle = frame_start + handle_end + frame_end_handle = frame_end + handle_end database_frame_duration = int( frame_end_handle - frame_start_handle + 1 ) - if source_duration >= database_frame_duration: - source_in += handle_start - source_out -= handle_end + source_in += handle_start + source_out -= handle_end # get timeline in timeline_start = self.active_timeline.GetStartFrame() From 5e309d01e0bc0a5ce02679f3df2c7b74bb23fae5 Mon Sep 17 00:00:00 2001 From: jrsndlr Date: Fri, 12 Jan 2024 11:26:45 +0100 Subject: [PATCH 050/163] hound --- openpype/hosts/resolve/api/plugin.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index a6123ed5d00..77e2991d3f2 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -411,11 +411,7 @@ def load(self, files): # Trim clip start if slate is present if "slate" in self.data["versionData"]["families"]: - print("LOAD: slate found ") source_in += 1 - print("LOAD media pool item source in slate : {}".format(source_in)) - # Calculate source duration excluding slate - source_duration = source_out - source_in + 1 if not self.with_handles: # Load file without the handles of the source media From dbe0b9c5b36e49987fdf9794c84964ba7b042cf8 Mon Sep 17 00:00:00 2001 From: jrsndlr Date: Fri, 12 Jan 2024 16:59:06 +0100 Subject: [PATCH 051/163] revert to conditional handles trim --- openpype/hosts/resolve/api/plugin.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 77e2991d3f2..d254e5213c9 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -412,6 +412,7 @@ def load(self, files): # Trim clip start if slate is present if "slate" in self.data["versionData"]["families"]: source_in += 1 + source_duration = source_out - source_in + 1 if not self.with_handles: # Load file without the handles of the source media @@ -442,8 +443,9 @@ def load(self, files): database_frame_duration = int( frame_end_handle - frame_start_handle + 1 ) - source_in += handle_start - source_out -= handle_end + if source_duration >= database_frame_duration: + source_in += handle_start + source_out -= handle_end # get timeline in timeline_start = self.active_timeline.GetStartFrame() From 749b3b41dd1b32c78a2a9176891b7ae38c95d95d Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Mon, 15 Jan 2024 10:10:13 +0000 Subject: [PATCH 052/163] `maya.create_animation_pointcache` Fix `_ensure_defaults` logic When traversing the `bool`ean argumetns, we not check if there are any before we actually traverse it, and we also cast the list to as set for performance improvements. --- .../plugins/create/create_animation_pointcache.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 3c782de88b9..40d854c5b32 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -201,10 +201,14 @@ def _ensure_defaults(cls, instance_data): creator_attr["abcDefaultExportBooleanArguments"] = abc_boolean_defaults - creator_attr["abcExportBooleanArguments"] = [ - arg for arg in creator_attr["abcExportBooleanArguments"] - if arg not in abc_boolean_overrides - ] + if creator_attr.get("abcExportBooleanArguments", []): + abc_boolean_overrides = set(abc_boolean_overrides) + abc_boolean_args = creator_attr["abcExportBooleanArguments"].copy() + + creator_attr["abcExportBooleanArguments"] = [ + arg for arg in abc_boolean_args + if arg not in abc_boolean_overrides + ] return instance_data From afc7967e351d3ccea410b0d1fb768c220a846baa Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Mon, 15 Jan 2024 18:05:07 +0000 Subject: [PATCH 053/163] `maya.creat_pointcache` Fix boolean arguments not being correctly parsed and presented The logic that was filtering out non-booleans for display was faulty, we not rely on the `maya.lib.alembic` module to sort them out. --- .../create/create_animation_pointcache.py | 36 +++++++++++-------- .../defaults/project_settings/maya.json | 3 +- .../schemas/schema_maya_create.json | 5 +++ 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 40d854c5b32..dc807a031dc 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -1,6 +1,8 @@ from maya import cmds from openpype.hosts.maya.api import lib, plugin +from openpype.hosts.maya.api.alembic import ALEMBIC_ARGS + from openpype.lib import ( BoolDef, TextDef, @@ -27,8 +29,7 @@ def _get_animation_attr_defs(cls): BoolDef("refresh", label="Refresh viewport during export"), BoolDef( "includeParentHierarchy", label="Include Parent Hierarchy" - ), - BoolDef("writeNormals", label="Write Normals"), + ) ] ) @@ -36,30 +37,36 @@ def _get_animation_attr_defs(cls): def _get_alembic_boolean_arguments(cls): - """Get two lists with the Alembic flags. + """Get two sets with the Alembic flags. Alembic flags are treted as booleans, so here we get all the possible options, and work out a list with all the ones that can be toggled and the list of defaults (un-toggleable.) """ + all_alembic_booleans = { + arg + for arg, arg_type in ALEMBIC_ARGS.items() + if arg_type is bool + } # The Arguments that can be modified by the Publisher - abc_args_overrides = set(getattr(cls, "abc_args_overrides", [])) + abc_args_overrides = set(getattr(cls, "abc_args_overrides", set())) # What we have set in the Settings as defaults. - abc_settings_boolean_args = set(getattr(cls, "abc_boolean_args", [])) + abc_settings_boolean_args = set(getattr(cls, "abc_boolean_args", set())) - abc_defaults = { + abc_boolean_args = { arg for arg in abc_settings_boolean_args if arg not in abc_args_overrides } - abc_overrideable = { - arg for arg in abc_settings_boolean_args if arg in abc_args_overrides + abc_args_overrides = { + arg + for arg in abc_args_overrides + if arg in all_alembic_booleans } - - return abc_defaults, abc_overrideable + return abc_boolean_args, abc_args_overrides def _get_animation_abc_attr_defs(cls): @@ -99,8 +106,8 @@ def _get_animation_abc_attr_defs(cls): abc_defs.append( EnumDef( "abcDefaultExportBooleanArguments", - abc_boolean_defaults, - default=abc_boolean_defaults, + list(abc_boolean_defaults), + default=list(abc_boolean_defaults), multiselection=True, label="Settings Defined Arguments", disabled=True, @@ -112,7 +119,7 @@ def _get_animation_abc_attr_defs(cls): abc_defs.append( EnumDef( "abcExportBooleanArguments", - abc_boolean_overrides if abc_boolean_overrides else [""], + list(abc_boolean_overrides) if abc_boolean_overrides else [""], multiselection=True, label="Arguments Overrides", disabled=True if not abc_boolean_overrides else False, @@ -199,10 +206,9 @@ def _ensure_defaults(cls, instance_data): abc_boolean_overrides, ) = _get_alembic_boolean_arguments(cls) - creator_attr["abcDefaultExportBooleanArguments"] = abc_boolean_defaults + creator_attr["abcDefaultExportBooleanArguments"] = list(abc_boolean_defaults) if creator_attr.get("abcExportBooleanArguments", []): - abc_boolean_overrides = set(abc_boolean_overrides) abc_boolean_args = creator_attr["abcExportBooleanArguments"].copy() creator_attr["abcExportBooleanArguments"] = [ diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index fc380705730..3c7edcc7695 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -561,7 +561,6 @@ "step": 1.0, "abc_boolean_args": [ "writeColorSets", - "visibleOnly", "worldSpace", "writeNormals" ], @@ -581,7 +580,7 @@ "dataFormat": "ogawa", "preRollStartFrame": 0, "refresh": false, - "stripNamespaces": false + "visibleOnly": false }, "CreateModel": { "enabled": true, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index b101a4c0c53..5ae4397a87a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -233,6 +233,11 @@ "minimum": 0.0, "decimal": 4 }, + { + "type": "boolean", + "key": "visibleOnly", + "label": "Visible Only default" + }, { "type": "text", "key": "attr", From d387dca0272c9be2a80beb52ac8272cabb10f426 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 26 Jan 2024 10:44:13 +0000 Subject: [PATCH 054/163] Use duration from streams as its more precise --- openpype/plugins/publish/extract_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 10eb2614829..37f7ea77371 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -445,7 +445,7 @@ def _create_frame_from_video(self, video_file_path, output_dir): # Set video input attributes max_int = str(2147483647) video_data = get_ffprobe_data(video_file_path, logger=self.log) - duration = float(video_data["format"]["duration"]) + duration = float(video_data["streams"][0]["duration"]) cmd_args = [ "-y", From e6818f94f5fead64b6ff8767462064fef8e10a41 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 29 Jan 2024 09:17:07 +0000 Subject: [PATCH 055/163] Illicit suggestion --- openpype/plugins/publish/extract_thumbnail.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 37f7ea77371..8a1e9fd12d0 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -445,7 +445,11 @@ def _create_frame_from_video(self, video_file_path, output_dir): # Set video input attributes max_int = str(2147483647) video_data = get_ffprobe_data(video_file_path, logger=self.log) - duration = float(video_data["streams"][0]["duration"]) + duration = max( + float(stream.get("duration", 0)) + for stream in video_data["streams"] + if stream.get("codec_type") == "video" + ) cmd_args = [ "-y", From 3f8830e27d9c7f4002985ebcf05f7fe395fc3e17 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 29 Jan 2024 10:13:18 +0000 Subject: [PATCH 056/163] Update openpype/plugins/publish/extract_thumbnail.py --- openpype/plugins/publish/extract_thumbnail.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 8a1e9fd12d0..2e272b061b2 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -445,6 +445,10 @@ def _create_frame_from_video(self, video_file_path, output_dir): # Set video input attributes max_int = str(2147483647) video_data = get_ffprobe_data(video_file_path, logger=self.log) + # Use duration of the individual streams since it is returned with + # higher decimal precision than 'format.duration'. We need this + # more precise value for calculating the correct amount of frames + # for higher FPS ranges or decimal ranges, e.g. 29.97 FPS duration = max( float(stream.get("duration", 0)) for stream in video_data["streams"] From 3807172faed3addc22790c6831065d7424e08035 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 29 Jan 2024 10:13:40 +0000 Subject: [PATCH 057/163] Update openpype/plugins/publish/extract_thumbnail.py --- openpype/plugins/publish/extract_thumbnail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/plugins/publish/extract_thumbnail.py b/openpype/plugins/publish/extract_thumbnail.py index 2e272b061b2..cbeada6bac4 100644 --- a/openpype/plugins/publish/extract_thumbnail.py +++ b/openpype/plugins/publish/extract_thumbnail.py @@ -448,7 +448,7 @@ def _create_frame_from_video(self, video_file_path, output_dir): # Use duration of the individual streams since it is returned with # higher decimal precision than 'format.duration'. We need this # more precise value for calculating the correct amount of frames - # for higher FPS ranges or decimal ranges, e.g. 29.97 FPS + # for higher FPS ranges or decimal ranges, e.g. 29.97 FPS duration = max( float(stream.get("duration", 0)) for stream in video_data["streams"] From 97e8ccd5c5209f11e9c822fd573e758abd094d39 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 31 Jan 2024 15:29:48 +0000 Subject: [PATCH 058/163] `server_addons.maya` Fix creator settings using legacy `Field` --- server_addon/maya/server/settings/creators.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 886a4b797b7..0707a204585 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -173,26 +173,26 @@ class CreatePointCacheModel(BaseSettingsModel): default_factory=list, title="Default Products" ) - priority: int = Field( + priority: int = SettingsField( title="Farm Job Priority") - pre_roll_start_frame: int = Field(title="Pre Roll Start Frame") - refresh: bool = Field( + pre_roll_start_frame: int = SettingsField(title="Pre Roll Start Frame") + refresh: bool = SettingsField( title="Refresh") - step: int = Field(title="Step") - farm: bool = Field( + step: int = SettingsField(title="Step") + farm: bool = SettingsField( title="Submit to the Farm") - attr: str = Field(title="Attributes") - attr_prefix: str = Field(title="Attributes Prefix") - data_format: AlembicDataFormat = Field( + attr: str = SettingsField(title="Attributes") + attr_prefix: str = SettingsField(title="Attributes Prefix") + data_format: AlembicDataFormat = SettingsField( enum_resolver=alembic_data_formats, title="Data Format", ) - abc_boolean_args: list[str] = Field( + abc_boolean_args: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_booleans_enum, title="Alembic Boolean Args", ) - abc_args_overrides: list[str] = Field( + abc_args_overrides: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_arguments_enum, title="Alembic Arguments Overrides", From 8c999fbea9061ae2bc02d8c6bdd26e7b90d94c60 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 31 Jan 2024 15:35:46 +0000 Subject: [PATCH 059/163] `server_addons.maya` Fix creator settings using legacy `Field` --- server_addon/maya/server/settings/creators.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 0707a204585..7e26980b923 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -136,26 +136,26 @@ class CreateAnimationModel(BaseSettingsModel): default_factory=list, title="Default Products" ) - priority: int = Field( + priority: int = SettingsField( title="Farm Job Priority") - pre_roll_start_frame: int = Field(title="Pre Roll Start Frame") - refresh: bool = Field( + pre_roll_start_frame: int = SettingsField(title="Pre Roll Start Frame") + refresh: bool = SettingsField( title="Refresh") - step: int = Field(title="Step") - farm: bool = Field( + step: int = SettingsField(title="Step") + farm: bool = SettingsField( title="Submit to the Farm") - attr: str = Field(title="Attributes") - attr_prefix: str = Field(title="Attributes Prefix") - data_format: AlembicDataFormat = Field( + attr: str = SettingsField(title="Attributes") + attr_prefix: str = SettingsField(title="Attributes Prefix") + data_format: AlembicDataFormat = SettingsField( enum_resolver=alembic_data_formats, title="Data Format", ) - abc_boolean_args: list[str] = Field( + abc_boolean_args: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_booleans_enum, title="Alembic Boolean Args", ) - abc_args_overrides: list[str] = Field( + abc_args_overrides: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_arguments_enum, title="Alembic Arguments Overrides", From 8e284aaa4dc7e0e272c00432f8a8f5b21ee37e23 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 31 Jan 2024 16:25:53 +0000 Subject: [PATCH 060/163] `settings.maya` Rename Alembic settings This commit attempts to better name and label the Alembic settings, used in the creation and extraction of Alembic pointcaches. --- .../defaults/project_settings/maya.json | 8 +++---- .../schemas/schema_maya_create.json | 16 ++++++------- server_addon/maya/server/settings/creators.py | 24 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 28e8cfefa42..214a4f737ac 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -559,12 +559,12 @@ "CreateAnimation": { "default_variants": [], "step": 1.0, - "abc_boolean_args": [ + "abc_export_flags": [ "writeColorSets", "worldSpace", "writeNormals" ], - "abc_args_overrides": [ + "abc_export_overrides": [ "step", "includeParentHierarchy", "writeNormals", @@ -598,13 +598,13 @@ "Main" ], "step": 1.0, - "abc_boolean_args": [ + "abc_export_flags": [ "selection", "uvWrite", "writeCreases", "writeVisibility" ], - "abc_args_overrides": [ + "abc_export_overrides": [ "attr", "attrPrefix", "step", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index 5ae4397a87a..6f9ead7fec1 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -160,9 +160,9 @@ }, { "type": "enum", - "key": "abc_boolean_args", + "key": "abc_export_flags", "multiselection": true, - "label": "Alembic Boolean Args", + "label": "Export Flags (.abc)", "enum_items": [ {"autoSubd": "autoSubd"}, {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, @@ -186,9 +186,9 @@ }, { "type": "enum", - "key": "abc_args_overrides", + "key": "abc_export_overrides", "multiselection": true, - "label": "Alembic Arguments Overrides:\nChoose which arguments\nthe user can modify.", + "label": "Export Overrides (.abc)", "enum_items": [ {"attr": "attr"}, {"attrPrefix": "attrPrefix"}, @@ -319,9 +319,9 @@ }, { "type": "enum", - "key": "abc_boolean_args", + "key": "abc_export_flags", "multiselection": true, - "label": "Alembic Boolean Args", + "label": "Export Flags (.abc)", "enum_items": [ {"autoSubd": "autoSubd"}, {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, @@ -345,9 +345,9 @@ }, { "type": "enum", - "key": "abc_args_overrides", + "key": "abc_export_overrides", "multiselection": true, - "label": "Alembic Arguments Overrides:\nChoose which arguments\nthe user can modify.", + "label": "Export Overrides (.abc)", "enum_items": [ {"attr": "attr"}, {"attrPrefix": "attrPrefix"}, diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 7e26980b923..2fc5a66818c 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -150,15 +150,15 @@ class CreateAnimationModel(BaseSettingsModel): enum_resolver=alembic_data_formats, title="Data Format", ) - abc_boolean_args: list[str] = SettingsField( + abc_export_flags: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_booleans_enum, - title="Alembic Boolean Args", + title="Export Flags (.abc)", ) - abc_args_overrides: list[str] = SettingsField( + abc_export_overrides: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_arguments_enum, - title="Alembic Arguments Overrides", + title="Export Overrides (.abc)", ) @@ -187,15 +187,15 @@ class CreatePointCacheModel(BaseSettingsModel): enum_resolver=alembic_data_formats, title="Data Format", ) - abc_boolean_args: list[str] = SettingsField( + abc_export_flags: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_booleans_enum, - title="Alembic Boolean Args", + title="Export Flags (.abc)", ) - abc_args_overrides: list[str] = SettingsField( + abc_export_overrides: list[str] = SettingsField( default_factory=list, enum_resolver=alembic_arguments_enum, - title="Alembic Arguments Overrides", + title="Export Overrides (.abc)", ) @@ -399,13 +399,13 @@ class CreatorsModel(BaseSettingsModel): "Main" ], "step": 1.0, - "abc_boolean_args": [ + "abc_export_flags": [ "writeColorSets", "visibleOnly", "worldSpace", "writeNormals" ], - "abc_args_overrides": [ + "abc_export_overrides": [ "step", "includeParentHierarchy", "writeNormals", @@ -439,13 +439,13 @@ class CreatorsModel(BaseSettingsModel): "Main" ], "step": 1.0, - "abc_boolean_args": [ + "abc_export_flags": [ "selection", "uvWrite", "writeCreases", "writeVisibility" ], - "abc_args_overrides": [ + "abc_export_overrides": [ "attr", "attrPrefix", "step", From 265239770e5d34e481f944cd8a9588dd0a23184d Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 31 Jan 2024 16:27:29 +0000 Subject: [PATCH 061/163] `maya.plugins.pointcache` Fix togglable flags defaults and adapt to new settings names We weren't setting defaults when some admin defined flag was added to the toggleable flags, this commit will now pre-select those. It also adapts to the (hopefully) better named settings for the Alembic export. --- .../create/create_animation_pointcache.py | 84 +++++++++++-------- .../plugins/publish/extract_pointcache.py | 41 +++++---- 2 files changed, 68 insertions(+), 57 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index dc807a031dc..e57e198ac40 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -17,8 +17,8 @@ def _get_animation_attr_defs(cls): """Get Animation generic definitions. - The line is blurry between what's "Animation" generic and "Alembic" is - blurry, but the rule of thumb is that whatever "AlembicExport -h" accepts + The line is blurry between what's "Animation" generic and "Alembic", + but the rule of thumb is that whatever "AlembicExport -h" accepts is "Alembic" and the other ones are "Animation". """ defs = lib.collect_animation_defs() @@ -36,37 +36,41 @@ def _get_animation_attr_defs(cls): return defs -def _get_alembic_boolean_arguments(cls): - """Get two sets with the Alembic flags. +def _get_abc_export_flags(cls): + """Get two sets with the Alembic Export flags. - Alembic flags are treted as booleans, so here we get all the possible - options, and work out a list with all the ones that can be toggled and the - list of defaults (un-toggleable.) + Alembic flags are treated as booleans, so here we get all the possible + options, and work out a list with all the ones that can be toggled by + the user, and the ones defined in the settings. """ - all_alembic_booleans = { - arg - for arg, arg_type in ALEMBIC_ARGS.items() - if arg_type is bool - } # The Arguments that can be modified by the Publisher - abc_args_overrides = set(getattr(cls, "abc_args_overrides", set())) + abc_export_overrides = set(getattr(cls, "abc_export_overrides", set())) # What we have set in the Settings as defaults. - abc_settings_boolean_args = set(getattr(cls, "abc_boolean_args", set())) + default_abc_export_flags = set(getattr(cls, "abc_export_flags", set())) + + # Set of un-toggleable flags, specified by the settings + abc_export_flags = { + arg + for arg in default_abc_export_flags + if arg not in abc_export_overrides + } - abc_boolean_args = { + # Set of all the available Alembic Export Flags + abc_boolean_flags = { arg - for arg in abc_settings_boolean_args - if arg not in abc_args_overrides + for arg, arg_type in ALEMBIC_ARGS.items() + if arg_type is bool } - abc_args_overrides = { + # Set of togglable flags + abc_export_toggleable_flags = { arg - for arg in abc_args_overrides - if arg in all_alembic_booleans + for arg in abc_export_overrides + if arg in abc_boolean_flags } - return abc_boolean_args, abc_args_overrides + return abc_export_flags, abc_export_toggleable_flags def _get_animation_abc_attr_defs(cls): @@ -98,16 +102,19 @@ def _get_animation_abc_attr_defs(cls): # The Arguments that can be modified by the Publisher abc_args_overrides = getattr(cls, "abc_args_overrides", None) + # What we have set in the Settings as defaults. + default_abc_export_flags = set(getattr(cls, "abc_export_flags", set())) + ( - abc_boolean_defaults, - abc_boolean_overrides, - ) = _get_alembic_boolean_arguments(cls) + abc_export_flags, + abc_export_toggleable_flags, + ) = _get_abc_export_flags(cls) abc_defs.append( EnumDef( - "abcDefaultExportBooleanArguments", - list(abc_boolean_defaults), - default=list(abc_boolean_defaults), + "abcExportFlags", + list(abc_export_flags), + default=list(abc_export_flags), multiselection=True, label="Settings Defined Arguments", disabled=True, @@ -116,13 +123,18 @@ def _get_animation_abc_attr_defs(cls): ) # Only display Boolan flags that the Admin defined as overrideable + abc_export_toggleable_defaults = [ + for arg in abc_export_toggleable_flags + if arg in default_abc_export_flags + ] abc_defs.append( EnumDef( - "abcExportBooleanArguments", - list(abc_boolean_overrides) if abc_boolean_overrides else [""], + "abcExportTogglableFlags", + list(abc_export_toggleable_flags) if abc_export_toggleable_flags else [""], + default=abc_export_toggleable_defaults, multiselection=True, - label="Arguments Overrides", - disabled=True if not abc_boolean_overrides else False, + label="Export Flags", + disabled=True if not abc_export_toggleable_flags else False, ) ) @@ -204,14 +216,14 @@ def _ensure_defaults(cls, instance_data): ( abc_boolean_defaults, abc_boolean_overrides, - ) = _get_alembic_boolean_arguments(cls) + ) = _get_abc_export_flags(cls) - creator_attr["abcDefaultExportBooleanArguments"] = list(abc_boolean_defaults) + creator_attr["abcExportFlags"] = list(abc_boolean_defaults) - if creator_attr.get("abcExportBooleanArguments", []): - abc_boolean_args = creator_attr["abcExportBooleanArguments"].copy() + if creator_attr.get("abcExportTogglableFlags", []): + abc_boolean_args = creator_attr["abcExportTogglableFlags"].copy() - creator_attr["abcExportBooleanArguments"] = [ + creator_attr["abcExportTogglableFlags"] = [ arg for arg in abc_boolean_args if arg not in abc_boolean_overrides ] diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 7eb69da151f..9a6f192e034 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -3,11 +3,11 @@ from maya import cmds from openpype.pipeline import publish -from openpype.hosts.maya.api.alembic import ALEMBIC_ARGS, extract_alembic +from openpype.hosts.maya.api.alembic import extract_alembic from openpype.hosts.maya.api.lib import ( suspended_refresh, maintained_selection, - iter_visible_nodes_in_range + iter_visible_nodes_in_range, ) @@ -40,14 +40,11 @@ def process(self, instance): # Collect Alembic Arguments creator_attributes = instance.data.get("creator_attributes") abc_flags = creator_attributes.get( - "abcDefaultExportBooleanArguments" - ) + creator_attributes.get( - "abcExportBooleanArguments" - ) + "abcExportTogglableFlags" + ) + creator_attributes.get("abcExportTogglableFlags") abc_attrs = [ - attr.strip() - for attr in creator_attributes.get("attr", "").split(";") + attr.strip() for attr in creator_attributes.get("attr", "").split(";") ] abc_attr_prefixes = [ @@ -88,7 +85,7 @@ def process(self, instance): "preRollStartFrame": creator_attributes.get("preRollStartFrame", 0), "renderableOnly": True if "renderableOnly" in abc_flags else False, "root": abc_root, - "selection": True, # Should this stay like so? + "selection": True, # Should this stay like so? "startFrame": start, "step": creator_attributes.get("step", 1.0), "stripNamespaces": True, @@ -109,15 +106,17 @@ def process(self, instance): # flag does not filter out those that are only hidden on some # frames as it counts "animated" or "connected" visibilities as # if it's always visible. - nodes = list(iter_visible_nodes_in_range(nodes, - start=start, - end=end)) + nodes = list(iter_visible_nodes_in_range(nodes, start=start, end=end)) suspend = not instance.data.get("refresh", False) with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(nodes, noExpand=True) - self.log.debug("Running `extract_alembic` with the arguments: {}".format(extract_abc_args)) + self.log.debug( + "Running `extract_alembic` with the arguments: {}".format( + extract_abc_args + ) + ) extract_alembic(**extract_abc_args) if "representations" not in instance.data: @@ -127,7 +126,7 @@ def process(self, instance): "name": "abc", "ext": "abc", "files": filename, - "stagingDir": dirname + "stagingDir": dirname, } instance.data["representations"].append(representation) @@ -159,7 +158,7 @@ def process(self, instance): "ext": "abc", "files": os.path.basename(path), "stagingDir": dirname, - "outputName": "proxy" + "outputName": "proxy", } instance.data["representations"].append(representation) @@ -172,18 +171,18 @@ class ExtractAnimation(ExtractAlembic): families = ["animation"] def get_members_and_roots(self, instance): - # Collect the out set nodes out_sets = [node for node in instance if node.endswith("out_SET")] if len(out_sets) != 1: - raise RuntimeError("Couldn't find exactly one out_SET: " - "{0}".format(out_sets)) + raise RuntimeError( + "Couldn't find exactly one out_SET: " "{0}".format(out_sets) + ) out_set = out_sets[0] roots = cmds.sets(out_set, query=True) # Include all descendants - nodes = roots + cmds.listRelatives(roots, - allDescendents=True, - fullPath=True) or [] + nodes = ( + roots + cmds.listRelatives(roots, allDescendents=True, fullPath=True) or [] + ) return nodes, roots From 76a6e2ec1a897cc846ac5dbd75ee114d8619cf44 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Wed, 31 Jan 2024 16:37:20 +0000 Subject: [PATCH 062/163] `maya.plugins` Fix create pointcache and apply format --- .../create/create_animation_pointcache.py | 20 +++++++++---------- .../plugins/publish/extract_pointcache.py | 19 +++++++++++++----- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index e57e198ac40..00002768cd0 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -29,7 +29,7 @@ def _get_animation_attr_defs(cls): BoolDef("refresh", label="Refresh viewport during export"), BoolDef( "includeParentHierarchy", label="Include Parent Hierarchy" - ) + ), ] ) @@ -59,16 +59,12 @@ def _get_abc_export_flags(cls): # Set of all the available Alembic Export Flags abc_boolean_flags = { - arg - for arg, arg_type in ALEMBIC_ARGS.items() - if arg_type is bool + arg for arg, arg_type in ALEMBIC_ARGS.items() if arg_type is bool } # Set of togglable flags abc_export_toggleable_flags = { - arg - for arg in abc_export_overrides - if arg in abc_boolean_flags + arg for arg in abc_export_overrides if arg in abc_boolean_flags } return abc_export_flags, abc_export_toggleable_flags @@ -118,19 +114,22 @@ def _get_animation_abc_attr_defs(cls): multiselection=True, label="Settings Defined Arguments", disabled=True, - hidden=True + hidden=True, ) ) # Only display Boolan flags that the Admin defined as overrideable abc_export_toggleable_defaults = [ + arg for arg in abc_export_toggleable_flags if arg in default_abc_export_flags ] abc_defs.append( EnumDef( "abcExportTogglableFlags", - list(abc_export_toggleable_flags) if abc_export_toggleable_flags else [""], + list(abc_export_toggleable_flags) + if abc_export_toggleable_flags + else [""], default=abc_export_toggleable_defaults, multiselection=True, label="Export Flags", @@ -224,8 +223,7 @@ def _ensure_defaults(cls, instance_data): abc_boolean_args = creator_attr["abcExportTogglableFlags"].copy() creator_attr["abcExportTogglableFlags"] = [ - arg for arg in abc_boolean_args - if arg not in abc_boolean_overrides + arg for arg in abc_boolean_args if arg not in abc_boolean_overrides ] return instance_data diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 9a6f192e034..4bfd5e2fe0b 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -44,7 +44,8 @@ def process(self, instance): ) + creator_attributes.get("abcExportTogglableFlags") abc_attrs = [ - attr.strip() for attr in creator_attributes.get("attr", "").split(";") + attr.strip() + for attr in creator_attributes.get("attr", "").split(";") ] abc_attr_prefixes = [ @@ -82,7 +83,9 @@ def process(self, instance): "eulerFilter": True if "eulerFilter" in abc_flags else False, "noNormals": True if "noNormals" in abc_flags else False, "preRoll": True if "preRoll" in abc_flags else False, - "preRollStartFrame": creator_attributes.get("preRollStartFrame", 0), + "preRollStartFrame": creator_attributes.get( + "preRollStartFrame", 0 + ), "renderableOnly": True if "renderableOnly" in abc_flags else False, "root": abc_root, "selection": True, # Should this stay like so? @@ -97,7 +100,9 @@ def process(self, instance): "writeCreases": True if "writeCreases" in abc_flags else False, "writeFaceSets": True if "writeFaceSets" in abc_flags else False, "writeUVSets": abc_writeUVSets, - "writeVisibility": True if "writeVisibility" in abc_flags else False, + "writeVisibility": True + if "writeVisibility" in abc_flags + else False, } if instance.data.get("visibleOnly", False): @@ -106,7 +111,9 @@ def process(self, instance): # flag does not filter out those that are only hidden on some # frames as it counts "animated" or "connected" visibilities as # if it's always visible. - nodes = list(iter_visible_nodes_in_range(nodes, start=start, end=end)) + nodes = list( + iter_visible_nodes_in_range(nodes, start=start, end=end) + ) suspend = not instance.data.get("refresh", False) with suspended_refresh(suspend=suspend): @@ -182,7 +189,9 @@ def get_members_and_roots(self, instance): # Include all descendants nodes = ( - roots + cmds.listRelatives(roots, allDescendents=True, fullPath=True) or [] + roots + + cmds.listRelatives(roots, allDescendents=True, fullPath=True) + or [] ) return nodes, roots From 534bc8295f8b894db9c31c5b1fa8c7ff8eb5e503 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Thu, 1 Feb 2024 10:52:45 +0000 Subject: [PATCH 063/163] `server_addons.maya.settings` fix missing `Literal` type in creators --- server_addon/maya/server/settings/creators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 2fc5a66818c..872984a06b4 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -1,3 +1,5 @@ +from typing import Literal + from ayon_server.settings import ( BaseSettingsModel, SettingsField, From 43bbf5af8e941cf5f17f88b96db31989a3eaf787 Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Thu, 1 Feb 2024 12:36:28 +0000 Subject: [PATCH 064/163] `server_addons.maya.settings` Fix missing `creator` defaults --- server_addon/maya/server/settings/creators.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 872984a06b4..492b2c180d1 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -396,6 +396,7 @@ class CreatorsModel(BaseSettingsModel): "CreateAnimation": { "write_color_sets": False, "write_face_sets": False, + "include_user_defined_attributes": False, "include_parent_hierarchy": False, "default_variants": [ "Main" @@ -437,6 +438,7 @@ class CreatorsModel(BaseSettingsModel): "enabled": True, "write_color_sets": False, "write_face_sets": False, + "include_user_defined_attributes": False, "default_variants": [ "Main" ], From 87b3d7fcec70be06e77f56cf4756d7b22adffdcb Mon Sep 17 00:00:00 2001 From: Oscar Domingo Date: Thu, 1 Feb 2024 16:15:04 +0000 Subject: [PATCH 065/163] `hosts.maya.create` Ensure we get a `set` in `abc_args_overrides` in `create_pointcache` --- .../hosts/maya/plugins/create/create_animation_pointcache.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 00002768cd0..353a4402ee8 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -96,7 +96,7 @@ def _get_animation_abc_attr_defs(cls): ] # The Arguments that can be modified by the Publisher - abc_args_overrides = getattr(cls, "abc_args_overrides", None) + abc_args_overrides = set(getattr(cls, "abc_args_overrides", set())) # What we have set in the Settings as defaults. default_abc_export_flags = set(getattr(cls, "abc_export_flags", set())) @@ -194,7 +194,7 @@ def _ensure_defaults(cls, instance_data): settings) instead of any value that might have been stored in the scene when the attribute was modifiable. """ - abc_args_overrides = getattr(cls, "abc_args_overrides", []) + abc_args_overrides = set(getattr(cls, "abc_args_overrides", set())) creator_attr = instance_data["creator_attributes"] attr_default = getattr(cls, "attr", "") From 14fc4ce6be35f02096ecc56cc865300ef67a705c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 17:51:52 +0800 Subject: [PATCH 066/163] Implementation of workfile creator --- .../max/plugins/create/create_workfile.py | 130 ++++++++++++++++++ .../max/plugins/publish/collect_members.py | 5 +- .../max/plugins/publish/collect_workfile.py | 14 +- 3 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 openpype/hosts/max/plugins/create/create_workfile.py diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py new file mode 100644 index 00000000000..b43a3531360 --- /dev/null +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +"""Creator plugin for creating workfiles.""" +from openpype import AYON_SERVER_ENABLED +from openpype.pipeline import CreatedInstance, AutoCreator, CreatorError +from openpype.client import get_asset_by_name, get_asset_name_identifier +from openpype.hosts.max.api import plugin +from openpype.hosts.max.api.lib import read, imprint +from pymxs import runtime as rt + + +class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator): + """Workfile auto-creator.""" + identifier = "io.openpype.creators.max.workfile" + label = "Workfile" + family = "workfile" + icon = "fa5.file" + + default_variant = "Main" + + def create(self): + variant = self.default_variant + current_instance = next( + ( + instance for instance in self.create_context.instances + if instance.creator_identifier == self.identifier + ), None) + project_name = self.project_name + asset_name = self.create_context.get_current_asset_name() + task_name = self.create_context.get_current_task_name() + host_name = self.create_context.host_name + + if current_instance is None: + current_instance_asset = None + elif AYON_SERVER_ENABLED: + current_instance_asset = current_instance["folderPath"] + else: + current_instance_asset = current_instance["asset"] + + if current_instance is None: + asset_doc = get_asset_by_name(project_name, asset_name) + data = { + "task": task_name, + "variant": variant + } + if AYON_SERVER_ENABLED: + data["folderPath"] = asset_name + else: + data["asset"] = asset_name + + data.update( + self.get_dynamic_data( + variant, task_name, asset_doc, + project_name, host_name, current_instance) + ) + subset_name = self.get_subset_name( + variant, task_name, asset_doc, project_name, host_name + ) + self.log.info("Auto-creating workfile instance...") + instance_node = self.create_node(subset_name) + data["instance_node"] = instance_node.name + current_instance = CreatedInstance( + self.family, subset_name, data, self + ) + self._add_instance_to_context(current_instance) + imprint(instance_node.name, current_instance.data) + elif ( + current_instance_asset != asset_name + or current_instance["task"] != task_name + ): + # Update instance context if is not the same + asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + variant, task_name, asset_doc, project_name, host_name + ) + asset_name = get_asset_name_identifier(asset_doc) + + if AYON_SERVER_ENABLED: + current_instance["folderPath"] = asset_name + else: + current_instance["asset"] = asset_name + current_instance["task"] = task_name + current_instance["subset"] = subset_name + + def collect_instances(self): + self.cache_subsets(self.collection_shared_data) + for instance in self.collection_shared_data["max_cached_subsets"].get(self.identifier, []): # noqa + if not rt.getNodeByName(instance): + continue + created_instance = CreatedInstance.from_existing( + read(rt.GetNodeByName(instance)), self + ) + self._add_instance_to_context(created_instance) + + + def update_instances(self, update_list): + for created_inst, changes in update_list: + instance_node = created_inst.get("instance_node") + new_values = { + key: changes[key].new_value + for key in changes.changed_keys + } + + imprint( + instance_node, + created_inst.data_to_store() + ) + + def remove_instances(self, instances): + """Remove specified instance from the scene. + + This is only removing `id` parameter so instance is no longer + instance, because it might contain valuable data for artist. + + """ + for instance in instances: + instance_node = rt.GetNodeByName( + instance.data.get("instance_node")) + if instance_node: + rt.Delete(instance_node) + + self._remove_instance_from_context(instance) + + + def create_node(self, subset_name): + if rt.getNodeByName(subset_name): + node = rt.getNodeByName(subset_name) + return node + node = rt.Container(name=subset_name) + node.isHidden = True + return node diff --git a/openpype/hosts/max/plugins/publish/collect_members.py b/openpype/hosts/max/plugins/publish/collect_members.py index 2970cf0e247..7cd646e0e72 100644 --- a/openpype/hosts/max/plugins/publish/collect_members.py +++ b/openpype/hosts/max/plugins/publish/collect_members.py @@ -12,7 +12,10 @@ class CollectMembers(pyblish.api.InstancePlugin): hosts = ['max'] def process(self, instance): - + if instance.data["family"] == "workfile": + self.log.debug("Skipping Actions for workfile family.") + self.log.debug("{}".format(instance.data["subset"])) + return if instance.data.get("instance_node"): container = rt.GetNodeByName(instance.data["instance_node"]) instance.data["members"] = [ diff --git a/openpype/hosts/max/plugins/publish/collect_workfile.py b/openpype/hosts/max/plugins/publish/collect_workfile.py index 0eb4bb731e1..446175c0ed0 100644 --- a/openpype/hosts/max/plugins/publish/collect_workfile.py +++ b/openpype/hosts/max/plugins/publish/collect_workfile.py @@ -6,15 +6,16 @@ from pymxs import runtime as rt -class CollectWorkfile(pyblish.api.ContextPlugin): +class CollectWorkfile(pyblish.api.InstancePlugin): """Inject the current working file into context""" order = pyblish.api.CollectorOrder - 0.01 label = "Collect 3dsmax Workfile" hosts = ['max'] - def process(self, context): + def process(self, instance): """Inject the current working file.""" + context = instance.context folder = rt.maxFilePath file = rt.maxFileName if not folder or not file: @@ -23,15 +24,12 @@ def process(self, context): context.data['currentFile'] = current_file - filename, ext = os.path.splitext(file) - - task = context.data["task"] + ext = os.path.splitext(file)[-1].lstrip(".") data = {} # create instance - instance = context.create_instance(name=filename) - subset = 'workfile' + task.capitalize() + subset = instance.data["subset"] data.update({ "subset": subset, @@ -55,7 +53,7 @@ def process(self, context): }] instance.data.update(data) - + self.log.info('Collected data: {}'.format(data)) self.log.info('Collected instance: {}'.format(file)) self.log.info('Scene path: {}'.format(current_file)) self.log.info('staging Dir: {}'.format(folder)) From 8bac54fcf0745645489a29fc2e78bac1d9d4c284 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 17:54:39 +0800 Subject: [PATCH 067/163] move back the subset name before dynamic data --- openpype/hosts/max/plugins/create/create_workfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index b43a3531360..70944b3e2cb 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -38,6 +38,9 @@ def create(self): if current_instance is None: asset_doc = get_asset_by_name(project_name, asset_name) + subset_name = self.get_subset_name( + variant, task_name, asset_doc, project_name, host_name + ) data = { "task": task_name, "variant": variant @@ -52,9 +55,6 @@ def create(self): variant, task_name, asset_doc, project_name, host_name, current_instance) ) - subset_name = self.get_subset_name( - variant, task_name, asset_doc, project_name, host_name - ) self.log.info("Auto-creating workfile instance...") instance_node = self.create_node(subset_name) data["instance_node"] = instance_node.name From af022f3561ac96b3d891b52238219ddd913d5f5c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 17:59:20 +0800 Subject: [PATCH 068/163] hound shut --- openpype/hosts/max/plugins/create/create_workfile.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index 70944b3e2cb..25213871d8c 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator plugin for creating workfiles.""" from openpype import AYON_SERVER_ENABLED -from openpype.pipeline import CreatedInstance, AutoCreator, CreatorError +from openpype.pipeline import CreatedInstance, AutoCreator from openpype.client import get_asset_by_name, get_asset_name_identifier from openpype.hosts.max.api import plugin from openpype.hosts.max.api.lib import read, imprint @@ -91,15 +91,9 @@ def collect_instances(self): ) self._add_instance_to_context(created_instance) - def update_instances(self, update_list): for created_inst, changes in update_list: instance_node = created_inst.get("instance_node") - new_values = { - key: changes[key].new_value - for key in changes.changed_keys - } - imprint( instance_node, created_inst.data_to_store() @@ -120,7 +114,6 @@ def remove_instances(self, instances): self._remove_instance_from_context(instance) - def create_node(self, subset_name): if rt.getNodeByName(subset_name): node = rt.getNodeByName(subset_name) From 2248240306fdf01dc2f756982b11304c48304cc5 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 18:00:08 +0800 Subject: [PATCH 069/163] hound shut --- openpype/hosts/max/plugins/create/create_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index 25213871d8c..4ec3c6d3ad1 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -92,7 +92,7 @@ def collect_instances(self): self._add_instance_to_context(created_instance) def update_instances(self, update_list): - for created_inst, changes in update_list: + for created_inst, _ in update_list: instance_node = created_inst.get("instance_node") imprint( instance_node, From bd8135f4dcea13240c8a894b9f70472b7a535851 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 7 Feb 2024 18:01:15 +0800 Subject: [PATCH 070/163] hound shut --- openpype/hosts/max/plugins/create/create_workfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/max/plugins/create/create_workfile.py b/openpype/hosts/max/plugins/create/create_workfile.py index 4ec3c6d3ad1..30692ccd064 100644 --- a/openpype/hosts/max/plugins/create/create_workfile.py +++ b/openpype/hosts/max/plugins/create/create_workfile.py @@ -93,7 +93,7 @@ def collect_instances(self): def update_instances(self, update_list): for created_inst, _ in update_list: - instance_node = created_inst.get("instance_node") + instance_node = created_inst.get("instance_node") imprint( instance_node, created_inst.data_to_store() From 547b56d65bd848b7d04deb57ce4ef1944284570d Mon Sep 17 00:00:00 2001 From: farrizabalaga Date: Wed, 7 Feb 2024 16:19:22 -0500 Subject: [PATCH 071/163] Move imports to workaround importing qt if using headless --- openpype/pype_commands.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index f744337c672..78be3ed3a7f 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -97,8 +97,6 @@ def publish(paths, targets=None, gui=False): install_openpype_plugins, get_global_context, ) - from openpype.tools.utils.host_tools import show_publish - from openpype.tools.utils.lib import qt_app_context # Register target and host import pyblish.api @@ -149,7 +147,12 @@ def publish(paths, targets=None, gui=False): for plugin in plugins: print(plugin) + print("HELLO?? why is gui being true") + print(gui) + log.warning(gui) if gui: + from openpype.tools.utils.host_tools import show_publish + from openpype.tools.utils.lib import qt_app_context with qt_app_context(): show_publish() else: From 06ddeb7b6bfa4637871961065f0c0e13856fe738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabi=C3=A0=20Serra=20Arrizabalaga?= Date: Wed, 7 Feb 2024 16:24:02 -0500 Subject: [PATCH 072/163] Update pype_commands.py --- openpype/pype_commands.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/openpype/pype_commands.py b/openpype/pype_commands.py index 78be3ed3a7f..7c4fd8f1a38 100644 --- a/openpype/pype_commands.py +++ b/openpype/pype_commands.py @@ -147,9 +147,6 @@ def publish(paths, targets=None, gui=False): for plugin in plugins: print(plugin) - print("HELLO?? why is gui being true") - print(gui) - log.warning(gui) if gui: from openpype.tools.utils.host_tools import show_publish from openpype.tools.utils.lib import qt_app_context From 3c47798d121215505c7b0349482f7947f1b79dbe Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 8 Feb 2024 20:07:16 +0800 Subject: [PATCH 073/163] edit the action.py too regarding to #6164 --- openpype/hosts/max/api/action.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/max/api/action.py b/openpype/hosts/max/api/action.py index 506847652bb..c3c1957af10 100644 --- a/openpype/hosts/max/api/action.py +++ b/openpype/hosts/max/api/action.py @@ -31,7 +31,10 @@ def process(self, context, plugin): if not invalid: self.log.info("No invalid nodes found.") return - invalid_names = [obj.name for obj in invalid] + invalid_names = [obj.name for obj in invalid if isinstance(obj, str)] + if not invalid_names: + invalid_names = [obj.name for obj, _ in invalid] + invalid = [obj for obj, _ in invalid] self.log.info( "Selecting invalid objects: %s", ", ".join(invalid_names) ) From fa404cb9404a4b0642dec6f1f3325f7e7cad0e8f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 15 Feb 2024 17:14:24 +0000 Subject: [PATCH 074/163] Fix settings colorspace from representation data. --- openpype/hosts/nuke/api/utils.py | 33 ++++-- openpype/hosts/nuke/plugins/load/load_clip.py | 112 +++++++++++++----- 2 files changed, 103 insertions(+), 42 deletions(-) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index a7df1dee71d..41857bd4ca8 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -103,33 +103,42 @@ def colorspace_exists_on_node(node, colorspace_name): except ValueError: # knob is not available on input node return False - all_clrs = get_colorspace_list(colorspace_knob) - return colorspace_name in all_clrs + return colorspace_name in get_colorspace_list(colorspace_knob) def get_colorspace_list(colorspace_knob): """Get available colorspace profile names + Because the values returned from colorspace_knob.values() do not correspond + to the value returned from colorspace_knob.value(), and the extracted + colorspace comes from using colorspace_knob.value(), we need to iterate + through all values to get the correct value. + + A code example of the above would be: + + for count, value in enumerate(colorspace_knob.values()): + colorspace_knob.setValue(count) + print(colorspace_knob.value() in colorspace_knob.values()) + Args: colorspace_knob (nuke.Knob): nuke knob object Returns: list: list of strings names of profiles """ + original_value = colorspace_knob.value() - all_clrs = list(colorspace_knob.values()) - reduced_clrs = [] + colorspaces = [] - if not colorspace_knob.getFlag(nuke.STRIP_CASCADE_PREFIX): - return all_clrs - - # strip colorspace with nested path - for clrs in all_clrs: - clrs = clrs.split('/')[-1] - reduced_clrs.append(clrs) + try: + for count, value in enumerate(colorspace_knob.values()): + colorspace_knob.setValue(count) + colorspaces.append(colorspace_knob.value()) + finally: + colorspace_knob.setValue(original_value) - return reduced_clrs + return colorspaces def is_headless(): diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 3a2ec3dbee5..2c40d9d6577 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -11,6 +11,9 @@ get_current_project_name, get_representation_path, ) +from openpype.pipeline.colorspace import ( + get_imageio_file_rules_colorspace_from_filepath +) from openpype.hosts.nuke.api.lib import ( get_imageio_input_colorspace, maintained_selection @@ -101,7 +104,6 @@ def load(self, context, name, namespace, options): filepath = self.filepath_from_context(context) filepath = filepath.replace("\\", "/") - self.log.debug("_ filepath: {}".format(filepath)) start_at_workfile = options.get( "start_at_workfile", self.options_defaults["start_at_workfile"]) @@ -140,6 +142,7 @@ def load(self, context, name, namespace, options): "Representation id `{}` is failing to load".format(repre_id)) return + print(representation["data"]["colorspaceData"]) read_name = self._get_node_name(representation) # Create the Loader with the filename path set @@ -154,8 +157,8 @@ def load(self, context, name, namespace, options): with viewer_update_and_undo_stop(): read_node["file"].setValue(filepath) - used_colorspace = self._set_colorspace( - read_node, version_data, representation["data"], filepath) + self.set_colorspace_to_node( + read_node, filepath, version, representation) self._set_range_to_node(read_node, first, last, start_at_workfile) @@ -180,8 +183,6 @@ def load(self, context, name, namespace, options): colorspace = representation["data"].get(key) colorspace = colorspace or version_data.get(key) data_imprint["db_colorspace"] = colorspace - if used_colorspace: - data_imprint["used_colorspace"] = used_colorspace else: value_ = context["version"]['data'].get( key, str(None)) @@ -302,8 +303,8 @@ def update(self, container, representation): # to avoid multiple undo steps for rest of process # we will switch off undo-ing with viewer_update_and_undo_stop(): - used_colorspace = self._set_colorspace( - read_node, version_data, representation["data"], filepath) + self.set_colorspace_to_node( + read_node, filepath, version_doc, representation) self._set_range_to_node(read_node, first, last, start_at_workfile) @@ -320,10 +321,6 @@ def update(self, container, representation): "author": version_data.get("author") } - # add used colorspace if found any - if used_colorspace: - updated_dict["used_colorspace"] = used_colorspace - last_version_doc = get_last_version_by_subset_id( project_name, version_doc["parent"], fields=["_id"] ) @@ -350,6 +347,36 @@ def update(self, container, representation): self.set_as_member(read_node) + def set_colorspace_to_node( + self, + read_node, + filepath, + version_doc, + representation_doc, + ): + """Set colorspace to read node. + + Sets colorspace with available names validation. + + Args: + read_node (nuke.Node): The nuke's read node + filepath (str): file path + version_doc (dict): version document + representation_doc (dict): representation document + + """ + used_colorspace = self._get_colorspace_data( + version_doc, representation_doc, filepath) + + if ( + used_colorspace + and colorspace_exists_on_node(read_node, used_colorspace) + ): + self.log.info(f"Used colorspace: {used_colorspace}") + read_node["colorspace"].setValue(used_colorspace) + else: + self.log.info("Colorspace not set...") + def remove(self, container): read_node = container["node"] assert read_node.Class() == "Read", "Must be Read" @@ -450,25 +477,50 @@ def _get_node_name(self, representation): return self.node_name_template.format(**name_data) - def _set_colorspace(self, node, version_data, repre_data, path): - output_color = None - path = path.replace("\\", "/") - # get colorspace - colorspace = repre_data.get("colorspace") - colorspace = colorspace or version_data.get("colorspace") + def _get_colorspace_data(self, version_doc, representation_doc, filepath): + """Get colorspace data from version and representation documents - # colorspace from `project_settings/nuke/imageio/regexInputs` - iio_colorspace = get_imageio_input_colorspace(path) + Args: + version_doc (dict): version document + representation_doc (dict): representation document + filepath (str): file path - # Set colorspace defined in version data - if ( - colorspace is not None - and colorspace_exists_on_node(node, str(colorspace)) - ): - node["colorspace"].setValue(str(colorspace)) - output_color = str(colorspace) - elif iio_colorspace is not None: - node["colorspace"].setValue(iio_colorspace) - output_color = iio_colorspace + Returns: + Any[str,None]: colorspace name or None + """ + # Get backward compatible colorspace key. + representation_data = representation_doc["data"] + colorspace = representation_doc["data"].get("colorspace") + self.log.debug( + f"Colorspace from representation colorspace: {colorspace}" + ) + + # Get backward compatible version data key if colorspace is not found. + colorspace = colorspace or version_doc["data"].get("colorspace") + self.log.debug(f"Colorspace from version colorspace: {colorspace}") + + # Get colorspace from representation colorspaceData if colorspace is + # not found. + colorspace_data = representation_doc["data"].get("colorspaceData", {}) + colorspace = colorspace or colorspace_data.get("colorspace") + self.log.debug( + f"Colorspace from representation colorspaceData: {colorspace}" + ) + + print(f"Colorspace found: {colorspace}") + + # check if any filerules are not applicable + new_parsed_colorspace = get_imageio_file_rules_colorspace_from_filepath( # noqa + filepath, "nuke", get_current_project_name() + ) + self.log.debug(f"Colorspace new filerules: {new_parsed_colorspace}") + + # colorspace from `project_settings/nuke/imageio/regexInputs` + old_parsed_colorspace = get_imageio_input_colorspace(filepath) + self.log.debug(f"Colorspace old filerules: {old_parsed_colorspace}") - return output_color + return ( + new_parsed_colorspace + or old_parsed_colorspace + or colorspace + ) From f285c1f9f00c7f820356729ef5516bcaa70370f3 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 15 Feb 2024 17:21:18 +0000 Subject: [PATCH 075/163] Hound --- openpype/hosts/nuke/api/utils.py | 2 +- openpype/hosts/nuke/plugins/load/load_clip.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index 41857bd4ca8..62552a24083 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -132,7 +132,7 @@ def get_colorspace_list(colorspace_knob): colorspaces = [] try: - for count, value in enumerate(colorspace_knob.values()): + for count, _ in enumerate(colorspace_knob.values()): colorspace_knob.setValue(count) colorspaces.append(colorspace_knob.value()) finally: diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 2c40d9d6577..0cdb7fc5969 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -489,7 +489,6 @@ def _get_colorspace_data(self, version_doc, representation_doc, filepath): Any[str,None]: colorspace name or None """ # Get backward compatible colorspace key. - representation_data = representation_doc["data"] colorspace = representation_doc["data"].get("colorspace") self.log.debug( f"Colorspace from representation colorspace: {colorspace}" From 3520f5cc90a03d882460513e63283b0b43d54f32 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Fri, 16 Feb 2024 15:25:34 +0000 Subject: [PATCH 076/163] Add AVALON_DB to Deadline submissions --- .../deadline/plugins/publish/submit_aftereffects_deadline.py | 1 + .../modules/deadline/plugins/publish/submit_blender_deadline.py | 1 + .../modules/deadline/plugins/publish/submit_fusion_deadline.py | 1 + .../modules/deadline/plugins/publish/submit_harmony_deadline.py | 1 + .../deadline/plugins/publish/submit_houdini_cache_deadline.py | 1 + .../deadline/plugins/publish/submit_houdini_render_deadline.py | 1 + openpype/modules/deadline/plugins/publish/submit_max_deadline.py | 1 + .../modules/deadline/plugins/publish/submit_maya_deadline.py | 1 + .../plugins/publish/submit_maya_remote_publish_deadline.py | 1 + .../modules/deadline/plugins/publish/submit_nuke_deadline.py | 1 + .../modules/deadline/plugins/publish/submit_publish_cache_job.py | 1 + openpype/modules/deadline/plugins/publish/submit_publish_job.py | 1 + 12 files changed, 12 insertions(+) diff --git a/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py b/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py index 009375e87ee..d40c371de0e 100644 --- a/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_aftereffects_deadline.py @@ -82,6 +82,7 @@ def get_job_info(self): "FTRACK_API_KEY", "FTRACK_API_USER", "FTRACK_SERVER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_blender_deadline.py b/openpype/modules/deadline/plugins/publish/submit_blender_deadline.py index 8f9e9a74253..58e69d0aeae 100644 --- a/openpype/modules/deadline/plugins/publish/submit_blender_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_blender_deadline.py @@ -104,6 +104,7 @@ def get_job_info(self): "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py b/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py index 9a718aa089a..dcb79588a74 100644 --- a/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_fusion_deadline.py @@ -223,6 +223,7 @@ def process(self, instance): "FTRACK_API_KEY", "FTRACK_API_USER", "FTRACK_SERVER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py b/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py index 17e672334cf..73bc10465d1 100644 --- a/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_harmony_deadline.py @@ -275,6 +275,7 @@ def get_job_info(self): "FTRACK_API_KEY", "FTRACK_API_USER", "FTRACK_SERVER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py index ada69575a8b..bef93b3947c 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_cache_deadline.py @@ -110,6 +110,7 @@ def get_job_info(self): "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py index bf7fb45a8b7..6ed9e66ce08 100644 --- a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py @@ -205,6 +205,7 @@ def get_job_info(self, dependency_job_ids=None): "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py index f06bd4dbe69..e31de0a101d 100644 --- a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py @@ -108,6 +108,7 @@ def get_job_info(self): "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 5591db151a2..4cd417b83b2 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -201,6 +201,7 @@ def get_job_info(self): "FTRACK_API_USER", "FTRACK_SERVER", "OPENPYPE_SG_USER", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py index 41a2a64ab58..a9fb10de8b3 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py @@ -108,6 +108,7 @@ def get_job_info(self): if key in os.environ}, **legacy_io.Session) # TODO replace legacy_io with context.data + environment["AVALON_DB"] = os.environ.get("AVALON_DB") environment["AVALON_PROJECT"] = project_name environment["AVALON_ASSET"] = instance.context.data["asset"] environment["AVALON_TASK"] = instance.context.data["task"] diff --git a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py index 746b009255f..9c2d2128067 100644 --- a/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_nuke_deadline.py @@ -376,6 +376,7 @@ def payload_submit( keys = [ "PYTHONPATH", "PATH", + "AVALON_DB", "AVALON_PROJECT", "AVALON_ASSET", "AVALON_TASK", diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py index 1bb45b77ccf..434a823cfe6 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -131,6 +131,7 @@ def _submit_deadline_post_job(self, instance, job): create_metadata_path(instance, anatomy) environment = { + "AVALON_DB": os.environ["AVALON_DB"], "AVALON_PROJECT": instance.context.data["projectName"], "AVALON_ASSET": instance.context.data["asset"], "AVALON_TASK": instance.context.data["task"], diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 82971daee5f..f622ec9a00a 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -187,6 +187,7 @@ def _submit_deadline_post_job(self, instance, job, instances): create_metadata_path(instance, anatomy) environment = { + "AVALON_DB": os.environ["AVALON_DB"], "AVALON_PROJECT": instance.context.data["projectName"], "AVALON_ASSET": instance.context.data["asset"], "AVALON_TASK": instance.context.data["task"], From 1d16f25b793908986cbc7002f81c7d4e30fc85b3 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Mon, 19 Feb 2024 11:58:13 +0000 Subject: [PATCH 077/163] Update openpype/hosts/nuke/plugins/load/load_clip.py --- openpype/hosts/nuke/plugins/load/load_clip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py index 0cdb7fc5969..e2bd2180b76 100644 --- a/openpype/hosts/nuke/plugins/load/load_clip.py +++ b/openpype/hosts/nuke/plugins/load/load_clip.py @@ -142,7 +142,6 @@ def load(self, context, name, namespace, options): "Representation id `{}` is failing to load".format(repre_id)) return - print(representation["data"]["colorspaceData"]) read_name = self._get_node_name(representation) # Create the Loader with the filename path set From 698e5db01e882f72f36d45563be5843c74494057 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 20 Feb 2024 15:48:51 +0000 Subject: [PATCH 078/163] Working version of attributes on extractor --- openpype/hosts/maya/api/alembic.py | 1 + .../create/create_animation_pointcache.py | 221 +-------------- .../plugins/publish/collect_pointcache.py | 5 - .../plugins/publish/extract_pointcache.py | 262 ++++++++++++++---- .../defaults/project_settings/maya.json | 26 ++ .../schemas/schema_maya_create.json | 104 ------- .../schemas/schema_maya_publish.json | 179 ++++++++++-- openpype/tools/publisher/widgets/widgets.py | 2 +- 8 files changed, 404 insertions(+), 396 deletions(-) diff --git a/openpype/hosts/maya/api/alembic.py b/openpype/hosts/maya/api/alembic.py index 63f826299f8..b657262b4db 100644 --- a/openpype/hosts/maya/api/alembic.py +++ b/openpype/hosts/maya/api/alembic.py @@ -70,6 +70,7 @@ def extract_alembic( worldSpace=False, writeColorSets=False, writeCreases=False, + writeNormals=False, writeFaceSets=False, writeUVSets=False, writeVisibility=False diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 353a4402ee8..6fe078f2adf 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -1,26 +1,16 @@ from maya import cmds from openpype.hosts.maya.api import lib, plugin -from openpype.hosts.maya.api.alembic import ALEMBIC_ARGS from openpype.lib import ( BoolDef, - TextDef, NumberDef, - EnumDef, - UISeparatorDef, - UILabelDef, ) from openpype.pipeline import CreatedInstance def _get_animation_attr_defs(cls): - """Get Animation generic definitions. - - The line is blurry between what's "Animation" generic and "Alembic", - but the rule of thumb is that whatever "AlembicExport -h" accepts - is "Alembic" and the other ones are "Animation". - """ + """Get Animation generic definitions.""" defs = lib.collect_animation_defs() defs.extend( [ @@ -30,205 +20,16 @@ def _get_animation_attr_defs(cls): BoolDef( "includeParentHierarchy", label="Include Parent Hierarchy" ), + BoolDef( + "includeUserDefinedAttributes", + label="Include User Defined Attributes" + ), ] ) return defs -def _get_abc_export_flags(cls): - """Get two sets with the Alembic Export flags. - - Alembic flags are treated as booleans, so here we get all the possible - options, and work out a list with all the ones that can be toggled by - the user, and the ones defined in the settings. - """ - - # The Arguments that can be modified by the Publisher - abc_export_overrides = set(getattr(cls, "abc_export_overrides", set())) - - # What we have set in the Settings as defaults. - default_abc_export_flags = set(getattr(cls, "abc_export_flags", set())) - - # Set of un-toggleable flags, specified by the settings - abc_export_flags = { - arg - for arg in default_abc_export_flags - if arg not in abc_export_overrides - } - - # Set of all the available Alembic Export Flags - abc_boolean_flags = { - arg for arg, arg_type in ALEMBIC_ARGS.items() if arg_type is bool - } - - # Set of togglable flags - abc_export_toggleable_flags = { - arg for arg in abc_export_overrides if arg in abc_boolean_flags - } - return abc_export_flags, abc_export_toggleable_flags - - -def _get_animation_abc_attr_defs(cls): - """Get definitions relating to Alembic. - - An admin can define in settings the default arguments, which are then not - modifiable by the person publishing, unless they are added to the Alembic - Overrides setting, which is mapped to `abc_args_overrides`. - - Most of the Alembic Arguments are flags, treated as booleans, and there are - two possible lists: the defaults (from settings) and the the toggleable by - the user, these two define an EnumDef respectively. - - We use a combination of the two above to only show a muiltiselection - dropdown for booleans, and disabling the non-boolean arguments on the - interface. - - There's also a new separator so it's clearer what belongs to common - Animation publishes versus what is Almebic specific, the line is blurry, - but the rule of thumb is that whatever "AlembicExport -h" accepts is - "Alembic" and the other ones are "Animation". - """ - abc_defs = None - abc_defs = [ - UISeparatorDef("sep_alembic_options"), - UILabelDef("Alembic Options"), - ] - - # The Arguments that can be modified by the Publisher - abc_args_overrides = set(getattr(cls, "abc_args_overrides", set())) - - # What we have set in the Settings as defaults. - default_abc_export_flags = set(getattr(cls, "abc_export_flags", set())) - - ( - abc_export_flags, - abc_export_toggleable_flags, - ) = _get_abc_export_flags(cls) - - abc_defs.append( - EnumDef( - "abcExportFlags", - list(abc_export_flags), - default=list(abc_export_flags), - multiselection=True, - label="Settings Defined Arguments", - disabled=True, - hidden=True, - ) - ) - - # Only display Boolan flags that the Admin defined as overrideable - abc_export_toggleable_defaults = [ - arg - for arg in abc_export_toggleable_flags - if arg in default_abc_export_flags - ] - abc_defs.append( - EnumDef( - "abcExportTogglableFlags", - list(abc_export_toggleable_flags) - if abc_export_toggleable_flags - else [""], - default=abc_export_toggleable_defaults, - multiselection=True, - label="Export Flags", - disabled=True if not abc_export_toggleable_flags else False, - ) - ) - - abc_defs.append( - TextDef( - "attr", - label="Custom Attributes", - default=getattr(cls, "attr", None), - placeholder="attr1; attr2; ...", - disabled=True if "attr" not in abc_args_overrides else False, - ) - ) - - abc_defs.append( - TextDef( - "attrPrefix", - label="Custom Attributes Prefix", - default=getattr(cls, "attrPrefix", None), - placeholder="prefix1; prefix2; ...", - disabled=True if "attrPrefix" not in abc_args_overrides else False, - ) - ) - - abc_defs.append( - EnumDef( - "dataFormat", - label="Data Format", - default=getattr(cls, "dataFormat", None), - items=["ogawa", "HDF"], - disabled=True if "dataFormat" not in abc_args_overrides else False, - ) - ) - - abc_defs.append( - NumberDef( - "preRollStartFrame", - label="Start frame for preroll", - default=getattr(cls, "preRollStartFrame", None), - tooltip=( - "The frame to start scene evaluation at. This is used to set" - " the starting frame for time dependent translations and can" - " be used to evaluate run-up that isn't actually translated." - ), - disabled=True - if "preRollStartFrame" not in abc_args_overrides - else False, - ) - ) - - return abc_defs - - -def _ensure_defaults(cls, instance_data): - """Ensure we get default values when an attribute is not overrideable. - - In instances where an attribute used to be modifiable, and then was locked - again, we want to make sure that we pass the default (what's on the - settings) instead of any value that might have been stored in the scene - when the attribute was modifiable. - """ - abc_args_overrides = set(getattr(cls, "abc_args_overrides", set())) - creator_attr = instance_data["creator_attributes"] - attr_default = getattr(cls, "attr", "") - - if "attr" not in abc_args_overrides: - creator_attr["attr"] = attr_default - - if "attrPrefix" not in abc_args_overrides: - creator_attr["attrPrefix"] = getattr(cls, "attrPrefix", "") - - if "dataFormat" not in abc_args_overrides: - creator_attr["dataFormat"] = getattr(cls, "dataFormat", "") - - if "preRollStartFrame" not in abc_args_overrides: - creator_attr["preRollStartFrame"] = getattr( - cls, "preRollStartFrame", "" - ) - - ( - abc_boolean_defaults, - abc_boolean_overrides, - ) = _get_abc_export_flags(cls) - - creator_attr["abcExportFlags"] = list(abc_boolean_defaults) - - if creator_attr.get("abcExportTogglableFlags", []): - abc_boolean_args = creator_attr["abcExportTogglableFlags"].copy() - - creator_attr["abcExportTogglableFlags"] = [ - arg for arg in abc_boolean_args if arg not in abc_boolean_overrides - ] - - return instance_data - - class CreateAnimation(plugin.MayaHiddenCreator): """Animation output for character rigs @@ -257,18 +58,12 @@ def collect_instances(self): for node in cached_subsets.get(self.identifier, []): node_data = self.read_instance_node(node) - _ensure_defaults(self, node_data) - created_instance = CreatedInstance.from_existing(node_data, self) self._add_instance_to_context(created_instance) def get_instance_attr_defs(self): super(CreateAnimation, self).get_instance_attr_defs() defs = _get_animation_attr_defs(self) - abc_defs = _get_animation_abc_attr_defs(self) - if abc_defs: - defs.extend(abc_defs) - return defs @@ -292,18 +87,12 @@ def collect_instances(self): for node in cached_subsets.get(self.identifier, []): node_data = self.read_instance_node(node) - _ensure_defaults(self, node_data) - created_instance = CreatedInstance.from_existing(node_data, self) self._add_instance_to_context(created_instance) def get_instance_attr_defs(self): super(CreatePointCache, self).get_instance_attr_defs() defs = _get_animation_attr_defs(self) - abc_defs = _get_animation_abc_attr_defs(self) - if abc_defs: - defs.extend(abc_defs) - return defs def create(self, subset_name, instance_data, pre_create_data): diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 389f16a5bf0..5578a57f314 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -10,11 +10,6 @@ class CollectPointcache(pyblish.api.InstancePlugin): families = ["pointcache"] label = "Collect Pointcache" hosts = ["maya"] - legacy_settings = { - "write_color_sets": "writeColorSets", - "write_face_sets": "writeFaceSets", - "include_user_defined_attributes": "includeUserDefinedAttributes" - } def process(self, instance): if instance.data.get("farm"): diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 4bfd5e2fe0b..26957b02f51 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -9,9 +9,18 @@ maintained_selection, iter_visible_nodes_in_range, ) +from openpype.lib import ( + BoolDef, + TextDef, + NumberDef, + EnumDef, + UISeparatorDef, + UILabelDef, +) +from openpype.pipeline.publish import OpenPypePyblishPluginMixin -class ExtractAlembic(publish.Extractor): +class ExtractAlembic(publish.Extractor, OpenPypePyblishPluginMixin): """Produce an alembic of just point positions and normals. Positions and normals, uvs, creases are preserved, but nothing more, @@ -25,6 +34,19 @@ class ExtractAlembic(publish.Extractor): hosts = ["maya"] families = ["pointcache", "model", "vrayproxy.alembic"] targets = ["local", "remote"] + flags = [] + attr = [] + attrPrefix = [] + dataFormat = "ogawa" + melPerFrameCallback = "" + melPostJobCallback = "" + preRollStartFrame = 0 + pythonPerFrameCallback = "" + pythonPostJobCallback = "" + stripNamespaces = -1 + userAttr = "" + userAttrPrefix = "" + export_overrides = [] def process(self, instance): if instance.data.get("farm"): @@ -37,20 +59,22 @@ def process(self, instance): start = float(instance.data.get("frameStartHandle", 1)) end = float(instance.data.get("frameEndHandle", 1)) - # Collect Alembic Arguments - creator_attributes = instance.data.get("creator_attributes") - abc_flags = creator_attributes.get( - "abcExportTogglableFlags" - ) + creator_attributes.get("abcExportTogglableFlags") + attribute_values = self.get_attr_values_from_data( + instance.data + ) - abc_attrs = [ + attrs = [ attr.strip() - for attr in creator_attributes.get("attr", "").split(";") + for attr in attribute_values.get("attr", "").split(";") + if attr.strip() ] + attrs += instance.data.get("userDefinedAttributes", []) + attrs += ["cbId"] - abc_attr_prefixes = [ - attr_prefix.strip() - for attr_prefix in instance.data.get("attrPrefix", "").split(";") + attr_prefixes = [ + attr.strip() + for attr in attribute_values.get("attrPrefix", "").split(";") + if attr.strip() ] self.log.debug("Extracting pointcache...") @@ -60,51 +84,52 @@ def process(self, instance): filename = "{name}.abc".format(**instance.data) path = os.path.join(parent_dir, filename) - abc_root = None + root = None if not instance.data.get("includeParentHierarchy", True): # Set the root nodes if we don't want to include parents # The roots are to be considered the ones that are the actual # direct members of the set - abc_root = roots + root = roots - abc_writeUVSets = False - - if int(cmds.about(version=True)) >= 2017: - # Since Maya 2017 alembic supports multiple uv sets - write them. - if "writeUVSets" in abc_flags: - abc_writeUVSets = True - - extract_abc_args = { + args = { "file": path, - "attr": abc_attrs, - "attrPrefix": abc_attr_prefixes, - "dataFormat": creator_attributes.get("dataFormat", "ogawa"), + "attr": attrs, + "attrPrefix": attr_prefixes, + "dataFormat": attribute_values.get("dataFormat", "ogawa"), "endFrame": end, - "eulerFilter": True if "eulerFilter" in abc_flags else False, - "noNormals": True if "noNormals" in abc_flags else False, - "preRoll": True if "preRoll" in abc_flags else False, - "preRollStartFrame": creator_attributes.get( + "eulerFilter": False, + "noNormals": False, + "preRoll": False, + "preRollStartFrame": attribute_values.get( "preRollStartFrame", 0 ), - "renderableOnly": True if "renderableOnly" in abc_flags else False, - "root": abc_root, - "selection": True, # Should this stay like so? + "renderableOnly": False, + "root": root, + "selection": True, "startFrame": start, - "step": creator_attributes.get("step", 1.0), - "stripNamespaces": True, - "uvWrite": True if "uvWrite" in abc_flags else False, - "verbose": True if "verbose" in abc_flags else False, - "wholeFrameGeo": True if "wholeFrameGeo" in abc_flags else False, - "worldSpace": True if "worldSpace" in abc_flags else False, - "writeColorSets": True if "writeColorSets" in abc_flags else False, - "writeCreases": True if "writeCreases" in abc_flags else False, - "writeFaceSets": True if "writeFaceSets" in abc_flags else False, - "writeUVSets": abc_writeUVSets, - "writeVisibility": True - if "writeVisibility" in abc_flags - else False, + "step": instance.data.get( + "creator_attributes", {} + ).get("step", 1.0), + "stripNamespaces": False, + "uvWrite": False, + "verbose": False, + "wholeFrameGeo": False, + "worldSpace": False, + "writeColorSets": False, + "writeCreases": False, + "writeFaceSets": False, + "writeUVSets": False, + "writeVisibility": False, } + # Export flags are defined as default enabled flags excluding flags + # that are exposed to the user, plus the flags the user has enabled + # when publishing. + flags = list(set(self.flags) - set(self.export_overrides)) + flags += attribute_values["flag_overrides"] + for flag in flags: + args[flag] = True + if instance.data.get("visibleOnly", False): # If we only want to include nodes that are visible in the frame # range then we need to do our own check. Alembic's `visibleOnly` @@ -121,10 +146,10 @@ def process(self, instance): cmds.select(nodes, noExpand=True) self.log.debug( "Running `extract_alembic` with the arguments: {}".format( - extract_abc_args + args ) ) - extract_alembic(**extract_abc_args) + extract_alembic(**args) if "representations" not in instance.data: instance.data["representations"] = [] @@ -148,17 +173,17 @@ def process(self, instance): return path = path.replace(".abc", "_proxy.abc") - extract_abc_args["file"] = path + args["file"] = path if not instance.data.get("includeParentHierarchy", True): # Set the root nodes if we don't want to include parents # The roots are to be considered the ones that are the actual # direct members of the set - extract_abc_args["root"] = instance.data["proxyRoots"] + args["root"] = instance.data["proxyRoots"] with suspended_refresh(suspend=suspend): with maintained_selection(): cmds.select(instance.data["proxy"]) - extract_alembic(**extract_abc_args) + extract_alembic(**args) representation = { "name": "proxy", @@ -172,9 +197,146 @@ def process(self, instance): def get_members_and_roots(self, instance): return instance[:], instance.data.get("setMembers") + @classmethod + def get_attribute_defs(cls): + override_defs = { + "attr": { + "def": TextDef, + "kwargs": { + "label": "Custom Attributes", + "placeholder": "attr1; attr2; ...", + } + }, + "attrPrefix": { + "def": TextDef, + "kwargs": { + "label": "Custom Attributes Prefix", + "placeholder": "prefix1; prefix2; ...", + } + }, + "dataFormat": { + "def": EnumDef, + "kwargs": { + "label": "Data Format", + "items": ["ogawa", "HDF"], + } + }, + "melPerFrameCallback": { + "def": TextDef, + "kwargs": { + "label": "melPerFrameCallback", + } + }, + "melPostJobCallback": { + "def": TextDef, + "kwargs": { + "label": "melPostJobCallback", + } + }, + "preRollStartFrame": { + "def": NumberDef, + "kwargs": { + "label": "Start frame for preroll", + "tooltip": ( + "The frame to start scene evaluation at. This is used" + " to set the starting frame for time dependent " + "translations and can be used to evaluate run-up that" + " isn't actually translated." + ), + } + }, + "pythonPerFrameCallback": { + "def": TextDef, + "kwargs": { + "label": "pythonPerFrameCallback", + } + }, + "pythonPostJobCallback": { + "def": TextDef, + "kwargs": { + "label": "pythonPostJobCallback", + } + }, + "stripNamespaces": { + "def": NumberDef, + "kwargs": { + "label": "stripNamespaces", + "tooltip": ( + "If this flag is present namespaces will be stripped " + "off of the node before being written to Alembic. The " + "int after the flag specifies how many namespaces will" + " be stripped off of the node name. Be careful that " + "the new stripped name does not collide with other " + "sibling node names.\n\nExamples:\n taco:foo:bar would" + " be written as just bar with -sn 0\ntaco:foo:bar " + "would be written as foo:bar with -sn 1" + ), + } + }, + "userAttr": { + "def": TextDef, + "kwargs": { + "label": "userAttr", + } + }, + "userAttrPrefix": { + "def": TextDef, + "kwargs": { + "label": "userAttrPrefix", + } + }, + "visibleOnly": { + "def": BoolDef, + "kwargs": { + "label": "Visible Only", + } + } + } + + defs = super(ExtractAlembic, cls).get_attribute_defs() + + defs.extend([ + UISeparatorDef("sep_alembic_options"), + UILabelDef("Alembic Options"), + ]) + + # The Arguments that can be modified by the Publisher + export_overrides = set(getattr(cls, "export_overrides", set())) + + # What we have set in the Settings as defaults. + flags = set(getattr(cls, "flags", set())) + + enabled_flags = [x for x in flags if x in export_overrides] + flag_overrides = export_overrides - set(override_defs.keys()) + defs.append( + EnumDef( + "flag_overrides", + flag_overrides, + default=enabled_flags, + multiselection=True, + label="Export Flags", + ) + ) + + for key, value in override_defs.items(): + if key not in export_overrides: + continue + + kwargs = value["kwargs"] + kwargs["default"] = getattr(cls, key, None) + defs.append( + value["def"](key, **value["kwargs"]) + ) + + defs.append( + UISeparatorDef("sep_alembic_options") + ) + + return defs + class ExtractAnimation(ExtractAlembic): - label = "Extract Animation" + label = "Extract Animation (Alembic)" families = ["animation"] def get_members_and_roots(self, instance): diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 214a4f737ac..1319a41f51a 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1150,6 +1150,32 @@ "pointcache", "model", "vrayproxy.alembic" + ], + "flags": [ + "stripNamespaces", + "writeColorSets", + "writeNormals", + "worldSpace" + ], + "attr": "", + "attrPrefix": "", + "dataFormat": "ogawa", + "melPerFrameCallback": "", + "melPostJobCallback": "", + "preRollStartFrame": 0, + "pythonPerFrameCallback": "", + "pythonPostJobCallback": "", + "userAttr": "", + "userAttrPrefix": "", + "visibleOnly": false, + "export_overrides": [ + "attr", + "attrPrefix", + "worldSpace", + "writeNormals", + "writeFaceSets", + "renderableOnly", + "visibleOnly" ] }, "ExtractObj": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index 6f9ead7fec1..7539d824360 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -158,114 +158,10 @@ "key": "refresh", "label": "Refresh" }, - { - "type": "enum", - "key": "abc_export_flags", - "multiselection": true, - "label": "Export Flags (.abc)", - "enum_items": [ - {"autoSubd": "autoSubd"}, - {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, - {"eulerFilter": "eulerFilter"}, - {"noNormals": "noNormals"}, - {"preRoll": "preRoll"}, - {"renderableOnly": "renderableOnly"}, - {"selection": "selection"}, - {"stripNamespaces": "stripNamespaces"}, - {"uvWrite": "uvWrite"}, - {"uvsOnly": "uvsOnly"}, - {"verbose": "verbose"}, - {"wholeFrameGeo": "wholeFrameGeo"}, - {"worldSpace": "worldSpace"}, - {"writeColorSets": "writeColorSets"}, - {"writeCreases": "writeCreases"}, - {"writeFaceSets": "writeFaceSets"}, - {"writeUVSets": "writeUVSets"}, - {"writeVisibility": "writeVisibility"} - ] - }, - { - "type": "enum", - "key": "abc_export_overrides", - "multiselection": true, - "label": "Export Overrides (.abc)", - "enum_items": [ - {"attr": "attr"}, - {"attrPrefix": "attrPrefix"}, - {"autoSubd": "autoSubd"}, - {"dataFormat": "dataFormat"}, - {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, - {"endFrame": "endFrame"}, - {"eulerFilter": "eulerFilter"}, - {"frameRange": "frameRange"}, - {"frameRelativeSample": "frameRelativeSample"}, - {"melPerFrameCallback": "melPerFrameCallback"}, - {"melPostJobCallback": "melPostJobCallback"}, - {"noNormals": "noNormals"}, - {"preRoll": "preRoll"}, - {"preRollStartFrame": "preRollStartFrame"}, - {"pythonPerFrameCallback": "pythonPerFrameCallback"}, - {"pythonPostJobCallback": "pythonPostJobCallback"}, - {"renderableOnly": "renderableOnly"}, - {"root": "root"}, - {"selection": "selection"}, - {"startFrame": "startFrame"}, - {"step": "step"}, - {"stripNamespaces": "stripNamespaces"}, - {"userAttr": "userAttr"}, - {"userAttrPrefix": "userAttrPrefix"}, - {"uvWrite": "uvWrite"}, - {"uvsOnly": "uvsOnly"}, - {"verbose": "verbose"}, - {"wholeFrameGeo": "wholeFrameGeo"}, - {"worldSpace": "worldSpace"}, - {"writeColorSets": "writeColorSets"}, - {"writeCreases": "writeCreases"}, - {"writeFaceSets": "writeFaceSets"}, - {"writeUVSets": "writeUVSets"}, - {"writeVisibility": "writeVisibility"} - ] - }, - { - "type": "number", - "key": "step", - "label": "Step", - "minimum": 0.0, - "decimal": 4 - }, { "type": "boolean", "key": "visibleOnly", "label": "Visible Only default" - }, - { - "type": "text", - "key": "attr", - "label": "Attributes" - }, - { - "type": "text", - "key": "attrPrefix", - "label": "Attr Prefix" - }, - { - "type": "enum", - "key": "dataFormat", - "label": "Data Format", - "enum_items": [ - { - "ogawa": "ogawa" - }, - { - "HDF": "HDF" - } - ] - }, - { - "type": "number", - "key": "preRollStartFrame", - "label": "Pre Roll Start Frame", - "minimum": 0 } ] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index d2e7c51e249..9af9dbb32b5 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -750,26 +750,6 @@ } ] }, - { - "type": "dict", - "collapsible": true, - "key": "ExtractAlembic", - "label": "Extract Alembic", - "checkbox_key": "enabled", - "children": [ - { - "type": "boolean", - "key": "enabled", - "label": "Enabled" - }, - { - "key": "families", - "label": "Families", - "type": "list", - "object_type": "text" - } - ] - }, { "type": "dict", "collapsible": true, @@ -1008,6 +988,165 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "ExtractAlembic", + "label": "Extract Pointcache/Animation", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "key": "families", + "label": "Families", + "type": "list", + "object_type": "text" + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Export Defaults" + }, + { + "type": "enum", + "key": "flags", + "multiselection": true, + "label": "Flags", + "enum_items": [ + {"autoSubd": "autoSubd"}, + {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, + {"eulerFilter": "eulerFilter"}, + {"noNormals": "noNormals"}, + {"preRoll": "preRoll"}, + {"renderableOnly": "renderableOnly"}, + {"stripNamespaces": "stripNamespaces"}, + {"uvWrite": "uvWrite"}, + {"uvsOnly": "uvsOnly"}, + {"verbose": "verbose"}, + {"wholeFrameGeo": "wholeFrameGeo"}, + {"worldSpace": "worldSpace"}, + {"writeColorSets": "writeColorSets"}, + {"writeFaceSets": "writeFaceSets"}, + {"writeUVSets": "writeUVSets"}, + {"writeVisibility": "writeVisibility"} + ] + }, + { + "type": "text", + "key": "attr", + "label": "Attributes" + }, + { + "type": "text", + "key": "attrPrefix", + "label": "Attr Prefix" + }, + { + "type": "enum", + "key": "dataFormat", + "label": "Data Format", + "enum_items": [ + { + "ogawa": "ogawa" + }, + { + "HDF": "HDF" + } + ] + }, + { + "type": "text", + "key": "melPerFrameCallback", + "label": "melPerFrameCallback" + }, + { + "type": "text", + "key": "melPostJobCallback", + "label": "melPostJobCallback" + }, + { + "type": "number", + "key": "preRollStartFrame", + "label": "Pre Roll Start Frame", + "minimum": 0 + }, + { + "type": "text", + "key": "pythonPerFrameCallback", + "label": "pythonPerFrameCallback" + }, + { + "type": "text", + "key": "pythonPostJobCallback", + "label": "pythonPostJobCallback" + }, + { + "type": "text", + "key": "userAttr", + "label": "userAttr" + }, + { + "type": "text", + "key": "userAttrPrefix", + "label": "userAttrPrefix" + }, + { + "type": "boolean", + "key": "visibleOnly", + "label": "visibleOnly" + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "These attributes are exposed to the user when publishing with default values from above." + }, + { + "type": "enum", + "key": "export_overrides", + "multiselection": true, + "label": "Export Overrides", + "enum_items": [ + {"attr": "attr"}, + {"attrPrefix": "attrPrefix"}, + {"autoSubd": "autoSubd"}, + {"dataFormat": "dataFormat"}, + {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, + {"eulerFilter": "eulerFilter"}, + {"melPerFrameCallback": "melPerFrameCallback"}, + {"melPostJobCallback": "melPostJobCallback"}, + {"noNormals": "noNormals"}, + {"preRoll": "preRoll"}, + {"preRollStartFrame": "preRollStartFrame"}, + {"pythonPerFrameCallback": "pythonPerFrameCallback"}, + {"pythonPostJobCallback": "pythonPostJobCallback"}, + {"renderableOnly": "renderableOnly"}, + {"stripNamespaces": "stripNamespaces"}, + {"userAttr": "userAttr"}, + {"userAttrPrefix": "userAttrPrefix"}, + {"uvWrite": "uvWrite"}, + {"uvsOnly": "uvsOnly"}, + {"verbose": "verbose"}, + {"visibleOnly": "visibleOnly"}, + {"wholeFrameGeo": "wholeFrameGeo"}, + {"worldSpace": "worldSpace"}, + {"writeColorSets": "writeColorSets"}, + {"writeCreases": "writeCreases"}, + {"writeFaceSets": "writeFaceSets"}, + {"writeNormals": "writeNormals"}, + {"writeUVSets": "writeUVSets"}, + {"writeVisibility": "writeVisibility"} + ] + } + ] + }, { "type": "dict", "collapsible": true, diff --git a/openpype/tools/publisher/widgets/widgets.py b/openpype/tools/publisher/widgets/widgets.py index ecccc4e0c8e..2b497707faf 100644 --- a/openpype/tools/publisher/widgets/widgets.py +++ b/openpype/tools/publisher/widgets/widgets.py @@ -1485,7 +1485,7 @@ def _input_value_changed(self, value, attr_id): class PublishPluginAttrsWidget(QtWidgets.QWidget): """Widget showing publsish plugin attributes for selected instances. - Attributes are defined on publish plugins. Publihs plugin may define + Attributes are defined on publish plugins. Publish plugin may define attribute definitions but must inherit `OpenPypePyblishPluginMixin` (~/openpype/pipeline/publish). At the moment requires to implement `get_attribute_defs` and `convert_attribute_values` class methods. From 4e8ea037b37f9a02787f0fef2a9a4fa1dca364e0 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 20 Feb 2024 16:39:01 +0000 Subject: [PATCH 079/163] Tidy up changes. --- .../defaults/project_settings/maya.json | 43 +----- .../schemas/schema_maya_create.json | 123 +----------------- website/docs/artist_hosts_maya.md | 18 --- 3 files changed, 4 insertions(+), 180 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 1319a41f51a..03c6f79effc 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -559,28 +559,10 @@ "CreateAnimation": { "default_variants": [], "step": 1.0, - "abc_export_flags": [ - "writeColorSets", - "worldSpace", - "writeNormals" - ], - "abc_export_overrides": [ - "step", - "includeParentHierarchy", - "writeNormals", - "includeUserDefinedAttributes", - "attr", - "attrPrefix" - ], "includeParentHierarchy": false, "farm": false, "priority": 50, - "attr": "", - "attrPrefix": "", - "dataFormat": "ogawa", - "preRollStartFrame": 0, - "refresh": false, - "visibleOnly": false + "refresh": false }, "CreateModel": { "enabled": true, @@ -598,33 +580,10 @@ "Main" ], "step": 1.0, - "abc_export_flags": [ - "selection", - "uvWrite", - "writeCreases", - "writeVisibility" - ], - "abc_export_overrides": [ - "attr", - "attrPrefix", - "step", - "writeColorSets", - "writeFaceSets", - "renderableOnly", - "worldSpace" - ], - "renderableOnly": false, - "visibleOnly": false, "includeParentHierarchy": false, "farm": false, "priority": 50, - "attr": "cbId", - "attrPrefix": "", - "dataFormat": "ogawa", - "preRollStartFrame": 0, "refresh": false, - "write_color_sets": false, - "write_face_sets": false, "include_user_defined_attributes": false }, "CreateProxyAlembic": { diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json index 7539d824360..16355eb1a2a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_create.json @@ -160,8 +160,8 @@ }, { "type": "boolean", - "key": "visibleOnly", - "label": "Visible Only default" + "key": "include_user_defined_attributes", + "label": "Include User Defined Attributes" } ] }, @@ -213,74 +213,6 @@ "label": "Default Variants", "object_type": "text" }, - { - "type": "enum", - "key": "abc_export_flags", - "multiselection": true, - "label": "Export Flags (.abc)", - "enum_items": [ - {"autoSubd": "autoSubd"}, - {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, - {"eulerFilter": "eulerFilter"}, - {"noNormals": "noNormals"}, - {"preRoll": "preRoll"}, - {"renderableOnly": "renderableOnly"}, - {"selection": "selection"}, - {"stripNamespaces": "stripNamespaces"}, - {"uvWrite": "uvWrite"}, - {"uvsOnly": "uvsOnly"}, - {"verbose": "verbose"}, - {"wholeFrameGeo": "wholeFrameGeo"}, - {"worldSpace": "worldSpace"}, - {"writeColorSets": "writeColorSets"}, - {"writeCreases": "writeCreases"}, - {"writeFaceSets": "writeFaceSets"}, - {"writeUVSets": "writeUVSets"}, - {"writeVisibility": "writeVisibility"} - ] - }, - { - "type": "enum", - "key": "abc_export_overrides", - "multiselection": true, - "label": "Export Overrides (.abc)", - "enum_items": [ - {"attr": "attr"}, - {"attrPrefix": "attrPrefix"}, - {"autoSubd": "autoSubd"}, - {"dataFormat": "dataFormat"}, - {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, - {"endFrame": "endFrame"}, - {"eulerFilter": "eulerFilter"}, - {"frameRange": "frameRange"}, - {"frameRelativeSample": "frameRelativeSample"}, - {"melPerFrameCallback": "melPerFrameCallback"}, - {"melPostJobCallback": "melPostJobCallback"}, - {"noNormals": "noNormals"}, - {"preRoll": "preRoll"}, - {"preRollStartFrame": "preRollStartFrame"}, - {"pythonPerFrameCallback": "pythonPerFrameCallback"}, - {"pythonPostJobCallback": "pythonPostJobCallback"}, - {"renderableOnly": "renderableOnly"}, - {"root": "root"}, - {"selection": "selection"}, - {"startFrame": "startFrame"}, - {"step": "step"}, - {"stripNamespaces": "stripNamespaces"}, - {"userAttr": "userAttr"}, - {"userAttrPrefix": "userAttrPrefix"}, - {"uvWrite": "uvWrite"}, - {"uvsOnly": "uvsOnly"}, - {"verbose": "verbose"}, - {"wholeFrameGeo": "wholeFrameGeo"}, - {"worldSpace": "worldSpace"}, - {"writeColorSets": "writeColorSets"}, - {"writeCreases": "writeCreases"}, - {"writeFaceSets": "writeFaceSets"}, - {"writeUVSets": "writeUVSets"}, - {"writeVisibility": "writeVisibility"} - ] - }, { "type": "number", "key": "step", @@ -288,16 +220,6 @@ "minimum": 0.0, "decimal": 4 }, - { - "type": "boolean", - "key": "renderableOnly", - "label": "Renderable Only default" - }, - { - "type": "boolean", - "key": "visibleOnly", - "label": "Visible Only default" - }, { "type": "boolean", "key": "includeParentHierarchy", @@ -314,54 +236,15 @@ "label": "Priority default", "minimum": 0 }, - { - "type": "text", - "key": "attr", - "label": "Attr default" - }, - { - "type": "text", - "key": "attrPrefix", - "label": "Attr Prefix default" - }, - { - "type": "enum", - "key": "dataFormat", - "label": "Data Format default", - "enum_items": [ - { - "ogawa": "ogawa" - }, - { - "HDF": "HDF" - } - ] - }, - { - "type": "number", - "key": "preRollStartFrame", - "label": "Pre Roll Start Frame default", - "minimum": 0 - }, { "type": "boolean", "key": "refresh", "label": "Refresh default" }, - { - "type": "boolean", - "key": "write_color_sets", - "label": "DEPRECATED! Write Color Sets" - }, - { - "type": "boolean", - "key": "write_face_sets", - "label": "DEPRECATED! Write Face Sets" - }, { "type": "boolean", "key": "include_user_defined_attributes", - "label": "DEPRECATED! Include User Defined Attributes" + "label": "Include User Defined Attributes" } ] }, diff --git a/website/docs/artist_hosts_maya.md b/website/docs/artist_hosts_maya.md index 60e28f6a05b..364461f4b69 100644 --- a/website/docs/artist_hosts_maya.md +++ b/website/docs/artist_hosts_maya.md @@ -347,24 +347,6 @@ Example setup: - **Include User Defined Attribudes**: include all user defined attributes in the publish. - **Farm**: if your studio has Deadline configured, artists could choose to offload potentially long running export of pointache and publish it to the farm. Only thing that is necessary is to toggle this attribute in created pointcache instance to True. - **Priority**: Farm priority. -- **Euler Filter**: Apply Euler filter while sampling rotations. -- **No Normals**: Present normal data for Alembic poly meshes will not be written. -- **Pre Roll**: This frame range will not be sampled. -- **Renderable Only**: Non-renderable hierarchy (invisible, or templated) will not be written out. -- **UV Write**: Uv data for PolyMesh and SubD shapes will be written to the Alembic file. Only the current uv map is used. -- **Write Color Sets**: Write all color sets on MFnMeshes as color 3 or color 4 indexed geometry parameters with face varying scope. -- **Write Face Sets**: Write all Face sets on MFnMeshes. -- **Whole Frame Geo**: Data for geometry will only be written out on whole frames. -- **World Space**: Any root nodes will be stored in world space. -- **Write Visibility**: Visibility state will be stored in the Alembic file. Otherwise everything written out is treated as visible. -- **Write UV Sets**: Write all uv sets on MFnMeshes as vector 2 indexed geometry parameters with face varying scope. -- **Write Creases**: If the mesh has crease edges or crease vertices, the mesh (OPolyMesh) would now be written out as an OSubD and crease info will be stored in the Alembic file. Otherwise, creases info won't be preserved in Alembic file unless a custom Boolean attribute SubDivisionMesh has been added to mesh node and its value is true. -- **Data Format**: The data format to use to write the file. Can be either "HDF" or "Ogawa". -- **Strip Namespaces**: Namespaces will be stripped off of the node before being written to Alembic. The int specifies how many namespaces will be stripped off of the node name. Be careful that the new stripped name does not collide with other sibling node names. -- **Verbose**: Prints the current frame that is being evaluated. -- **Pre Roll Start Frame**: The frame to start scene evaluation at. This is used to set the starting frame for time dependent translations and can be used to evaluate run-up that isn't actually translated. -- **Include Parent Hierarchy**: Set the root nodes if we don't want to include parents. The roots are to be considered the ones that are the actual direct members of the set. -- **Visible Only**: Does not filter out nodes that are only hidden on some frames as it counts "animated" or "connected" visibilities as if it's always visible. ### Loading Point Caches From 70982d9d799c1b31e7f95a2ddff06f2f1bd1bd09 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 20 Feb 2024 18:31:16 +0000 Subject: [PATCH 080/163] Backwards compatibility wip --- .../create/create_animation_pointcache.py | 18 ++++++++++++++++++ .../maya/plugins/publish/collect_animation.py | 7 ++++++- .../maya/plugins/publish/collect_pointcache.py | 5 +++++ .../maya/plugins/publish/extract_pointcache.py | 17 ----------------- .../defaults/project_settings/maya.json | 5 +++-- 5 files changed, 32 insertions(+), 20 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 6fe078f2adf..739a0fbb268 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -5,6 +5,7 @@ from openpype.lib import ( BoolDef, NumberDef, + TextDef, ) from openpype.pipeline import CreatedInstance @@ -30,6 +31,21 @@ def _get_animation_attr_defs(cls): return defs +def _get_legacy_attr_defs(cls): + """These attributes are defined to hide legacy attributes in the publisher + from the user.""" + return [ + BoolDef("writeColorSets", label="writeColorSets", hidden=True), + BoolDef("writeNormals", label="writeNormals", hidden=True), + BoolDef("writeFaceSets", label="writeFaceSets", hidden=True), + BoolDef("renderableOnly", label="renderableOnly", hidden=True), + BoolDef("visibleOnly", label="visibleOnly", hidden=True), + BoolDef("worldSpace", label="worldSpace", hidden=True), + TextDef("attr", label="attr", hidden=True), + TextDef("attrPrefix", label="attrPrefix", hidden=True), + ] + + class CreateAnimation(plugin.MayaHiddenCreator): """Animation output for character rigs @@ -64,6 +80,7 @@ def collect_instances(self): def get_instance_attr_defs(self): super(CreateAnimation, self).get_instance_attr_defs() defs = _get_animation_attr_defs(self) + defs += _get_legacy_attr_defs(self) return defs @@ -93,6 +110,7 @@ def collect_instances(self): def get_instance_attr_defs(self): super(CreatePointCache, self).get_instance_attr_defs() defs = _get_animation_attr_defs(self) + defs += _get_legacy_attr_defs(self) return defs def create(self, subset_name, instance_data, pre_create_data): diff --git a/openpype/hosts/maya/plugins/publish/collect_animation.py b/openpype/hosts/maya/plugins/publish/collect_animation.py index 33b3d102c2a..8628622bdd2 100644 --- a/openpype/hosts/maya/plugins/publish/collect_animation.py +++ b/openpype/hosts/maya/plugins/publish/collect_animation.py @@ -17,7 +17,7 @@ class CollectAnimationOutputGeometry(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder + 0.4 families = ["animation"] - label = "Collect Animation Output Geometry" + label = "Collect Animation" hosts = ["maya"] ignore_type = ["constraints"] @@ -57,3 +57,8 @@ def process(self, instance): if instance.data.get("farm"): instance.data["families"].append("publish.farm") + + # User defined attributes. + instance.data["includeUserDefinedAttributes"] = ( + instance.data["creator_attributes"]["includeUserDefinedAttributes"] + ) diff --git a/openpype/hosts/maya/plugins/publish/collect_pointcache.py b/openpype/hosts/maya/plugins/publish/collect_pointcache.py index 5578a57f314..8b4289ed80c 100644 --- a/openpype/hosts/maya/plugins/publish/collect_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/collect_pointcache.py @@ -45,3 +45,8 @@ def process(self, instance): if proxy_set: instance.remove(proxy_set) instance.data["setMembers"].remove(proxy_set) + + # User defined attributes. + instance.data["includeUserDefinedAttributes"] = ( + instance.data["creator_attributes"]["includeUserDefinedAttributes"] + ) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 26957b02f51..68fadef6ca3 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -43,7 +43,6 @@ class ExtractAlembic(publish.Extractor, OpenPypePyblishPluginMixin): preRollStartFrame = 0 pythonPerFrameCallback = "" pythonPostJobCallback = "" - stripNamespaces = -1 userAttr = "" userAttrPrefix = "" export_overrides = [] @@ -257,22 +256,6 @@ def get_attribute_defs(cls): "label": "pythonPostJobCallback", } }, - "stripNamespaces": { - "def": NumberDef, - "kwargs": { - "label": "stripNamespaces", - "tooltip": ( - "If this flag is present namespaces will be stripped " - "off of the node before being written to Alembic. The " - "int after the flag specifies how many namespaces will" - " be stripped off of the node name. Be careful that " - "the new stripped name does not collide with other " - "sibling node names.\n\nExamples:\n taco:foo:bar would" - " be written as just bar with -sn 0\ntaco:foo:bar " - "would be written as foo:bar with -sn 1" - ), - } - }, "userAttr": { "def": TextDef, "kwargs": { diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 03c6f79effc..6d85c8e3944 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -562,7 +562,8 @@ "includeParentHierarchy": false, "farm": false, "priority": 50, - "refresh": false + "refresh": false, + "include_user_defined_attributes": false }, "CreateModel": { "enabled": true, @@ -1112,7 +1113,6 @@ ], "flags": [ "stripNamespaces", - "writeColorSets", "writeNormals", "worldSpace" ], @@ -1131,6 +1131,7 @@ "attr", "attrPrefix", "worldSpace", + "writeColorSets", "writeNormals", "writeFaceSets", "renderableOnly", From 64b366ca3d703fcd1cde53c917be3ab0a0b79d0f Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Feb 2024 12:08:44 +0000 Subject: [PATCH 081/163] Backwards compatibility --- .../create/create_animation_pointcache.py | 61 ++++++++++++++----- .../plugins/publish/extract_pointcache.py | 1 + 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 739a0fbb268..791aa0072b2 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -5,7 +5,6 @@ from openpype.lib import ( BoolDef, NumberDef, - TextDef, ) from openpype.pipeline import CreatedInstance @@ -31,19 +30,45 @@ def _get_animation_attr_defs(cls): return defs -def _get_legacy_attr_defs(cls): - """These attributes are defined to hide legacy attributes in the publisher - from the user.""" - return [ - BoolDef("writeColorSets", label="writeColorSets", hidden=True), - BoolDef("writeNormals", label="writeNormals", hidden=True), - BoolDef("writeFaceSets", label="writeFaceSets", hidden=True), - BoolDef("renderableOnly", label="renderableOnly", hidden=True), - BoolDef("visibleOnly", label="visibleOnly", hidden=True), - BoolDef("worldSpace", label="worldSpace", hidden=True), - TextDef("attr", label="attr", hidden=True), - TextDef("attrPrefix", label="attrPrefix", hidden=True), +def extract_alembic_attributes(node_data, class_name): + """This is a legacy transfer of creator attributes to publish attributes + for ExtractAlembic/ExtractAnimation plugin. + """ + publish_attributes = node_data["publish_attributes"] + + if class_name in publish_attributes: + return node_data + + extract_alembic_flags = [ + "writeColorSets", + "writeFaceSets", + "writeNormals", + "renderableOnly", + "visibleOnly", + "worldSpace", + "renderableOnly" + ] + extract_alembic_attributes = [ + "attr", + "attrPrefix", + "visibleOnly" ] + attributes = extract_alembic_flags + extract_alembic_attributes + plugin_attributes = {"flag_overrides": []} + for attr in attributes: + if attr not in node_data["creator_attributes"].keys(): + continue + value = node_data["creator_attributes"].pop(attr) + + if value and attr in extract_alembic_flags: + plugin_attributes["flag_overrides"].append(attr) + + if attr in extract_alembic_attributes: + plugin_attributes[attr] = value + + publish_attributes[class_name] = plugin_attributes + + return node_data class CreateAnimation(plugin.MayaHiddenCreator): @@ -74,13 +99,17 @@ def collect_instances(self): for node in cached_subsets.get(self.identifier, []): node_data = self.read_instance_node(node) + + node_data = extract_alembic_attributes( + node_data, "ExtractAnimation" + ) + created_instance = CreatedInstance.from_existing(node_data, self) self._add_instance_to_context(created_instance) def get_instance_attr_defs(self): super(CreateAnimation, self).get_instance_attr_defs() defs = _get_animation_attr_defs(self) - defs += _get_legacy_attr_defs(self) return defs @@ -104,13 +133,15 @@ def collect_instances(self): for node in cached_subsets.get(self.identifier, []): node_data = self.read_instance_node(node) + + node_data = extract_alembic_attributes(node_data, "ExtractAlembic") + created_instance = CreatedInstance.from_existing(node_data, self) self._add_instance_to_context(created_instance) def get_instance_attr_defs(self): super(CreatePointCache, self).get_instance_attr_defs() defs = _get_animation_attr_defs(self) - defs += _get_legacy_attr_defs(self) return defs def create(self, subset_name, instance_data, pre_create_data): diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 68fadef6ca3..92033d57c4c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -45,6 +45,7 @@ class ExtractAlembic(publish.Extractor, OpenPypePyblishPluginMixin): pythonPostJobCallback = "" userAttr = "" userAttrPrefix = "" + visibleOnly = False export_overrides = [] def process(self, instance): From 05dd980b80da8fa772579498c75575be3d02bb3d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Feb 2024 12:12:58 +0000 Subject: [PATCH 082/163] Revert AYON settings --- server_addon/maya/server/settings/creators.py | 135 ------------------ 1 file changed, 135 deletions(-) diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index 492b2c180d1..d7199936852 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -1,5 +1,3 @@ -from typing import Literal - from ayon_server.settings import ( BaseSettingsModel, SettingsField, @@ -59,74 +57,6 @@ class BasicExportMeshModel(BaseSettingsModel): ) -def alembic_booleans_enum(): - return [ - "autoSubd", - "dontSkipUnwritten", - "eulerFilter", - "noNormals", - "preRoll", - "renderableOnly", - "selection", - "stripNamespaces", - "uvWrite", - "uvsOnly", - "verbose", - "wholeFrameGeo", - "worldSpace", - "writeColorSets", - "writeCreases", - "writeFaceSets", - "writeUVSets", - "writeVisibility", - ] - -def alembic_arguments_enum(): - return [ - "attr", - "attrPrefix", - "autoSubd", - "dataFormat", - "dontSkipUnwrittenFrames", - "endFrame", - "eulerFilter", - "frameRange", - "frameRelativeSample", - "melPerFrameCallback", - "melPostJobCallback", - "noNormals", - "preRoll", - "preRollStartFrame", - "pythonPerFrameCallback", - "pythonPostJobCallback", - "renderableOnly", - "root", - "selection", - "startFrame", - "step", - "stripNamespaces", - "userAttr", - "userAttrPrefix", - "uvWrite", - "uvsOnly", - "verbose", - "wholeFrameGeo", - "worldSpace", - "writeColorSets", - "writeCreases", - "writeFaceSets", - "writeUVSets", - "writeVisibility", - ] - -AlembicDataFormat = Literal["ogawa", "hdf5"] - -def alembic_data_formats(): - return [ - "ogawa", - "hdf5" - ] - class CreateAnimationModel(BaseSettingsModel): write_color_sets: bool = SettingsField(title="Write Color Sets") write_face_sets: bool = SettingsField(title="Write Face Sets") @@ -148,20 +78,6 @@ class CreateAnimationModel(BaseSettingsModel): title="Submit to the Farm") attr: str = SettingsField(title="Attributes") attr_prefix: str = SettingsField(title="Attributes Prefix") - data_format: AlembicDataFormat = SettingsField( - enum_resolver=alembic_data_formats, - title="Data Format", - ) - abc_export_flags: list[str] = SettingsField( - default_factory=list, - enum_resolver=alembic_booleans_enum, - title="Export Flags (.abc)", - ) - abc_export_overrides: list[str] = SettingsField( - default_factory=list, - enum_resolver=alembic_arguments_enum, - title="Export Overrides (.abc)", - ) class CreatePointCacheModel(BaseSettingsModel): @@ -185,20 +101,6 @@ class CreatePointCacheModel(BaseSettingsModel): title="Submit to the Farm") attr: str = SettingsField(title="Attributes") attr_prefix: str = SettingsField(title="Attributes Prefix") - data_format: AlembicDataFormat = SettingsField( - enum_resolver=alembic_data_formats, - title="Data Format", - ) - abc_export_flags: list[str] = SettingsField( - default_factory=list, - enum_resolver=alembic_booleans_enum, - title="Export Flags (.abc)", - ) - abc_export_overrides: list[str] = SettingsField( - default_factory=list, - enum_resolver=alembic_arguments_enum, - title="Export Overrides (.abc)", - ) class CreateProxyAlembicModel(BaseSettingsModel): @@ -402,26 +304,8 @@ class CreatorsModel(BaseSettingsModel): "Main" ], "step": 1.0, - "abc_export_flags": [ - "writeColorSets", - "visibleOnly", - "worldSpace", - "writeNormals" - ], - "abc_export_overrides": [ - "step", - "includeParentHierarchy", - "writeNormals", - "includeUserDefinedAttributes", - "attr", - "attrPrefix" - ], "farm": False, "priority": 50, - "attr": "", - "attr_prefix": "", - "data_format": "ogawa", - "pre_roll_start_frame": 0, "refresh": False, }, "CreateModel": { @@ -443,28 +327,9 @@ class CreatorsModel(BaseSettingsModel): "Main" ], "step": 1.0, - "abc_export_flags": [ - "selection", - "uvWrite", - "writeCreases", - "writeVisibility" - ], - "abc_export_overrides": [ - "attr", - "attrPrefix", - "step", - "writeColorSets", - "writeFaceSets", - "renderableOnly", - "worldSpace" - ], "include_parent_hierarchy": False, "farm": False, "priority": 50, - "attr": "cbId", - "attr_prefix": "", - "data_format": "ogawa", - "pre_roll_start_frame": 0, "refresh": False }, "CreateProxyAlembic": { From c123e831d5468b17a9e66567cc08d08e0d3ae726 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Wed, 21 Feb 2024 12:16:38 +0000 Subject: [PATCH 083/163] revert ayon settings --- server_addon/maya/server/settings/creators.py | 35 ++----------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/server_addon/maya/server/settings/creators.py b/server_addon/maya/server/settings/creators.py index d7199936852..5f3b850a1fe 100644 --- a/server_addon/maya/server/settings/creators.py +++ b/server_addon/maya/server/settings/creators.py @@ -68,16 +68,6 @@ class CreateAnimationModel(BaseSettingsModel): default_factory=list, title="Default Products" ) - priority: int = SettingsField( - title="Farm Job Priority") - pre_roll_start_frame: int = SettingsField(title="Pre Roll Start Frame") - refresh: bool = SettingsField( - title="Refresh") - step: int = SettingsField(title="Step") - farm: bool = SettingsField( - title="Submit to the Farm") - attr: str = SettingsField(title="Attributes") - attr_prefix: str = SettingsField(title="Attributes Prefix") class CreatePointCacheModel(BaseSettingsModel): @@ -91,16 +81,6 @@ class CreatePointCacheModel(BaseSettingsModel): default_factory=list, title="Default Products" ) - priority: int = SettingsField( - title="Farm Job Priority") - pre_roll_start_frame: int = SettingsField(title="Pre Roll Start Frame") - refresh: bool = SettingsField( - title="Refresh") - step: int = SettingsField(title="Step") - farm: bool = SettingsField( - title="Submit to the Farm") - attr: str = SettingsField(title="Attributes") - attr_prefix: str = SettingsField(title="Attributes Prefix") class CreateProxyAlembicModel(BaseSettingsModel): @@ -298,15 +278,11 @@ class CreatorsModel(BaseSettingsModel): "CreateAnimation": { "write_color_sets": False, "write_face_sets": False, - "include_user_defined_attributes": False, "include_parent_hierarchy": False, + "include_user_defined_attributes": False, "default_variants": [ "Main" - ], - "step": 1.0, - "farm": False, - "priority": 50, - "refresh": False, + ] }, "CreateModel": { "enabled": True, @@ -325,12 +301,7 @@ class CreatorsModel(BaseSettingsModel): "include_user_defined_attributes": False, "default_variants": [ "Main" - ], - "step": 1.0, - "include_parent_hierarchy": False, - "farm": False, - "priority": 50, - "refresh": False + ] }, "CreateProxyAlembic": { "enabled": True, From 1f1d7c3a231c9377eb4bf5c090f946fdc5ae8d95 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Feb 2024 12:51:14 +0000 Subject: [PATCH 084/163] Missing writeNormals flag --- .../schemas/projects_schema/schemas/schema_maya_publish.json | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 9af9dbb32b5..38b56b58aed 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -1033,6 +1033,7 @@ {"worldSpace": "worldSpace"}, {"writeColorSets": "writeColorSets"}, {"writeFaceSets": "writeFaceSets"}, + {"writeNormals": "writeNormals"}, {"writeUVSets": "writeUVSets"}, {"writeVisibility": "writeVisibility"} ] From 669e0a79550766030d7e573b45c41176bb9d1266 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 22 Feb 2024 13:16:30 +0000 Subject: [PATCH 085/163] Add OP settings and convert in plugin --- .../plugins/publish/collect_clip_effects.py | 10 +++++--- .../defaults/project_settings/hiero.json | 4 +++ .../projects_schema/schema_project_hiero.json | 25 +++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py index d7f646ebc9f..44767e458ac 100644 --- a/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py +++ b/openpype/hosts/hiero/plugins/publish/collect_clip_effects.py @@ -72,9 +72,13 @@ def process(self, instance): subset_split.insert(0, "effect") - effect_categories = { - x["name"]: x["effect_classes"] for x in self.effect_categories - } + # Need to convert to dict for AYON settings. This isinstance check can + # be removed in the future when OpenPype is no longer. + effect_categories = self.effect_categories + if isinstance(self.effect_categories, list): + effect_categories = { + x["name"]: x["effect_classes"] for x in self.effect_categories + } category_by_effect = {"": ""} for key, values in effect_categories.items(): diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index 9c83733b096..efd80a8876d 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -69,6 +69,10 @@ "tags_addition": [ "review" ] + }, + "CollectClipEffects": { + "enabled": true, + "effect_categories": {} } }, "filters": {}, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json index d80edf902b2..73bd475815a 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json @@ -312,6 +312,31 @@ "label": "Tags addition" } ] + }, + { + "type": "dict", + "collapsible": true, + "checkbox_key": "enabled", + "key": "CollectClipEffects", + "label": "Collect Clip Effects", + "is_group": true, + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "dict-modifiable", + "key": "effect_categories", + "label": "Effect Categories", + "object_type": { + "type": "list", + "key": "effects_classes", + "object_type": "text" + } + } + ] } ] }, From 81c657227ef19487c03a91d92bebca1705cbb830 Mon Sep 17 00:00:00 2001 From: Simone Barbieri Date: Fri, 23 Feb 2024 17:12:51 +0000 Subject: [PATCH 086/163] Use folderpath when collecting the render instance --- .../hosts/unreal/plugins/publish/collect_render_instances.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/unreal/plugins/publish/collect_render_instances.py b/openpype/hosts/unreal/plugins/publish/collect_render_instances.py index dad0310dfcc..d7b9191fa32 100644 --- a/openpype/hosts/unreal/plugins/publish/collect_render_instances.py +++ b/openpype/hosts/unreal/plugins/publish/collect_render_instances.py @@ -64,7 +64,7 @@ def process(self, instance): new_data = new_instance.data - new_data["asset"] = seq_name + new_data["asset"] = f"/{s.get('output')}" new_data["setMembers"] = seq_name new_data["family"] = "render" new_data["families"] = ["render", "review"] From 38be2d8e36e42c39e3f9cd185b6f5eb912525f62 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 24 Feb 2024 03:29:22 +0000 Subject: [PATCH 087/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 95203e17c9b..668d180d207 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.8-nightly.1" +__version__ = "3.18.8-nightly.2" From 1dcdb311054a42b8a85c56664553dd3118370aec Mon Sep 17 00:00:00 2001 From: Jose Caraballo Date: Mon, 26 Feb 2024 17:39:55 +0100 Subject: [PATCH 088/163] Pass correct arguments to function. --- openpype/tools/publisher/control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 13d007dd356..41039a5be0d 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -2337,7 +2337,7 @@ def run_action(self, plugin_id, action_id): "title": "Action failed", "message": "Action failed.", "traceback": "".join( - traceback.format_exception(exception) + traceback.format_exception(type(exception), exception, exception.__traceback__) ), "label": action.__name__, "identifier": action.id From 98deab1f638ac83b03c9b34d82e634db16690f83 Mon Sep 17 00:00:00 2001 From: Jose Caraballo Date: Mon, 26 Feb 2024 17:51:48 +0100 Subject: [PATCH 089/163] Fix line length. --- openpype/tools/publisher/control.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py index 41039a5be0d..f9b8bcc5129 100644 --- a/openpype/tools/publisher/control.py +++ b/openpype/tools/publisher/control.py @@ -2337,7 +2337,11 @@ def run_action(self, plugin_id, action_id): "title": "Action failed", "message": "Action failed.", "traceback": "".join( - traceback.format_exception(type(exception), exception, exception.__traceback__) + traceback.format_exception( + type(exception), + exception, + exception.__traceback__ + ) ), "label": action.__name__, "identifier": action.id From 3a3b535001460d4e20cbccc3e05ca265872c4037 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 5 Mar 2024 11:01:59 +0000 Subject: [PATCH 090/163] Fix submit_max_deadline imports (#6235) --- .../deadline/plugins/publish/submit_max_deadline.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py index e31de0a101d..07bbb1cacbb 100644 --- a/openpype/modules/deadline/plugins/publish/submit_max_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_max_deadline.py @@ -16,11 +16,6 @@ replace_with_published_scene_path ) from openpype.pipeline.publish import KnownPublishError -from openpype.hosts.max.api.lib import ( - get_current_renderer, - get_multipass_setting -) -from openpype.hosts.max.api.lib_rendersettings import RenderSettings from openpype_modules.deadline import abstract_submit_deadline from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo from openpype.lib import is_running_from_build @@ -294,6 +289,9 @@ def get_plugin_info_through_camera(self, camera): Args: infos(dict): a dictionary with plugin info. """ + from openpype.hosts.max.api.lib import get_current_renderer + from openpype.hosts.max.api.lib_rendersettings import RenderSettings + instance = self._instance # set the target camera plugin_info = copy.deepcopy(self.plugin_info) @@ -359,6 +357,8 @@ def _use_published_name_for_multiples(self, data, project_settings): job_info_list (list): A list of multiple job infos plugin_info_list (list): A list of multiple plugin infos """ + from openpype.hosts.max.api.lib import get_multipass_setting + job_info_list = [] plugin_info_list = [] instance = self._instance From 73bc49750eb1f5a71d063082e5a2500c152b303f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 6 Mar 2024 16:36:47 +0100 Subject: [PATCH 091/163] Update openpype/hosts/resolve/api/lib.py --- openpype/hosts/resolve/api/lib.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/openpype/hosts/resolve/api/lib.py b/openpype/hosts/resolve/api/lib.py index e9c4921bb54..96caacf8d10 100644 --- a/openpype/hosts/resolve/api/lib.py +++ b/openpype/hosts/resolve/api/lib.py @@ -279,10 +279,6 @@ def create_timeline_item( # timing variables if all([timeline_in, source_start, source_end]): fps = timeline.GetSetting("timelineFrameRate") - # Strangely, Resolve seem to output '23' instead of 23.976 - if fps == '23': - fps = 23.976 - duration = source_end - source_start timecode_in = frames_to_timecode(timeline_in, fps) timecode_out = frames_to_timecode(timeline_in + duration, fps) From 531fd55bee86d6a1d6d9947059745570f0c49ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 6 Mar 2024 16:37:50 +0100 Subject: [PATCH 092/163] Update openpype/hosts/resolve/api/plugin.py --- openpype/hosts/resolve/api/plugin.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 2781ab6b758..36f5a2db7d1 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -408,11 +408,12 @@ def load(self, files): _clip_property = media_pool_item.GetClipProperty source_in = int(_clip_property("Start")) source_out = int(_clip_property("End")) - + source_duration = int(_clip_property("Frames")) + # Trim clip start if slate is present if "slate" in self.data["versionData"]["families"]: source_in += 1 - source_duration = source_out - source_in + 1 + source_duration = source_out - source_in + 1 if not self.with_handles: # Load file without the handles of the source media From 7d2ede8d6533c717daea77e199f15fc0b823d56e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C5=BEek?= Date: Wed, 6 Mar 2024 16:39:27 +0100 Subject: [PATCH 093/163] Update openpype/hosts/resolve/api/plugin.py --- openpype/hosts/resolve/api/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/resolve/api/plugin.py b/openpype/hosts/resolve/api/plugin.py index 36f5a2db7d1..fc7900321c4 100644 --- a/openpype/hosts/resolve/api/plugin.py +++ b/openpype/hosts/resolve/api/plugin.py @@ -409,7 +409,6 @@ def load(self, files): source_in = int(_clip_property("Start")) source_out = int(_clip_property("End")) source_duration = int(_clip_property("Frames")) - # Trim clip start if slate is present if "slate" in self.data["versionData"]["families"]: source_in += 1 From 2e76f273c752df404b7e55079d9cd8a047d98f79 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 09:30:33 +0000 Subject: [PATCH 094/163] Change labels and export_overrides key --- .../settings/defaults/project_settings/maya.json | 2 +- .../schemas/schema_maya_publish.json | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 6d85c8e3944..7d0af6feb4f 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1127,7 +1127,7 @@ "userAttr": "", "userAttrPrefix": "", "visibleOnly": false, - "export_overrides": [ + "overrides": [ "attr", "attrPrefix", "worldSpace", diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json index 38b56b58aed..88f3d476ca3 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_publish.json @@ -1017,7 +1017,7 @@ "type": "enum", "key": "flags", "multiselection": true, - "label": "Flags", + "label": "Export Flags", "enum_items": [ {"autoSubd": "autoSubd"}, {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, @@ -1041,12 +1041,12 @@ { "type": "text", "key": "attr", - "label": "Attributes" + "label": "Custom Attributes" }, { "type": "text", "key": "attrPrefix", - "label": "Attr Prefix" + "label": "Custom Attributes Prefix" }, { "type": "enum", @@ -1100,7 +1100,7 @@ { "type": "boolean", "key": "visibleOnly", - "label": "visibleOnly" + "label": "Visible Only" }, { "type": "splitter" @@ -1111,12 +1111,12 @@ }, { "type": "enum", - "key": "export_overrides", + "key": "overrides", "multiselection": true, - "label": "Export Overrides", + "label": "Exposed Overrides", "enum_items": [ - {"attr": "attr"}, - {"attrPrefix": "attrPrefix"}, + {"attr": "Custom Attributes"}, + {"attrPrefix": "Custom Attributes Prefix"}, {"autoSubd": "autoSubd"}, {"dataFormat": "dataFormat"}, {"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"}, From 370e422e1aac0c1e339c5791aabb44e437ce6538 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 09:30:52 +0000 Subject: [PATCH 095/163] Fix plugin --- openpype/hosts/maya/plugins/publish/extract_pointcache.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index 92033d57c4c..a951f9b8cab 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -285,13 +285,13 @@ def get_attribute_defs(cls): ]) # The Arguments that can be modified by the Publisher - export_overrides = set(getattr(cls, "export_overrides", set())) + overrides = set(getattr(cls, "overrides", set())) # What we have set in the Settings as defaults. flags = set(getattr(cls, "flags", set())) - enabled_flags = [x for x in flags if x in export_overrides] - flag_overrides = export_overrides - set(override_defs.keys()) + enabled_flags = [x for x in flags if x in overrides] + flag_overrides = overrides - set(override_defs.keys()) defs.append( EnumDef( "flag_overrides", @@ -303,7 +303,7 @@ def get_attribute_defs(cls): ) for key, value in override_defs.items(): - if key not in export_overrides: + if key not in overrides: continue kwargs = value["kwargs"] From b976a5d05a13d8636ba79d544bd7ec126eb80e5a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 11:40:53 +0000 Subject: [PATCH 096/163] Dont brute force the colorspace list --- openpype/hosts/nuke/api/utils.py | 41 ++++++++++++++------------------ 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index 62552a24083..db9a6fcd055 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -1,4 +1,6 @@ import os +import re + import nuke from openpype import resources @@ -110,35 +112,28 @@ def colorspace_exists_on_node(node, colorspace_name): def get_colorspace_list(colorspace_knob): """Get available colorspace profile names - Because the values returned from colorspace_knob.values() do not correspond - to the value returned from colorspace_knob.value(), and the extracted - colorspace comes from using colorspace_knob.value(), we need to iterate - through all values to get the correct value. - - A code example of the above would be: - - for count, value in enumerate(colorspace_knob.values()): - colorspace_knob.setValue(count) - print(colorspace_knob.value() in colorspace_knob.values()) - Args: colorspace_knob (nuke.Knob): nuke knob object Returns: list: list of strings names of profiles """ - original_value = colorspace_knob.value() - - colorspaces = [] - - try: - for count, _ in enumerate(colorspace_knob.values()): - colorspace_knob.setValue(count) - colorspaces.append(colorspace_knob.value()) - finally: - colorspace_knob.setValue(original_value) - - return colorspaces + results = [] + + # This pattern is to match with roles which uses an indentation and + # parentheses with original colorspace. The value returned from the + # colorspace is the string before the indentation, so we'll need to + # convert the values to match with value returned from the knob, + # ei. knob.value(). + pattern = r".*\t.* \(.*\)" + for colorspace in nuke.getColorspaceList(colorspace_knob): + match = re.search(pattern, colorspace) + if match: + results.append(colorspace.split("\t")[0]) + else: + results.append(colorspace) + + return results def is_headless(): From c4d58ca363676b6c7d8af0a5538bcec074270ccf Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Thu, 7 Mar 2024 12:30:54 +0000 Subject: [PATCH 097/163] Update openpype/hosts/nuke/api/utils.py --- openpype/hosts/nuke/api/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/api/utils.py b/openpype/hosts/nuke/api/utils.py index db9a6fcd055..4220f007354 100644 --- a/openpype/hosts/nuke/api/utils.py +++ b/openpype/hosts/nuke/api/utils.py @@ -129,7 +129,7 @@ def get_colorspace_list(colorspace_knob): for colorspace in nuke.getColorspaceList(colorspace_knob): match = re.search(pattern, colorspace) if match: - results.append(colorspace.split("\t")[0]) + results.append(colorspace.split("\t", 1)[0]) else: results.append(colorspace) From 75d74bf826ae1f418bb7fdfbcf5b5726c97e611c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 19:10:40 +0000 Subject: [PATCH 098/163] Remove representation unnessecary frameStart --- openpype/hosts/nuke/plugins/publish/collect_writes.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_writes.py b/openpype/hosts/nuke/plugins/publish/collect_writes.py index 6f9245f5b96..2bf43ed75a8 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_writes.py +++ b/openpype/hosts/nuke/plugins/publish/collect_writes.py @@ -194,7 +194,6 @@ def _set_additional_instance_data( "frameEndHandle": last_frame, }) - # TODO temporarily set stagingDir as persistent for backward # compatibility. This is mainly focused on `renders`folders which # were previously not cleaned up (and could be used in read notes) @@ -269,10 +268,6 @@ def _get_existing_frames_representation( "tags": [] } - frame_start_str = self._get_frame_start_str(first_frame, last_frame) - - representation['frameStart'] = frame_start_str - # set slate frame collected_frames = self._add_slate_frame_to_collected_frames( instance, From d99ad33be394a7c30cc705c11b3d8d7f22bcbb1a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 7 Mar 2024 19:10:58 +0000 Subject: [PATCH 099/163] Exclude instances nodes from slate collection. --- openpype/hosts/nuke/plugins/publish/collect_slate_node.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/nuke/plugins/publish/collect_slate_node.py b/openpype/hosts/nuke/plugins/publish/collect_slate_node.py index 3baa0cd9b56..ac30bd6051a 100644 --- a/openpype/hosts/nuke/plugins/publish/collect_slate_node.py +++ b/openpype/hosts/nuke/plugins/publish/collect_slate_node.py @@ -17,7 +17,8 @@ def process(self, instance): ( n_ for n_ in nuke.allNodes() if "slate" in n_.name().lower() - if not n_["disable"].getValue() + if not n_["disable"].getValue() and + "publish_instance" not in n_.knobs() # Exclude instance nodes. ), None ) From e83bd724240c8f7f78cde3d8720a89b1e07a5850 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 9 Mar 2024 08:48:55 +0000 Subject: [PATCH 100/163] Hardcoded rule for cryptomatte and multipartEXR --- openpype/pipeline/farm/pyblish_functions.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/openpype/pipeline/farm/pyblish_functions.py b/openpype/pipeline/farm/pyblish_functions.py index 975fdd31cc0..cf2f05fcb08 100644 --- a/openpype/pipeline/farm/pyblish_functions.py +++ b/openpype/pipeline/farm/pyblish_functions.py @@ -621,7 +621,15 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, preview = match_aov_pattern(app, aov_patterns, render_file_name) # toggle preview on if multipart is on - if instance.data.get("multipartExr"): + # Because we cant query the multipartExr data member of each AOV we'll + # need to have hardcoded rule of excluding any renders with + # "crytomatte" in the file name from being a multipart EXR. This issue + # happens with Redshift that forces Cryptomatte renders to be separate + # files even when the rest of the AOVs are merged into a single EXR. + # There might be an edge case where the main instance has cryptomatte + # in the name even though its a multipart EXR. + if (instance.data.get("multipartExr") and + "cryptomatte" not in render_file_name.lower()): log.debug("Adding preview tag because its multipartExr") preview = True From 4a5525ac9c486bf29ca957a96835de4c500b0049 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sat, 9 Mar 2024 09:15:00 +0000 Subject: [PATCH 101/163] Enforce multipartExr to false instead of inheriting --- openpype/pipeline/farm/pyblish_functions.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/farm/pyblish_functions.py b/openpype/pipeline/farm/pyblish_functions.py index cf2f05fcb08..bd532969d23 100644 --- a/openpype/pipeline/farm/pyblish_functions.py +++ b/openpype/pipeline/farm/pyblish_functions.py @@ -620,6 +620,11 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, aov_patterns = aov_filter preview = match_aov_pattern(app, aov_patterns, render_file_name) + + new_instance = deepcopy(skeleton) + new_instance["subset"] = subset_name + new_instance["subsetGroup"] = group_name + # toggle preview on if multipart is on # Because we cant query the multipartExr data member of each AOV we'll # need to have hardcoded rule of excluding any renders with @@ -632,10 +637,8 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, "cryptomatte" not in render_file_name.lower()): log.debug("Adding preview tag because its multipartExr") preview = True - - new_instance = deepcopy(skeleton) - new_instance["subset"] = subset_name - new_instance["subsetGroup"] = group_name + else: + new_instance["multipartExr"] = False # explicitly disable review by user preview = preview and not do_not_add_review From 30f476782327fe0f8d66482579e1a9896696fff9 Mon Sep 17 00:00:00 2001 From: Jakub Trllo Date: Tue, 12 Mar 2024 16:26:46 +0100 Subject: [PATCH 102/163] copy and open of published workfile should work --- openpype/tools/ayon_workfiles/control.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openpype/tools/ayon_workfiles/control.py b/openpype/tools/ayon_workfiles/control.py index 9d19571267e..b932f4eeb2c 100644 --- a/openpype/tools/ayon_workfiles/control.py +++ b/openpype/tools/ayon_workfiles/control.py @@ -573,6 +573,7 @@ def copy_workfile_representation( workdir, filename, template_key, + src_filepath=representation_filepath ) except Exception: failed = True From 69c31961a81ba1559d22307d1214816d0e5dc6ae Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 Mar 2024 12:48:04 +0100 Subject: [PATCH 103/163] Refactor print statements to use format method - Updated print statements to use the format method for string interpolation. This improves readability and consistency in the codebase. --- openpype/scripts/ocio_wrapper.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/openpype/scripts/ocio_wrapper.py b/openpype/scripts/ocio_wrapper.py index 0a78e33c1ff..45227c11f8b 100644 --- a/openpype/scripts/ocio_wrapper.py +++ b/openpype/scripts/ocio_wrapper.py @@ -83,7 +83,7 @@ def get_colorspace(in_path, out_path): with open(json_path, "w") as f_: json.dump(out_data, f_) - print(f"Colorspace data are saved to '{json_path}'") + print("Colorspace data are saved to '{}'".format(json_path)) def _get_colorspace_data(config_path): @@ -102,7 +102,7 @@ def _get_colorspace_data(config_path): if not config_path.is_file(): raise IOError( - f"Input path `{config_path}` should be `config.ocio` file") + "Input path `{}` should be `config.ocio` file".format(config_path)) config = ocio.Config().CreateFromFile(str(config_path)) @@ -118,10 +118,9 @@ def _get_colorspace_data(config_path): for color in config.getColorSpaces() }, "displays_views": { - f"{view} ({display})": { + "{} ({})".format(display, view): { "display": display, "view": view - } for display in config.getDisplays() for view in config.getViews(display) @@ -181,7 +180,7 @@ def get_views(in_path, out_path): with open(json_path, "w") as f_: json.dump(out_data, f_) - print(f"Viewer data are saved to '{json_path}'") + print("Viewer data are saved to '{}'".format(json_path)) def _get_views_data(config_path): @@ -207,11 +206,12 @@ def _get_views_data(config_path): for display in config.getDisplays(): for view in config.getViews(display): colorspace = config.getDisplayViewColorSpaceName(display, view) - # Special token. See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views # noqa + # Special token. + # See https://opencolorio.readthedocs.io/en/latest/guides/authoring/authoring.html#shared-views # noqa if colorspace == "": colorspace = display - data_[f"{display}/{view}"] = { + data_["{}/{}".format(display, view)] = { "display": display, "view": view, "colorspace": colorspace @@ -254,7 +254,7 @@ def get_version(config_path, out_path): with open(json_path, "w") as f_: json.dump(out_data, f_) - print(f"Config version data are saved to '{json_path}'") + print("Config version data are saved to '{}'".format(json_path)) def _get_version_data(config_path): @@ -325,7 +325,7 @@ def get_config_file_rules_colorspace_from_filepath( with open(json_path, "w") as f_: json.dump(colorspace, f_) - print(f"Colorspace name is saved to '{json_path}'") + print("Colorspace name is saved to '{}'".format(json_path)) def _get_config_file_rules_colorspace_from_filepath(config_path, filepath): @@ -345,7 +345,7 @@ def _get_config_file_rules_colorspace_from_filepath(config_path, filepath): if not config_path.is_file(): raise IOError( - f"Input path `{config_path}` should be `config.ocio` file") + "Input path `{}` should be `config.ocio` file".format(config_path)) config = ocio.Config().CreateFromFile(str(config_path)) @@ -427,7 +427,8 @@ def get_display_view_colorspace_name(in_path, out_path, with open(out_path, "w") as f: json.dump(out_data, f) - print(f"Display view colorspace saved to '{out_path}'") + print("Display view colorspace saved to '{}'".format(out_path)) + if __name__ == '__main__': main() From d43458945a3495080200c54bdba8e32156439cc0 Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 Mar 2024 16:37:41 +0100 Subject: [PATCH 104/163] nuke12 is having its own PyOpenColorIO 1.1.1 which is missing some functionalinty --- openpype/pipeline/colorspace.py | 4 +++ openpype/scripts/ocio_wrapper.py | 60 +++++++++++--------------------- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index 9f720f6ae95..2d1837e8ead 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -463,9 +463,13 @@ def compatibility_check(): try: import PyOpenColorIO # noqa: F401 + config = PyOpenColorIO.GetCurrentConfig() + config.getDisplayViewColorSpaceName("sRGB", "sRGB") CachedData.has_compatible_ocio_package = True except ImportError: CachedData.has_compatible_ocio_package = False + except AttributeError: + CachedData.has_compatible_ocio_package = False # compatible return CachedData.has_compatible_ocio_package diff --git a/openpype/scripts/ocio_wrapper.py b/openpype/scripts/ocio_wrapper.py index 45227c11f8b..fe3fbb5d920 100644 --- a/openpype/scripts/ocio_wrapper.py +++ b/openpype/scripts/ocio_wrapper.py @@ -18,10 +18,9 @@ - returning all available viewers found in input config path. """ - +import os import click import json -from pathlib import Path import PyOpenColorIO as ocio @@ -76,14 +75,12 @@ def get_colorspace(in_path, out_path): > pyton.exe ./ocio_wrapper.py config get_colorspace --in_path= --out_path= """ - json_path = Path(out_path) - out_data = _get_colorspace_data(in_path) - with open(json_path, "w") as f_: + with open(out_path, "w") as f_: json.dump(out_data, f_) - print("Colorspace data are saved to '{}'".format(json_path)) + print("Colorspace data are saved to '{}'".format(out_path)) def _get_colorspace_data(config_path): @@ -98,13 +95,11 @@ def _get_colorspace_data(config_path): Returns: dict: aggregated available colorspaces """ - config_path = Path(config_path) - - if not config_path.is_file(): + if not os.path.isfile(config_path): raise IOError( "Input path `{}` should be `config.ocio` file".format(config_path)) - config = ocio.Config().CreateFromFile(str(config_path)) + config = ocio.Config().CreateFromFile(config_path) colorspace_data = { "roles": {}, @@ -173,14 +168,12 @@ def get_views(in_path, out_path): > pyton.exe ./ocio_wrapper.py config get_views \ --in_path= --out_path= """ - json_path = Path(out_path) - out_data = _get_views_data(in_path) - with open(json_path, "w") as f_: + with open(out_path, "w") as f_: json.dump(out_data, f_) - print("Viewer data are saved to '{}'".format(json_path)) + print("Viewer data are saved to '{}'".format(out_path)) def _get_views_data(config_path): @@ -195,12 +188,10 @@ def _get_views_data(config_path): Returns: dict: aggregated available viewers """ - config_path = Path(config_path) - - if not config_path.is_file(): + if not os.path.isfile(config_path): raise IOError("Input path should be `config.ocio` file") - config = ocio.Config().CreateFromFile(str(config_path)) + config = ocio.Config().CreateFromFile(config_path) data_ = {} for display in config.getDisplays(): @@ -247,14 +238,12 @@ def get_version(config_path, out_path): > pyton.exe ./ocio_wrapper.py config get_version \ --config_path= --out_path= """ - json_path = Path(out_path) - out_data = _get_version_data(config_path) - with open(json_path, "w") as f_: + with open(out_path, "w") as f_: json.dump(out_data, f_) - print("Config version data are saved to '{}'".format(json_path)) + print("Config version data are saved to '{}'".format(out_path)) def _get_version_data(config_path): @@ -269,12 +258,10 @@ def _get_version_data(config_path): Returns: dict: minor and major keys with values """ - config_path = Path(config_path) - - if not config_path.is_file(): + if not os.path.isfile(config_path): raise IOError("Input path should be `config.ocio` file") - config = ocio.Config().CreateFromFile(str(config_path)) + config = ocio.Config().CreateFromFile(config_path) return { "major": config.getMajorVersion(), @@ -317,15 +304,13 @@ def get_config_file_rules_colorspace_from_filepath( colorspace get_config_file_rules_colorspace_from_filepath \ --config_path= --filepath= --out_path= """ - json_path = Path(out_path) - colorspace = _get_config_file_rules_colorspace_from_filepath( config_path, filepath) - with open(json_path, "w") as f_: + with open(out_path, "w") as f_: json.dump(colorspace, f_) - print("Colorspace name is saved to '{}'".format(json_path)) + print("Colorspace name is saved to '{}'".format(out_path)) def _get_config_file_rules_colorspace_from_filepath(config_path, filepath): @@ -341,16 +326,14 @@ def _get_config_file_rules_colorspace_from_filepath(config_path, filepath): Returns: dict: aggregated available colorspaces """ - config_path = Path(config_path) - - if not config_path.is_file(): + if not os.path.isfile(config_path): raise IOError( "Input path `{}` should be `config.ocio` file".format(config_path)) - config = ocio.Config().CreateFromFile(str(config_path)) + config = ocio.Config().CreateFromFile(config_path) # TODO: use `parseColorSpaceFromString` instead if ocio v1 - colorspace = config.getColorSpaceFromFilepath(str(filepath)) + colorspace = config.getColorSpaceFromFilepath(filepath) return colorspace @@ -370,13 +353,10 @@ def _get_display_view_colorspace_name(config_path, display, view): Returns: view color space name (str) e.g. "Output - sRGB" """ - - config_path = Path(config_path) - - if not config_path.is_file(): + if not os.path.isfile(config_path): raise IOError("Input path should be `config.ocio` file") - config = ocio.Config.CreateFromFile(str(config_path)) + config = ocio.Config.CreateFromFile(config_path) colorspace = config.getDisplayViewColorSpaceName(display, view) return colorspace From 34359c217180c55e7bd56c53be6cfbf8f327aced Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Wed, 13 Mar 2024 17:04:05 +0100 Subject: [PATCH 105/163] improving compatibility check --- openpype/pipeline/colorspace.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/colorspace.py b/openpype/pipeline/colorspace.py index 2d1837e8ead..b42080ef061 100644 --- a/openpype/pipeline/colorspace.py +++ b/openpype/pipeline/colorspace.py @@ -463,13 +463,13 @@ def compatibility_check(): try: import PyOpenColorIO # noqa: F401 + # Requirement, introduced in newer ocio version config = PyOpenColorIO.GetCurrentConfig() - config.getDisplayViewColorSpaceName("sRGB", "sRGB") - CachedData.has_compatible_ocio_package = True + CachedData.has_compatible_ocio_package = ( + hasattr(config, "getDisplayViewColorSpaceName") + ) except ImportError: CachedData.has_compatible_ocio_package = False - except AttributeError: - CachedData.has_compatible_ocio_package = False # compatible return CachedData.has_compatible_ocio_package From a304d72fc1254f91b435a309298bbb86c63fe3ab Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 25 Mar 2024 10:56:03 +0000 Subject: [PATCH 106/163] Align Hiero and Nuke settings. --- .../defaults/project_settings/hiero.json | 18 ++++++------ .../defaults/project_settings/nuke.json | 13 ++++++--- .../projects_schema/schema_project_hiero.json | 28 +++++++++---------- .../schemas/schema_nuke_imageio.json | 27 +++++++++++++++++- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index efd80a8876d..ae09b34c988 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -10,15 +10,15 @@ "rules": {} }, "workfile": { - "ocioConfigName": "nuke-default", - "workingSpace": "linear", - "sixteenBitLut": "sRGB", - "eightBitLut": "sRGB", - "floatLut": "linear", - "logLut": "Cineon", - "viewerLut": "sRGB", - "thumbnailLut": "sRGB", - "monitorOutLut": "sRGB" + "ocioConfigName": "aces_1.2", + "workingSpace": "scene_linear (ACES - ACEScg)", + "viewerLut": "ACES/sRGB", + "thumbnailLut": "ACES/sRGB", + "monitorOutLut": "ACES/sRGB", + "eightBitLut": "matte_paint (Utility - sRGB - Texture)", + "sixteenBitLut": "texture_paint (ACES - ACEScc)", + "logLut": "compositing_log (Input - ADX - ADX10)", + "floatLut": "scene_linear (ACES - ACEScg)" }, "regexInputs": { "inputs": [ diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 15c2d262e0a..b41c1e49903 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -19,16 +19,21 @@ "rules": {} }, "viewer": { - "viewerProcess": "sRGB (default)" + "viewerProcess": "sRGB (ACES)" }, "baking": { "viewerProcess": "rec709 (default)" }, "workfile": { "colorManagement": "OCIO", - "OCIO_config": "nuke-default", - "workingSpaceLUT": "scene_linear", - "monitorLut": "sRGB (default)" + "OCIO_config": "aces_1.2", + "workingSpaceLUT": "scene_linear (ACES - ACEScg)", + "monitorLut": "sRGB (ACES)", + "monitorOutLUT": "sRGB (ACES)", + "int8Lut": "matte_paint (Utility - sRGB - Texture)", + "int16Lut": "texture_paint (ACES - ACEScc)", + "logLut": "compositing_log (Input - ADX - ADX10)", + "floatLut": "scene_linear (ACES - ACEScg)" }, "nodes": { "requiredNodes": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json index 73bd475815a..2511e229ef1 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json @@ -57,38 +57,38 @@ }, { "type": "text", - "key": "sixteenBitLut", - "label": "16 Bit Files" + "key": "viewerLut", + "label": "Viewer" }, { "type": "text", - "key": "eightBitLut", - "label": "8 Bit Files" + "key": "thumbnailLut", + "label": "Thumbnails" }, { "type": "text", - "key": "floatLut", - "label": "Floating Point Files" + "key": "monitorOutLut", + "label": "Monitor" }, { "type": "text", - "key": "logLut", - "label": "Log Files" + "key": "eightBitLut", + "label": "8 Bit Files" }, { "type": "text", - "key": "viewerLut", - "label": "Viewer" + "key": "sixteenBitLut", + "label": "16 Bit Files" }, { "type": "text", - "key": "thumbnailLut", - "label": "Thumbnails" + "key": "logLut", + "label": "Log Files" }, { "type": "text", - "key": "monitorOutLut", - "label": "Monitor" + "key": "floatLut", + "label": "Floating Point Files" } ] } diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json index af826fcf467..b925a98c897 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json @@ -105,7 +105,32 @@ { "type": "text", "key": "monitorLut", - "label": "monitor" + "label": "Thumbnails" + }, + { + "type": "text", + "key": "monitorOutLUT", + "label": "Monitor Out" + }, + { + "type": "text", + "key": "int8Lut", + "label": "8-bit files" + }, + { + "type": "text", + "key": "int16Lut", + "label": "16-bit files" + }, + { + "type": "text", + "key": "logLut", + "label": "log files" + }, + { + "type": "text", + "key": "floatLut", + "label": "float files" } ] } From 90c0122a7d9bf7a300a52b3ee06586b6f32c7797 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Mon, 25 Mar 2024 10:56:32 +0000 Subject: [PATCH 107/163] Apply colorspaces in memory --- openpype/hosts/hiero/api/events.py | 4 +- openpype/hosts/hiero/api/lib.py | 89 +++++++++++++++++++++++++----- 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/openpype/hosts/hiero/api/events.py b/openpype/hosts/hiero/api/events.py index 862a2607c1d..015f023083f 100644 --- a/openpype/hosts/hiero/api/events.py +++ b/openpype/hosts/hiero/api/events.py @@ -4,8 +4,8 @@ from .lib import ( sync_avalon_data_to_workfile, launch_workfiles_app, - selection_changed_timeline, before_project_save, + apply_colorspace_project ) from .tags import add_tags_to_workfile from .menu import update_menu_task_label @@ -42,6 +42,8 @@ def afterNewProjectCreated(event): # reset workfiles startup not to open any more in session os.environ["WORKFILES_STARTUP"] = "0" + apply_colorspace_project() + def beforeProjectLoad(event): log.info("before project load event...") diff --git a/openpype/hosts/hiero/api/lib.py b/openpype/hosts/hiero/api/lib.py index bf719160d11..af02889ed33 100644 --- a/openpype/hosts/hiero/api/lib.py +++ b/openpype/hosts/hiero/api/lib.py @@ -100,9 +100,9 @@ def flatten(list_): def get_current_project(remove_untitled=False): - projects = flatten(hiero.core.projects()) + projects = hiero.core.projects() if not remove_untitled: - return next(iter(projects)) + return projects[0] # if remove_untitled for proj in projects: @@ -243,8 +243,13 @@ def get_track_items( # collect all available active sequence track items if not return_list: sequence = get_current_sequence(name=sequence_name) - # get all available tracks from sequence - tracks = list(sequence.audioTracks()) + list(sequence.videoTracks()) + + tracks = [] + if sequence is not None: + # get all available tracks from sequence + tracks = list(sequence.audioTracks()) + tracks += list(sequence.videoTracks()) + # loop all tracks for track in tracks: if check_locked and track.isLocked(): @@ -1039,18 +1044,68 @@ def _set_hrox_project_knobs(doc, **knobs): def apply_colorspace_project(): - project_name = get_current_project_name() - # get path the the active projects - project = get_current_project(remove_untitled=True) - current_file = project.path() - - # close the active project - project.close() - + """Apply colorspaces from settings. + + Due to not being able to set the project settings through the Python API, + we need to do use some dubious code to find the widgets and set them. It is + possible to set the project settings without traversing through the widgets + but it involves reading the hrox files from disk with XML, so no in-memory + support. See https://community.foundry.com/discuss/topic/137771/change-a-project-s-default-color-transform-with-python # noqa + for more details. + """ # get presets for hiero + project_name = get_current_project_name() imageio = get_project_settings(project_name)["hiero"]["imageio"] presets = imageio.get("workfile") + # Open Project Settings UI. + for act in hiero.ui.registeredActions(): + if act.objectName() == "foundry.project.settings": + act.trigger() + + # Find widgets from their sibling label. + labels = { + "Working Space:": "workingSpace", + "Viewer:": "viewerLut", + "Thumbnails:": "thumbnailLut", + "Monitor Out:": "monitorOutLut", + "8 Bit Files:": "eightBitLut", + "16 Bit Files:": "sixteenBitLut", + "Log Files:": "logLut", + "Floating Point Files:": "floatLut" + } + widgets = {x: None for x in labels.values()} + + def _recursive_children(widget, labels, widgets): + children = widget.children() + for count, child in enumerate(children): + if isinstance(child, QtWidgets.QLabel): + if child.text() in labels.keys(): + widgets[labels[child.text()]] = children[count + 1] + _recursive_children(child, labels, widgets) + + app = QtWidgets.QApplication.instance() + title = "Project Settings" + for widget in app.topLevelWidgets(): + if isinstance(widget, QtWidgets.QMainWindow): + if widget.windowTitle() != title: + continue + _recursive_children(widget, labels, widgets) + widget.close() + + msg = "Setting value \"{}\" is not a valid option for \"{}\"" + for key, widget in widgets.items(): + options = [widget.itemText(i) for i in range(widget.count())] + setting_value = presets[key] + assert setting_value in options, msg.format(setting_value, key) + widget.setCurrentText(presets[key]) + + # This code block is for setting up project colorspaces for files on disk. + # Due to not having Python API access to set the project settings, the + # Foundry recommended way is to modify the hrox files on disk with XML. See + # this forum thread for more details; + # https://community.foundry.com/discuss/topic/137771/change-a-project-s-default-color-transform-with-python # noqa + ''' # backward compatibility layer # TODO: remove this after some time config_data = get_imageio_config( @@ -1063,6 +1118,13 @@ def apply_colorspace_project(): "ocioConfigName": "custom" }) + # get path the the active projects + project = get_current_project() + current_file = project.path() + + msg = "The project needs to be saved to disk to apply colorspace settings." + assert current_file, msg + # save the workfile as subversion "comment:_colorspaceChange" split_current_file = os.path.splitext(current_file) copy_current_file = current_file @@ -1105,6 +1167,7 @@ def apply_colorspace_project(): # open the file as current project hiero.core.openProject(copy_current_file) + ''' def apply_colorspace_clips(): @@ -1114,10 +1177,8 @@ def apply_colorspace_clips(): # get presets for hiero imageio = get_project_settings(project_name)["hiero"]["imageio"] - from pprint import pprint presets = imageio.get("regexInputs", {}).get("inputs", {}) - pprint(presets) for clip in clips: clip_media_source_path = clip.mediaSource().firstpath() clip_name = clip.name() From c25bbf2d3685fdf44065e929bcbe3cb67609b160 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Tue, 26 Mar 2024 13:06:17 +0000 Subject: [PATCH 108/163] [Automated] Release --- CHANGELOG.md | 220 ++++++++++++++++++++++++++++++++++++++++++++ openpype/version.py | 2 +- pyproject.toml | 2 +- 3 files changed, 222 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec3448570a..de9ac77bdd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,226 @@ # Changelog +## [3.18.8](https://github.com/ynput/OpenPype/tree/3.18.8) + + +[Full Changelog](https://github.com/ynput/OpenPype/compare/3.18.7...3.18.8) + +### **🚀 Enhancements** + + +
+Max: Implementation of Camera Attributes Validator #6110 + +Implement Validate Camera Attributes in camera family in Max host + + +___ + +
+ + +
+Max: Add missing workfile creator #6203 + +Add the missing workfile creator in 3dsMax. + + +___ + +
+ + +
+Deadline: Expose families transfer setting - OP-8268 #6217 + +This PR exposes the `families_transfer` attribute on the `ProcessSubmittedJobOnFarm` plugin.The use case is to remove `ftrack` from the list if a studio does not want all render passes from Maya to become asset versions in Ftrack. + + +___ + +
+ +### **🐛 Bug fixes** + + +
+Deadline: Add AVALON_DB to Deadline submissions - OP-8270 #6218 + +Because testing uses a different database name https://github.com/ynput/OpenPype/blob/develop/tests/lib/testing_classes.py#L46 we need to add `AVALON_DB` to the environment for Deadline submissions. + + +___ + +
+ + +
+Houdini: fix default render product name in Vray #6083 + +This is fixing key name for default render products in VRay. Original name `RGB Color` caused issues during job submission. + + +___ + +
+ + +
+Resolve Clip Load - Slate support #6126 + +Loaded clip should ignore the slate, and be trimmed the same regardless of slate presence.closes: https://github.com/ynput/OpenPype/issues/6124#AY-1684 + + +___ + +
+ + +
+Use duration from streams as its more precise #6171 + +When dealing with 30 fps mov of 2 frames, the duration was reduce to 3 decimal places (0.067) which meant that the flag for ffmpeg `-ss` ended up with a time that was not precise enough for ffmpeg to pick a frame; `0.0335`. Should be `0.0333`.Using the duration from the streams is more precise; `0.066667`. + + +___ + +
+ + +
+Core: Headless publish failing without GL lib #6205 + +Trying to run a headless publish in the farm I hit another blocker: +``` +2024-02-07 20:42:45: 0: STDOUT: !!! AYON crashed: +2024-02-07 20:42:45: 0: STDOUT: Traceback (most recent call last): +2024-02-07 20:42:45: 0: STDOUT: File "start.py", line 740, in main_cli +2024-02-07 20:42:45: 0: STDOUT: )) +2024-02-07 20:42:45: 0: STDOUT: File "/usr/ayon-launcher/1.0.0+ax/dependencies/click/core.py", line 1157, in __call__ +2024-02-07 20:42:45: 0: STDOUT: return self.main(*args, **kwargs) +2024-02-07 20:42:45: 0: STDOUT: File "/usr/ayon-launcher/1.0.0+ax/dependencies/click/core.py", line 1078, in main +2024-02-07 20:42:45: 0: STDOUT: rv = self.invoke(ctx) +2024-02-07 20:42:45: 0: STDOUT: File "/usr/ayon-launcher/1.0.0+ax/dependencies/click/core.py", line 1688, in invoke +2024-02-07 20:42:45: 0: STDOUT: return _process_result(sub_ctx.command.invoke(sub_ctx)) +2024-02-07 20:42:45: 0: STDOUT: File "/usr/ayon-launcher/1.0.0+ax/dependencies/click/core.py", line 1434, in invoke +2024-02-07 20:42:45: 0: STDOUT: return ctx.invoke(self.callback, **ctx.params) +2024-02-07 20:42:45: 0: STDOUT: File "/usr/ayon-launcher/1.0.0+ax/dependencies/click/core.py", line 783, in invoke +2024-02-07 20:42:45: 0: STDOUT: return __callback(*args, **kwargs) +2024-02-07 20:42:45: 0: STDOUT: File "/pipe/dev/farrizabalaga/OpenPype/openpype/cli.py", line 197, in publish +2024-02-07 20:42:45: 0: STDOUT: PypeCommands.publish(list(paths), targets, gui) +2024-02-07 20:42:45: 0: STDOUT: File "/pipe/dev/farrizabalaga/OpenPype/openpype/pype_commands.py", line 100, in publish +2024-02-07 20:42:45: 0: STDOUT: from openpype.tools.utils.host_tools import show_publish +2024-02-07 20:42:45: 0: STDOUT: File "/pipe/dev/farrizabalaga/OpenPype/openpype/tools/utils/__init__.py", line 1, in +2024-02-07 20:42:45: 0: STDOUT: from .layouts import FlowLayout +2024-02-07 20:42:45: 0: STDOUT: File "/pipe/dev/farrizabalaga/OpenPype/openpype/tools/utils/layouts.py", line 1, in +2024-02-07 20:42:45: 0: STDOUT: from qtpy import QtWidgets, QtCore +2024-02-07 20:42:45: 0: STDOUT: File "/usr/ayon-launcher/1.0.0+ax/dependencies/qtpy/QtWidgets.py", line 111, in +2024-02-07 20:42:45: 0: STDOUT: from PySide2.QtWidgets import * +2024-02-07 20:42:45: 0: STDOUT: File "/usr/ayon-launcher/1.0.0+ax/vendor/python/shiboken2/files.dir/shibokensupport/__feature__.py", line 142, in _import +2024-02-07 20:42:45: 0: STDOUT: return original_import(name, *args, **kwargs) +2024-02-07 20:42:45: 0: STDOUT: ImportError: libGL.so.1: cannot open shared object file: No such file or directory +``` +The imports of `openpype.tools.utils.host_tools.__init__.py` were throwing an error due to trying to import QtWidgets unnecessarily. + + +___ + +
+ + +
+Nuke: LoadClip colorspace override - OP-6591 #6215 + +Setting the colorspace from the representation data was not supported. + + +___ + +
+ + +
+Hiero: Add OP settings and convert in plugin - OP-8338 #6232 + +Missing settings for https://github.com/ynput/OpenPype/pull/6143. + + +___ + +
+ + +
+Unreal: Fix Render Instance Collector to use folderPath #6233 + +Fix Render Instance Collector to use folderPath instead of just the asset name. + + +___ + +
+ + +
+Bugfix - Fix "Action Failed" window not showing #6236 + +This PR targets to fix issue #6234. + + +___ + +
+ + +
+Nuke: render use existing frames with slate offsets the published render - AY-1433 #6239 + +Due to `frameStart` data member on representation for existing frames, the frame indexes would be re-numbered when integrating due to this; https://github.com/ynput/OpenPype/blob/develop/openpype/plugins/publish/integrate.py#L712-L726Removing `frameStart` had no effect on publishing workflows, local or farm.Also fixed an issues with slate collection which could misbehave if the instance node had "slate" in the name.Resolves #5883 + + +___ + +
+ + +
+AYON Workfiles tool: Copy and open of published workfile works #6241 + +Fix copy and open published workfiles. + + +___ + +
+ + +
+Chore: OCIO and python2 compatibility fixes #6242 + +Nuke 12 is now fully supported with our OCIO wrapping functionalities. + + +___ + +
+ +### **Merged pull requests** + + +
+Tests: Fix failing maya automatic test #6235 + +Improvement on https://github.com/ynput/OpenPype/pull/6231 + + +___ + +
+ + + + ## [3.18.7](https://github.com/ynput/OpenPype/tree/3.18.7) diff --git a/openpype/version.py b/openpype/version.py index 668d180d207..2485c3b17b9 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.8-nightly.2" +__version__ = "3.18.8" diff --git a/pyproject.toml b/pyproject.toml index eef6a2e978a..bbff2176bfc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "OpenPype" -version = "3.18.7" # OpenPype +version = "3.18.8" # OpenPype description = "Open VFX and Animation pipeline with support." authors = ["OpenPype Team "] license = "MIT License" From b94fbb06baedfca48f481d36bacf4b45a7b2fc16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 26 Mar 2024 13:07:12 +0000 Subject: [PATCH 109/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c01ab5122cf..3148852d0d5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,8 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.8 + - 3.18.8-nightly.2 - 3.18.8-nightly.1 - 3.18.7 - 3.18.7-nightly.5 @@ -133,8 +135,6 @@ body: - 3.15.11-nightly.5 - 3.15.11-nightly.4 - 3.15.11-nightly.3 - - 3.15.11-nightly.2 - - 3.15.11-nightly.1 validations: required: true - type: dropdown From deca92cdcc8f7775c312fab1f6bb91ec39c64efb Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 27 Mar 2024 03:25:23 +0000 Subject: [PATCH 110/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 2485c3b17b9..d72dcf2f57b 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.8" +__version__ = "3.18.9-nightly.1" From b2ce62fca7c400d1b3186f161945b98fc169a63b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Mar 2024 03:25:59 +0000 Subject: [PATCH 111/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 3148852d0d5..6881b258156 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.1 - 3.18.8 - 3.18.8-nightly.2 - 3.18.8-nightly.1 @@ -134,7 +135,6 @@ body: - 3.15.11 - 3.15.11-nightly.5 - 3.15.11-nightly.4 - - 3.15.11-nightly.3 validations: required: true - type: dropdown From 3def9fe29e19691ca8f7d1514ca74677c8edd00a Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 28 Mar 2024 10:47:41 +0000 Subject: [PATCH 112/163] Validate alembic options defaults --- .../create/create_animation_pointcache.py | 4 +- .../plugins/publish/extract_pointcache.py | 17 ++- .../validate_alembic_options_defaults.py | 106 ++++++++++++++++++ 3 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py diff --git a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py index 791aa0072b2..e44b0c7b27d 100644 --- a/openpype/hosts/maya/plugins/create/create_animation_pointcache.py +++ b/openpype/hosts/maya/plugins/create/create_animation_pointcache.py @@ -54,14 +54,14 @@ def extract_alembic_attributes(node_data, class_name): "visibleOnly" ] attributes = extract_alembic_flags + extract_alembic_attributes - plugin_attributes = {"flag_overrides": []} + plugin_attributes = {"flags": []} for attr in attributes: if attr not in node_data["creator_attributes"].keys(): continue value = node_data["creator_attributes"].pop(attr) if value and attr in extract_alembic_flags: - plugin_attributes["flag_overrides"].append(attr) + plugin_attributes["flags"].append(attr) if attr in extract_alembic_attributes: plugin_attributes[attr] = value diff --git a/openpype/hosts/maya/plugins/publish/extract_pointcache.py b/openpype/hosts/maya/plugins/publish/extract_pointcache.py index a951f9b8cab..79e07cf7322 100644 --- a/openpype/hosts/maya/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/maya/plugins/publish/extract_pointcache.py @@ -46,7 +46,7 @@ class ExtractAlembic(publish.Extractor, OpenPypePyblishPluginMixin): userAttr = "" userAttrPrefix = "" visibleOnly = False - export_overrides = [] + overrides = [] def process(self, instance): if instance.data.get("farm"): @@ -122,11 +122,10 @@ def process(self, instance): "writeVisibility": False, } - # Export flags are defined as default enabled flags excluding flags - # that are exposed to the user, plus the flags the user has enabled - # when publishing. - flags = list(set(self.flags) - set(self.export_overrides)) - flags += attribute_values["flag_overrides"] + # Export flags are defined as default enabled flags plus publisher + # enabled flags. + non_exposed_flags = list(set(self.flags) - set(self.overrides)) + flags = attribute_values["flags"] + non_exposed_flags for flag in flags: args[flag] = True @@ -291,11 +290,11 @@ def get_attribute_defs(cls): flags = set(getattr(cls, "flags", set())) enabled_flags = [x for x in flags if x in overrides] - flag_overrides = overrides - set(override_defs.keys()) + flags = overrides - set(override_defs.keys()) defs.append( EnumDef( - "flag_overrides", - flag_overrides, + "flags", + flags, default=enabled_flags, multiselection=True, label="Export Flags", diff --git a/openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py new file mode 100644 index 00000000000..f0bcf3de1f0 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -0,0 +1,106 @@ +import pyblish.api + +from openpype.pipeline import OptionalPyblishPluginMixin +from openpype.pipeline.publish import RepairAction, PublishValidationError + + +class ValidateAlembicOptionsDefaults( + pyblish.api.InstancePlugin, OptionalPyblishPluginMixin +): + """Validate the attributes on the instance are defaults.""" + + order = pyblish.api.ValidatorOrder + families = ["pointcache", "animation"] + hosts = ["maya"] + label = "Validate Alembic Options Defaults" + actions = [RepairAction] + optional = True + + @classmethod + def _get_plugin_name(self, publish_attributes): + for key in ["ExtractAnimation", "ExtractAlembic"]: + if key in publish_attributes.keys(): + return key + + @classmethod + def _get_settings(self, context): + maya_settings = context.data["project_settings"]["maya"] + settings = maya_settings["publish"]["ExtractAlembic"] + # Flags are a special case since they are a combination of overrides + # and default flags from the settings. + settings["flags"] = [ + x for x in settings["flags"] if x in settings["overrides"] + ] + return settings + + @classmethod + def _get_publish_attributes(self, instance): + attributes = instance.data["publish_attributes"][ + self._get_plugin_name( + instance.data["publish_attributes"] + ) + ] + + settings = self._get_settings(instance.context) + + # Flags are a special case since they are a combination of exposed + # flags and default flags from the settings. So we need to add the + # default flags from the settings and ensure unique items. + non_exposed_flags = [ + x for x in settings["flags"] if x not in settings["overrides"] + ] + attributes["flags"] = attributes["flags"] + non_exposed_flags + + return attributes + + def process(self, instance): + if not self.is_active(instance.data): + return + + settings = self._get_settings(instance.context) + + attributes = self._get_publish_attributes(instance) + + msg = ( + "Alembic Extract setting \"{}\" is not the default value:" + "\nCurrent: {}" + "\nDefault Value: {}\n" + ) + errors = [] + for key, value in attributes.items(): + default_value = settings[key] + + # Lists are best to compared sorted since we cant rely on the order + # of the items. + if isinstance(value, list): + value = sorted(value) + default_value = sorted(default_value) + + if value != default_value: + errors.append(msg.format(key, value, default_value)) + + if errors: + raise PublishValidationError("\n".join(errors)) + + @classmethod + def repair(cls, instance): + # Find create instance twin. + create_context = instance.context.data["create_context"] + create_instance = None + for Instance in create_context.instances: + if Instance.data["instance_id"] == instance.data["instance_id"]: + create_instance = Instance + break + + assert create_instance is not None + + # Set the settings values on the create context then save to workfile. + publish_attributes = instance.data["publish_attributes"] + plugin_name = cls._get_plugin_name(publish_attributes) + attributes = cls._get_publish_attributes(instance) + settings = cls._get_settings(instance.context) + create_publish_attributes = create_instance.data["publish_attributes"] + for key, value in attributes.items(): + create_publish_attributes[plugin_name][key] = settings[key] + + create_context.save_changes() From b645c62ab2f59ac6ed7a92f2113564ab4c28ec33 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Thu, 28 Mar 2024 10:52:27 +0000 Subject: [PATCH 113/163] Hound --- .../maya/plugins/publish/validate_alembic_options_defaults.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py b/openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py index f0bcf3de1f0..e16196a6d38 100644 --- a/openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py +++ b/openpype/hosts/maya/plugins/publish/validate_alembic_options_defaults.py @@ -100,7 +100,7 @@ def repair(cls, instance): attributes = cls._get_publish_attributes(instance) settings = cls._get_settings(instance.context) create_publish_attributes = create_instance.data["publish_attributes"] - for key, value in attributes.items(): + for key in attributes.keys(): create_publish_attributes[plugin_name][key] = settings[key] create_context.save_changes() From 97e6d7f7d64ce1a155b1a73943167b91445f365b Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 30 Mar 2024 03:25:58 +0000 Subject: [PATCH 114/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index d72dcf2f57b..a2c6f9747ae 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.1" +__version__ = "3.18.9-nightly.2" From 77bccf69dfebea27fb9b9b0e0c67b4548753c62b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 30 Mar 2024 03:26:32 +0000 Subject: [PATCH 115/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 6881b258156..c123692b8ae 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.2 - 3.18.9-nightly.1 - 3.18.8 - 3.18.8-nightly.2 @@ -134,7 +135,6 @@ body: - 3.15.12-nightly.1 - 3.15.11 - 3.15.11-nightly.5 - - 3.15.11-nightly.4 validations: required: true - type: dropdown From 9d248cf1c829939da8f4e3c7075c2d2e69cb9cda Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 3 Apr 2024 03:25:24 +0000 Subject: [PATCH 116/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index a2c6f9747ae..1511fe14f1a 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.2" +__version__ = "3.18.9-nightly.3" From 583fa85301e5d2e58880d7e238e3db7a13a3657d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Apr 2024 03:26:07 +0000 Subject: [PATCH 117/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index c123692b8ae..11b1f89bae7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.3 - 3.18.9-nightly.2 - 3.18.9-nightly.1 - 3.18.8 @@ -134,7 +135,6 @@ body: - 3.15.12-nightly.2 - 3.15.12-nightly.1 - 3.15.11 - - 3.15.11-nightly.5 validations: required: true - type: dropdown From a59faf0dfacf1c99f39c6cfbef64c44ceddb771a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 3 Apr 2024 18:27:26 +0800 Subject: [PATCH 118/163] ported https://github.com/ynput/ayon-core/pull/201 to OP --- .../publish/validate_animated_reference.py | 9 +++++++-- .../plugins/publish/validate_animation_content.py | 11 ++++++++--- ...validate_animation_out_set_related_node_ids.py | 10 +++++++--- .../publish/validate_ass_relative_paths.py | 9 +++++++-- .../plugins/publish/validate_assembly_name.py | 10 +++++++--- .../publish/validate_assembly_namespaces.py | 10 +++++++--- .../publish/validate_assembly_transforms.py | 13 +++++++++++-- .../plugins/publish/validate_camera_attributes.py | 12 +++++++++--- .../plugins/publish/validate_camera_contents.py | 11 ++++++++--- .../validate_current_renderlayer_renderable.py | 13 +++++++++---- .../plugins/publish/validate_glsl_material.py | 7 +++++-- .../maya/plugins/publish/validate_glsl_plugin.py | 9 +++++++-- .../plugins/publish/validate_instancer_content.py | 12 +++++++++--- .../publish/validate_instancer_frame_ranges.py | 13 +++++++++---- .../plugins/publish/validate_loaded_plugin.py | 10 +++++++--- .../publish/validate_look_shading_group.py | 10 +++++++--- .../maya/plugins/publish/validate_maya_units.py | 10 +++++++--- .../plugins/publish/validate_mesh_lamina_faces.py | 8 ++++++-- .../maya/plugins/publish/validate_mesh_ngons.py | 11 +++++++++-- .../publish/validate_mesh_no_negative_scale.py | 10 +++++++--- .../plugins/publish/validate_mesh_non_manifold.py | 10 +++++++--- .../publish/validate_mesh_shader_connections.py | 10 +++++++--- .../publish/validate_mesh_vertices_have_edges.py | 13 ++++++++++--- .../plugins/publish/validate_model_content.py | 12 ++++++++---- .../plugins/publish/validate_no_default_camera.py | 9 +++++++-- .../maya/plugins/publish/validate_no_namespace.py | 9 +++++++-- .../publish/validate_no_null_transforms.py | 9 +++++++-- .../maya/plugins/publish/validate_no_vraymesh.py | 13 +++++++++---- .../plugins/publish/validate_node_no_ghosting.py | 14 +++++++++----- .../publish/validate_plugin_path_attributes.py | 9 +++++++-- .../plugins/publish/validate_render_image_rule.py | 13 ++++++++++--- .../publish/validate_render_no_default_cameras.py | 7 ++++++- .../publish/validate_render_single_camera.py | 9 +++++++-- .../plugins/publish/validate_renderlayer_aovs.py | 15 +++++++++++---- .../maya/plugins/publish/validate_rig_contents.py | 10 ++++++++-- .../plugins/publish/validate_rig_controllers.py | 10 ++++++++-- .../validate_rig_controllers_arnold_attributes.py | 10 ++++++++-- .../plugins/publish/validate_rig_joints_hidden.py | 9 +++++++-- .../publish/validate_rig_out_set_node_ids.py | 11 ++++++++--- .../publish/validate_shape_render_stats.py | 8 ++++++-- .../maya/plugins/publish/validate_shape_zero.py | 9 +++++++-- .../publish/validate_skeletalmesh_hierarchy.py | 7 ++++++- .../publish/validate_skinCluster_deformer_set.py | 8 ++++++-- .../maya/plugins/publish/validate_step_size.py | 11 +++++++---- .../plugins/publish/validate_transform_zero.py | 10 +++++++--- .../maya/plugins/publish/validate_unique_names.py | 13 +++++++++---- .../publish/validate_unreal_mesh_triangulated.py | 10 ++++++++-- .../maya/plugins/publish/validate_visible_only.py | 10 +++++++--- .../validate_vray_distributed_rendering.py | 13 ++++++++++--- .../publish/validate_vray_referenced_aovs.py | 12 +++++++++--- .../publish/validate_vray_translator_settings.py | 9 +++++++-- .../maya/plugins/publish/validate_vrayproxy.py | 9 ++++++--- .../plugins/publish/validate_vrayproxy_members.py | 10 +++++++--- .../validate_yeti_renderscript_callbacks.py | 13 +++++++++---- .../publish/validate_yeti_rig_cache_state.py | 9 +++++++-- .../validate_yeti_rig_input_in_instance.py | 10 +++++++--- .../plugins/publish/validate_yeti_rig_settings.py | 12 +++++++++--- 57 files changed, 438 insertions(+), 155 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_animated_reference.py b/openpype/hosts/maya/plugins/publish/validate_animated_reference.py index 4537892d6d7..dd7b361780d 100644 --- a/openpype/hosts/maya/plugins/publish/validate_animated_reference.py +++ b/openpype/hosts/maya/plugins/publish/validate_animated_reference.py @@ -2,12 +2,14 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( PublishValidationError, - ValidateContentsOrder + ValidateContentsOrder, + OptionalPyblishPluginMixin ) from maya import cmds -class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin): +class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate all nodes in skeletonAnim_SET are referenced""" order = ValidateContentsOrder @@ -16,8 +18,11 @@ class ValidateAnimatedReferenceRig(pyblish.api.InstancePlugin): label = "Animated Reference Rig" accepted_controllers = ["transform", "locator"] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): + if not self.is_active(instance.data): + return animated_sets = instance.data.get("animated_skeleton", []) if not animated_sets: self.log.debug( diff --git a/openpype/hosts/maya/plugins/publish/validate_animation_content.py b/openpype/hosts/maya/plugins/publish/validate_animation_content.py index 99acdc7b8f1..69c43d6bcc0 100644 --- a/openpype/hosts/maya/plugins/publish/validate_animation_content.py +++ b/openpype/hosts/maya/plugins/publish/validate_animation_content.py @@ -2,12 +2,14 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( PublishValidationError, - ValidateContentsOrder + ValidateContentsOrder, + OptionalPyblishPluginMixin ) -class ValidateAnimationContent(pyblish.api.InstancePlugin): - """Adheres to the content of 'animation' family +class ValidateAnimationContent(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Adheres to the content of 'animation' product type - Must have collected `out_hierarchy` data. - All nodes in `out_hierarchy` must be in the instance. @@ -19,6 +21,7 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin): families = ["animation"] label = "Animation Content" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False @classmethod def get_invalid(cls, instance): @@ -48,6 +51,8 @@ def get_invalid(cls, instance): return invalid def process(self, instance): + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py index 6f5f03ab396..e30a58ca998 100644 --- a/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_animation_out_set_related_node_ids.py @@ -6,11 +6,13 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin): +class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate if deformed shapes have related IDs to the original shapes When a deformer is applied in the scene on a referenced mesh that already @@ -28,10 +30,12 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin): openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction ] + optional = False def process(self, instance): """Process all meshes""" - + if not self.is_active(instance.data): + return # Ensure all nodes have a cbId and a related ID to the original shapes # if a deformer has been created on the shape invalid = self.get_invalid(instance) diff --git a/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py b/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py index 49913fa42b5..ce0610dc204 100644 --- a/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py +++ b/openpype/hosts/maya/plugins/publish/validate_ass_relative_paths.py @@ -8,11 +8,13 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateAssRelativePaths(pyblish.api.InstancePlugin): +class ValidateAssRelativePaths(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure exporting ass file has set relative texture paths""" order = ValidateContentsOrder @@ -20,8 +22,11 @@ class ValidateAssRelativePaths(pyblish.api.InstancePlugin): families = ['ass'] label = "ASS has relative texture paths" actions = [RepairAction] + optional = False def process(self, instance): + if not self.is_active(instance.data): + return # we cannot ask this until user open render settings as # `defaultArnoldRenderOptions` doesn't exist errors = [] diff --git a/openpype/hosts/maya/plugins/publish/validate_assembly_name.py b/openpype/hosts/maya/plugins/publish/validate_assembly_name.py index 00588cd3006..636b9004e8f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_assembly_name.py +++ b/openpype/hosts/maya/plugins/publish/validate_assembly_name.py @@ -2,11 +2,13 @@ import maya.cmds as cmds import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateAssemblyName(pyblish.api.InstancePlugin): +class ValidateAssemblyName(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """ Ensure Assembly name ends with `GRP` Check if assembly name ends with `_GRP` string. @@ -17,6 +19,7 @@ class ValidateAssemblyName(pyblish.api.InstancePlugin): families = ["assembly"] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] active = False + optional = True @classmethod def get_invalid(cls, instance): @@ -47,7 +50,8 @@ def get_invalid(cls, instance): return invalid def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError("Found {} invalid named assembly " diff --git a/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py b/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py index 06577f38f7e..a04aa6edfe8 100644 --- a/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py +++ b/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py @@ -1,10 +1,12 @@ import pyblish.api import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateAssemblyNamespaces(pyblish.api.InstancePlugin): +class ValidateAssemblyNamespaces(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure namespaces are not nested In the outliner an item in a normal namespace looks as following: @@ -20,9 +22,11 @@ class ValidateAssemblyNamespaces(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder families = ["assembly"] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return self.log.debug("Checking namespace for %s" % instance.name) if self.get_invalid(instance): raise PublishValidationError("Nested namespaces found") diff --git a/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py b/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py index a24455ebaac..de881e2a84e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py +++ b/openpype/hosts/maya/plugins/publish/validate_assembly_transforms.py @@ -2,10 +2,15 @@ from maya import cmds import openpype.hosts.maya.api.action -from openpype.pipeline.publish import PublishValidationError, RepairAction +from openpype.pipeline.publish import ( + PublishValidationError, + RepairAction, + OptionalPyblishPluginMixin +) -class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin): +class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Verify only root nodes of the loaded asset have transformations. Note: This check is temporary and is subject to change. @@ -34,7 +39,11 @@ class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin): " This can alter the look of your scene. " "Are you sure you want to continue?") + optional = False + def process(self, instance): + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_camera_attributes.py b/openpype/hosts/maya/plugins/publish/validate_camera_attributes.py index e5745612e99..3aca7c01904 100644 --- a/openpype/hosts/maya/plugins/publish/validate_camera_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_camera_attributes.py @@ -3,10 +3,14 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( - PublishValidationError, ValidateContentsOrder) + PublishValidationError, + ValidateContentsOrder, + OptionalPyblishPluginMixin +) -class ValidateCameraAttributes(pyblish.api.InstancePlugin): +class ValidateCameraAttributes(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates Camera has no invalid attribute keys or values. The Alembic file format does not a specific subset of attributes as such @@ -20,6 +24,7 @@ class ValidateCameraAttributes(pyblish.api.InstancePlugin): hosts = ['maya'] label = 'Camera Attributes' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = True DEFAULTS = [ ("filmFitOffset", 0.0), @@ -62,7 +67,8 @@ def get_invalid(cls, instance): def process(self, instance): """Process all the nodes in the instance""" - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_camera_contents.py b/openpype/hosts/maya/plugins/publish/validate_camera_contents.py index 767ac55718f..689189371ad 100644 --- a/openpype/hosts/maya/plugins/publish/validate_camera_contents.py +++ b/openpype/hosts/maya/plugins/publish/validate_camera_contents.py @@ -3,10 +3,13 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( - PublishValidationError, ValidateContentsOrder) + PublishValidationError, + ValidateContentsOrder, + OptionalPyblishPluginMixin) -class ValidateCameraContents(pyblish.api.InstancePlugin): +class ValidateCameraContents(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates Camera instance contents. A Camera instance may only hold a SINGLE camera's transform, nothing else. @@ -22,6 +25,7 @@ class ValidateCameraContents(pyblish.api.InstancePlugin): label = 'Camera Contents' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] validate_shapes = True + optional = False @classmethod def get_invalid(cls, instance): @@ -71,7 +75,8 @@ def get_invalid(cls, instance): def process(self, instance): """Process all the nodes in the instance""" - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError("Invalid camera contents: " diff --git a/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py b/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py index f072e5e3234..422d3bb8d46 100644 --- a/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py +++ b/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py @@ -1,10 +1,13 @@ import pyblish.api from maya import cmds -from openpype.pipeline.publish import context_plugin_should_run +from openpype.pipeline.publish import ( + context_plugin_should_run, + OptionalPyblishPluginMixin +) - -class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin): +class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin, + OptionalPyblishPluginMixin): """Validate if current render layer has a renderable camera There is a bug in Redshift which occurs when the current render layer @@ -20,9 +23,11 @@ class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin): order = pyblish.api.ValidatorOrder hosts = ["maya"] families = ["renderlayer"] + optional = False def process(self, context): - + if not self.is_active(context.data): + return # Workaround bug pyblish-base#250 if not context_plugin_should_run(self, context): return diff --git a/openpype/hosts/maya/plugins/publish/validate_glsl_material.py b/openpype/hosts/maya/plugins/publish/validate_glsl_material.py index 3b386c3def1..5d43718e478 100644 --- a/openpype/hosts/maya/plugins/publish/validate_glsl_material.py +++ b/openpype/hosts/maya/plugins/publish/validate_glsl_material.py @@ -6,10 +6,11 @@ RepairAction, ValidateContentsOrder ) -from openpype.pipeline import PublishValidationError +from openpype.pipeline import PublishValidationError, OptionalPyblishPluginMixin -class ValidateGLSLMaterial(pyblish.api.InstancePlugin): +class ValidateGLSLMaterial(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """ Validate if the asset uses GLSL Shader """ @@ -23,6 +24,8 @@ class ValidateGLSLMaterial(pyblish.api.InstancePlugin): active = True def process(self, instance): + if not self.is_active(instance.data): + return shading_grp = self.get_material_from_shapes(instance) if not shading_grp: raise PublishValidationError("No shading group found") diff --git a/openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py b/openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py index da065fcf946..61201afa21b 100644 --- a/openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py +++ b/openpype/hosts/maya/plugins/publish/validate_glsl_plugin.py @@ -5,11 +5,13 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateGLSLPlugin(pyblish.api.InstancePlugin): +class ValidateGLSLPlugin(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """ Validate if the asset uses GLSL Shader """ @@ -19,8 +21,11 @@ class ValidateGLSLPlugin(pyblish.api.InstancePlugin): hosts = ['maya'] label = 'maya2glTF plugin' actions = [RepairAction] + optional = False def process(self, instance): + if not self.is_active(instance.data): + return if not cmds.pluginInfo("maya2glTF", query=True, loaded=True): raise PublishValidationError("maya2glTF is not loaded") diff --git a/openpype/hosts/maya/plugins/publish/validate_instancer_content.py b/openpype/hosts/maya/plugins/publish/validate_instancer_content.py index 236adfb03d7..92bbd1ef78a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_instancer_content.py +++ b/openpype/hosts/maya/plugins/publish/validate_instancer_content.py @@ -2,10 +2,14 @@ import pyblish.api from openpype.hosts.maya.api import lib -from openpype.pipeline.publish import PublishValidationError +from openpype.pipeline.publish import ( + PublishValidationError, + OptionalPyblishPluginMixin +) -class ValidateInstancerContent(pyblish.api.InstancePlugin): +class ValidateInstancerContent(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates that all meshes in the instance have object IDs. This skips a check on intermediate objects because we consider them @@ -14,9 +18,11 @@ class ValidateInstancerContent(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder label = 'Instancer Content' families = ['instancer'] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return error = False members = instance.data['setMembers'] export_members = instance.data['exactExportMembers'] diff --git a/openpype/hosts/maya/plugins/publish/validate_instancer_frame_ranges.py b/openpype/hosts/maya/plugins/publish/validate_instancer_frame_ranges.py index 714c6229d6e..05917b5b3bd 100644 --- a/openpype/hosts/maya/plugins/publish/validate_instancer_frame_ranges.py +++ b/openpype/hosts/maya/plugins/publish/validate_instancer_frame_ranges.py @@ -3,8 +3,10 @@ import pyblish.api -from openpype.pipeline.publish import PublishValidationError - +from openpype.pipeline.publish import ( + PublishValidationError, + OptionalPyblishPluginMixin +) def is_cache_resource(resource): """Return whether resource is a cacheFile resource""" @@ -34,7 +36,8 @@ def filter_ticks(files): return tick_files, ticks -class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin): +class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates all instancer particle systems are cached correctly. This means they should have the files/frames as required by the start-end @@ -46,6 +49,7 @@ class ValidateInstancerFrameRanges(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder label = 'Instancer Cache Frame Ranges' families = ['instancer'] + optional = False @classmethod def get_invalid(cls, instance): @@ -157,7 +161,8 @@ def get_invalid(cls, instance): return invalid def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_loaded_plugin.py b/openpype/hosts/maya/plugins/publish/validate_loaded_plugin.py index eac13053db4..2a790c56630 100644 --- a/openpype/hosts/maya/plugins/publish/validate_loaded_plugin.py +++ b/openpype/hosts/maya/plugins/publish/validate_loaded_plugin.py @@ -4,17 +4,20 @@ from openpype.pipeline.publish import ( RepairContextAction, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateLoadedPlugin(pyblish.api.ContextPlugin): +class ValidateLoadedPlugin(pyblish.api.ContextPlugin, + OptionalPyblishPluginMixin): """Ensure there are no unauthorized loaded plugins""" label = "Loaded Plugin" order = pyblish.api.ValidatorOrder host = ["maya"] actions = [RepairContextAction] + optional = True @classmethod def get_invalid(cls, context): @@ -35,7 +38,8 @@ def get_invalid(cls, context): return invalid def process(self, context): - + if not self.is_active(context.data): + return invalid = self.get_invalid(context) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_look_shading_group.py b/openpype/hosts/maya/plugins/publish/validate_look_shading_group.py index dbe7a70e6a6..e2c241f9a0b 100644 --- a/openpype/hosts/maya/plugins/publish/validate_look_shading_group.py +++ b/openpype/hosts/maya/plugins/publish/validate_look_shading_group.py @@ -5,16 +5,17 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateShadingEngine(pyblish.api.InstancePlugin): +class ValidateShadingEngine(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate all shading engines are named after the surface material. Shading engines should be named "{surface_shader}SG" """ - order = ValidateContentsOrder families = ["look"] hosts = ["maya"] @@ -22,9 +23,12 @@ class ValidateShadingEngine(pyblish.api.InstancePlugin): actions = [ openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction ] + optional = True # The default connections to check def process(self, instance): + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_maya_units.py b/openpype/hosts/maya/plugins/publish/validate_maya_units.py index ae6dc093a91..553fd06f8e7 100644 --- a/openpype/hosts/maya/plugins/publish/validate_maya_units.py +++ b/openpype/hosts/maya/plugins/publish/validate_maya_units.py @@ -7,11 +7,13 @@ from openpype.pipeline.publish import ( RepairContextAction, ValidateSceneOrder, - PublishXmlValidationError + PublishXmlValidationError, + OptionalPyblishPluginMixin ) -class ValidateMayaUnits(pyblish.api.ContextPlugin): +class ValidateMayaUnits(pyblish.api.ContextPlugin, + OptionalPyblishPluginMixin): """Check if the Maya units are set correct""" order = ValidateSceneOrder @@ -35,6 +37,7 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin): "Maya scene {setting} must be '{required_value}'. " "Current value is '{current_value}'." ) + optional = False @classmethod def apply_settings(cls, project_settings): @@ -52,7 +55,8 @@ def apply_settings(cls, project_settings): cls.validate_fps = settings.get("validate_fps", cls.validate_fps) def process(self, context): - + if not self.is_active(context.data): + return # Collected units linearunits = context.data.get('linearUnits') angularunits = context.data.get('angularUnits') diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py index f120361583c..ea7e3fe9aaa 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py @@ -2,10 +2,11 @@ import pyblish.api import openpype.hosts.maya.api.action -from openpype.pipeline.publish import ValidateMeshOrder +from openpype.pipeline.publish import ValidateMeshOrder, OptionalPyblishPluginMixin -class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin): +class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate meshes don't have lamina faces. Lamina faces share all of their edges. @@ -17,6 +18,7 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin): families = ['model'] label = 'Mesh Lamina Faces' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = True @staticmethod def get_invalid(instance): @@ -28,6 +30,8 @@ def get_invalid(instance): def process(self, instance): """Process all the nodes in the instance 'objectSet'""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py b/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py index 5b67db33070..0e70360442c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_ngons.py @@ -3,10 +3,14 @@ import pyblish.api import openpype.hosts.maya.api.action from openpype.hosts.maya.api import lib -from openpype.pipeline.publish import ValidateContentsOrder +from openpype.pipeline.publish import ( + ValidateContentsOrder, + OptionalPyblishPluginMixin +) -class ValidateMeshNgons(pyblish.api.Validator): +class ValidateMeshNgons(pyblish.api.Validator, + OptionalPyblishPluginMixin): """Ensure that meshes don't have ngons Ngon are faces with more than 4 sides. @@ -21,6 +25,7 @@ class ValidateMeshNgons(pyblish.api.Validator): families = ["model"] label = "Mesh ngons" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = True @staticmethod def get_invalid(instance): @@ -39,6 +44,8 @@ def get_invalid(instance): def process(self, instance): """Process all the nodes in the instance "objectSet""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_no_negative_scale.py b/openpype/hosts/maya/plugins/publish/validate_mesh_no_negative_scale.py index 48b4d0f557e..048d757000c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_no_negative_scale.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_no_negative_scale.py @@ -4,7 +4,8 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( ValidateMeshOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) @@ -15,7 +16,8 @@ def _as_report_list(values, prefix="- ", suffix="\n"): return prefix + (suffix + prefix).join(values) -class ValidateMeshNoNegativeScale(pyblish.api.Validator): +class ValidateMeshNoNegativeScale(pyblish.api.Validator, + OptionalPyblishPluginMixin): """Ensure that meshes don't have a negative scale. Using negatively scaled proxies in a VRayMesh results in inverted @@ -32,6 +34,7 @@ class ValidateMeshNoNegativeScale(pyblish.api.Validator): families = ['model'] label = 'Mesh No Negative Scale' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False @staticmethod def get_invalid(instance): @@ -52,7 +55,8 @@ def get_invalid(instance): def process(self, instance): """Process all the nodes in the instance 'objectSet'""" - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_non_manifold.py b/openpype/hosts/maya/plugins/publish/validate_mesh_non_manifold.py index 6fd63fb29f2..d9a21dddbff 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_non_manifold.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_non_manifold.py @@ -4,7 +4,8 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( ValidateMeshOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) @@ -15,7 +16,8 @@ def _as_report_list(values, prefix="- ", suffix="\n"): return prefix + (suffix + prefix).join(values) -class ValidateMeshNonManifold(pyblish.api.Validator): +class ValidateMeshNonManifold(pyblish.api.Validator, + OptionalPyblishPluginMixin): """Ensure that meshes don't have non-manifold edges or vertices To debug the problem on the meshes you can use Maya's modeling @@ -28,6 +30,7 @@ class ValidateMeshNonManifold(pyblish.api.Validator): families = ['model'] label = 'Mesh Non-Manifold Edges/Vertices' actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = True @staticmethod def get_invalid(instance): @@ -44,7 +47,8 @@ def get_invalid(instance): def process(self, instance): """Process all the nodes in the instance 'objectSet'""" - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_shader_connections.py b/openpype/hosts/maya/plugins/publish/validate_mesh_shader_connections.py index 1db7613999b..9c57bc8d101 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_shader_connections.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_shader_connections.py @@ -5,7 +5,8 @@ from openpype.pipeline.publish import ( RepairAction, ValidateMeshOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) @@ -79,7 +80,8 @@ def disconnect(node_a, node_b): cmds.disconnectAttr(source, input) -class ValidateMeshShaderConnections(pyblish.api.InstancePlugin): +class ValidateMeshShaderConnections(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure mesh shading engine connections are valid. In some scenarios Maya keeps connections to multiple shaders even if just @@ -96,10 +98,12 @@ class ValidateMeshShaderConnections(pyblish.api.InstancePlugin): label = "Mesh Shader Connections" actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] + optional = True def process(self, instance): """Process all the nodes in the instance 'objectSet'""" - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py index 71678594443..9275bb31234 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_vertices_have_edges.py @@ -4,10 +4,15 @@ import openpype.hosts.maya.api.action from openpype.hosts.maya.api.lib import len_flattened from openpype.pipeline.publish import ( - PublishValidationError, RepairAction, ValidateMeshOrder) + PublishValidationError, + RepairAction, + ValidateMeshOrder, + OptionalPyblishPluginMixin +) -class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin): +class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate meshes have only vertices that are connected to edges. Maya can have invalid geometry with vertices that have no edges or @@ -32,6 +37,7 @@ class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin): label = 'Mesh Vertices Have Edges' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] + optional = True @classmethod def repair(cls, instance): @@ -72,7 +78,8 @@ def get_invalid(cls, instance): return invalid def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_model_content.py b/openpype/hosts/maya/plugins/publish/validate_model_content.py index 19373efad92..343133f738d 100644 --- a/openpype/hosts/maya/plugins/publish/validate_model_content.py +++ b/openpype/hosts/maya/plugins/publish/validate_model_content.py @@ -5,12 +5,14 @@ from openpype.hosts.maya.api import lib from openpype.pipeline.publish import ( ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateModelContent(pyblish.api.InstancePlugin): - """Adheres to the content of 'model' family +class ValidateModelContent(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): + """Adheres to the content of 'model' product type - Must have one top group. (configurable) - Must only contain: transforms, meshes and groups @@ -24,6 +26,7 @@ class ValidateModelContent(pyblish.api.InstancePlugin): actions = [openpype.hosts.maya.api.action.SelectInvalidAction] validate_top_group = True + optional = False @classmethod def get_invalid(cls, instance): @@ -91,7 +94,8 @@ def _is_visible(node): return list(invalid) def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py b/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py index f0aa9261f72..62b46125a43 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_default_camera.py @@ -4,7 +4,8 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) @@ -15,7 +16,8 @@ def _as_report_list(values, prefix="- ", suffix="\n"): return prefix + (suffix + prefix).join(values) -class ValidateNoDefaultCameras(pyblish.api.InstancePlugin): +class ValidateNoDefaultCameras(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure no default (startup) cameras are in the instance. This might be unnecessary. In the past there were some issues with @@ -28,6 +30,7 @@ class ValidateNoDefaultCameras(pyblish.api.InstancePlugin): families = ['camera'] label = "No Default Cameras" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False @staticmethod def get_invalid(instance): @@ -37,6 +40,8 @@ def get_invalid(instance): def process(self, instance): """Process all the cameras in the instance""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_no_namespace.py b/openpype/hosts/maya/plugins/publish/validate_no_namespace.py index 13eeae58591..59ec4f3cc35 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_namespace.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_namespace.py @@ -4,7 +4,8 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) import openpype.hosts.maya.api.action @@ -24,7 +25,8 @@ def get_namespace(node_name): return node_name.rpartition(":")[0] -class ValidateNoNamespace(pyblish.api.InstancePlugin): +class ValidateNoNamespace(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure the nodes don't have a namespace""" order = ValidateContentsOrder @@ -33,6 +35,7 @@ class ValidateNoNamespace(pyblish.api.InstancePlugin): label = 'No Namespaces' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] + optional = False @staticmethod def get_invalid(instance): @@ -41,6 +44,8 @@ def get_invalid(instance): def process(self, instance): """Process all the nodes in the instance""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py b/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py index 187135fdf30..acd742e4a05 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_null_transforms.py @@ -5,7 +5,8 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) @@ -37,7 +38,8 @@ def has_shape_children(node): return True -class ValidateNoNullTransforms(pyblish.api.InstancePlugin): +class ValidateNoNullTransforms(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure no null transforms are in the scene. Warning: @@ -54,6 +56,7 @@ class ValidateNoNullTransforms(pyblish.api.InstancePlugin): label = 'No Empty/Null Transforms' actions = [RepairAction, openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False @staticmethod def get_invalid(instance): @@ -70,6 +73,8 @@ def get_invalid(instance): def process(self, instance): """Process all the transform nodes in the instance """ + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_no_vraymesh.py b/openpype/hosts/maya/plugins/publish/validate_no_vraymesh.py index 22fd1edc29b..d6d8fc25424 100644 --- a/openpype/hosts/maya/plugins/publish/validate_no_vraymesh.py +++ b/openpype/hosts/maya/plugins/publish/validate_no_vraymesh.py @@ -1,7 +1,9 @@ import pyblish.api from maya import cmds -from openpype.pipeline.publish import PublishValidationError - +from openpype.pipeline.publish import ( + PublishValidationError, + OptionalPyblishPluginMixin +) def _as_report_list(values, prefix="- ", suffix="\n"): """Return list as bullet point list for a report""" @@ -10,15 +12,18 @@ def _as_report_list(values, prefix="- ", suffix="\n"): return prefix + (suffix + prefix).join(values) -class ValidateNoVRayMesh(pyblish.api.InstancePlugin): +class ValidateNoVRayMesh(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate there are no VRayMesh objects in the instance""" order = pyblish.api.ValidatorOrder label = 'No V-Ray Proxies (VRayMesh)' families = ["pointcache"] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return if not cmds.pluginInfo("vrayformaya", query=True, loaded=True): return diff --git a/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py b/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py index 0f608dab2c2..00610d8a4af 100644 --- a/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py +++ b/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py @@ -3,10 +3,12 @@ import pyblish.api import openpype.hosts.maya.api.action -from openpype.pipeline.publish import ValidateContentsOrder - - -class ValidateNodeNoGhosting(pyblish.api.InstancePlugin): +from openpype.pipeline.publish import ( + ValidateContentsOrder, + OptionalPyblishPluginMixin +) +class ValidateNodeNoGhosting(pyblish.api.InstancePlugin. + OptionalPyblishPluginMixin): """Ensure nodes do not have ghosting enabled. If one would publish towards a non-Maya format it's likely that stats @@ -23,6 +25,7 @@ class ValidateNodeNoGhosting(pyblish.api.InstancePlugin): families = ['model', 'rig'] label = "No Ghosting" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False _attributes = {'ghosting': 0} @@ -46,7 +49,8 @@ def get_invalid(cls, instance): return invalid def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py b/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py index cb5c68e4ab4..3382e2ab338 100644 --- a/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_plugin_path_attributes.py @@ -8,11 +8,13 @@ from openpype.hosts.maya.api.action import SelectInvalidAction from openpype.pipeline.publish import ( ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidatePluginPathAttributes(pyblish.api.InstancePlugin): +class ValidatePluginPathAttributes(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """ Validate plug-in path attributes point to existing file paths. """ @@ -22,6 +24,7 @@ class ValidatePluginPathAttributes(pyblish.api.InstancePlugin): families = ["workfile"] label = "Plug-in Path Attributes" actions = [SelectInvalidAction] + optional = False # Attributes are defined in project settings attribute = [] @@ -56,6 +59,8 @@ def get_invalid(cls, instance): def process(self, instance): """Process all directories Set as Filenames in Non-Maya Nodes""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py index 030e41ca1f2..c0d2472a67b 100644 --- a/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py +++ b/openpype/hosts/maya/plugins/publish/validate_render_image_rule.py @@ -5,10 +5,15 @@ from maya import cmds from openpype.pipeline.publish import ( - PublishValidationError, RepairAction, ValidateContentsOrder) + PublishValidationError, + RepairAction, + ValidateContentsOrder, + OptionalPyblishPluginMixin +) -class ValidateRenderImageRule(pyblish.api.InstancePlugin): +class ValidateRenderImageRule(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates Maya Workpace "images" file rule matches project settings. This validates against the configured default render image folder: @@ -22,9 +27,11 @@ class ValidateRenderImageRule(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["renderlayer"] actions = [RepairAction] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return required_images_rule = os.path.normpath( self.get_default_render_image_folder(instance) ) diff --git a/openpype/hosts/maya/plugins/publish/validate_render_no_default_cameras.py b/openpype/hosts/maya/plugins/publish/validate_render_no_default_cameras.py index 9d4410186b1..e7957851d65 100644 --- a/openpype/hosts/maya/plugins/publish/validate_render_no_default_cameras.py +++ b/openpype/hosts/maya/plugins/publish/validate_render_no_default_cameras.py @@ -6,10 +6,12 @@ from openpype.pipeline.publish import ( ValidateContentsOrder, PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin): +class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure no default (startup) cameras are to be rendered.""" order = ValidateContentsOrder @@ -17,6 +19,7 @@ class ValidateRenderNoDefaultCameras(pyblish.api.InstancePlugin): families = ['renderlayer'] label = "No Default Cameras Renderable" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False @staticmethod def get_invalid(instance): @@ -32,6 +35,8 @@ def get_invalid(instance): def process(self, instance): """Process all the cameras in the instance""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_render_single_camera.py b/openpype/hosts/maya/plugins/publish/validate_render_single_camera.py index 2c0d6041756..706704099d4 100644 --- a/openpype/hosts/maya/plugins/publish/validate_render_single_camera.py +++ b/openpype/hosts/maya/plugins/publish/validate_render_single_camera.py @@ -7,11 +7,13 @@ from openpype.hosts.maya.api.lib_rendersettings import RenderSettings from openpype.pipeline.publish import ( ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateRenderSingleCamera(pyblish.api.InstancePlugin): +class ValidateRenderSingleCamera(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate renderable camera count for layer and token. Pipeline is supporting multiple renderable cameras per layer, but image @@ -24,11 +26,14 @@ class ValidateRenderSingleCamera(pyblish.api.InstancePlugin): families = ["renderlayer", "vrayscene"] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False R_CAMERA_TOKEN = re.compile(r'%c|', re.IGNORECASE) def process(self, instance): """Process all the cameras in the instance""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError("Invalid cameras for render.") diff --git a/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py b/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py index f8de983e060..6bc9f2f41ad 100644 --- a/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py +++ b/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py @@ -3,13 +3,16 @@ import openpype.hosts.maya.api.action from openpype.client import get_subset_by_name from openpype.pipeline import legacy_io -from openpype.pipeline.publish import PublishValidationError +from openpype.pipeline.publish import ( + PublishValidationError, + OptionalPyblishPluginMixin +) - -class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): +class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate created AOVs / RenderElement is registered in the database - Each render element is registered as a subset which is formatted based on + Each render element is registered as a product which is formatted based on the render layer and the render element, example: . @@ -27,8 +30,12 @@ class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["renderlayer"] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): + if not self.is_active(instance.data): + return + invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_contents.py b/openpype/hosts/maya/plugins/publish/validate_rig_contents.py index 106b4024e28..9c679013ec3 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_contents.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_contents.py @@ -3,11 +3,13 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( PublishValidationError, - ValidateContentsOrder + ValidateContentsOrder, + OptionalPyblishPluginMixin ) -class ValidateRigContents(pyblish.api.InstancePlugin): +class ValidateRigContents(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Ensure rig contains pipeline-critical content Every rig must contain at least two object sets: @@ -21,11 +23,14 @@ class ValidateRigContents(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["rig"] action = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = True accepted_output = ["mesh", "transform"] accepted_controllers = ["transform"] def process(self, instance): + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( @@ -213,6 +218,7 @@ class ValidateSkeletonRigContents(ValidateRigContents): label = "Skeleton Rig Contents" hosts = ["maya"] families = ["rig.fbx"] + optional = True @classmethod def get_invalid(cls, instance): diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py b/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py index 82248c57b3f..0cf320045c9 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_controllers.py @@ -5,13 +5,15 @@ from openpype.pipeline.publish import ( ValidateContentsOrder, RepairAction, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) import openpype.hosts.maya.api.action from openpype.hosts.maya.api.lib import undo_chunk -class ValidateRigControllers(pyblish.api.InstancePlugin): +class ValidateRigControllers(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate rig controllers. Controls must have the transformation attributes on their default @@ -33,6 +35,7 @@ class ValidateRigControllers(pyblish.api.InstancePlugin): label = "Rig Controllers" hosts = ["maya"] families = ["rig"] + optional = True actions = [RepairAction, openpype.hosts.maya.api.action.SelectInvalidAction] @@ -50,6 +53,9 @@ class ValidateRigControllers(pyblish.api.InstancePlugin): } def process(self, instance): + if not self.is_active(instance.data): + return + invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_controllers_arnold_attributes.py b/openpype/hosts/maya/plugins/publish/validate_rig_controllers_arnold_attributes.py index 03f6a5f1ab6..c32ca6a9262 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_controllers_arnold_attributes.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_controllers_arnold_attributes.py @@ -5,14 +5,16 @@ from openpype.pipeline.publish import ( ValidateContentsOrder, RepairAction, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) from openpype.hosts.maya.api import lib import openpype.hosts.maya.api.action -class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin): +class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate rig control curves have no keyable arnold attributes. The Arnold plug-in will create curve attributes like: @@ -35,6 +37,7 @@ class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin): label = "Rig Controllers (Arnold Attributes)" hosts = ["maya"] families = ["rig"] + optional = False actions = [RepairAction, openpype.hosts.maya.api.action.SelectInvalidAction] @@ -48,6 +51,9 @@ class ValidateRigControllersArnoldAttributes(pyblish.api.InstancePlugin): ] def process(self, instance): + if not self.is_active(instance.data): + return + invalid = self.get_invalid(instance) if invalid: raise PublishValidationError('{} failed, see log ' diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py b/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py index 2bb5036f8ba..4f9fc6577c9 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_joints_hidden.py @@ -7,11 +7,13 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateRigJointsHidden(pyblish.api.InstancePlugin): +class ValidateRigJointsHidden(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate all joints are hidden visually. This includes being hidden: @@ -28,6 +30,7 @@ class ValidateRigJointsHidden(pyblish.api.InstancePlugin): label = "Joints Hidden" actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] + optional = True @staticmethod def get_invalid(instance): @@ -36,6 +39,8 @@ def get_invalid(instance): def process(self, instance): """Process all the nodes in the instance 'objectSet'""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py index 80ac0f27e69..1a43ae810ad 100644 --- a/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py +++ b/openpype/hosts/maya/plugins/publish/validate_rig_out_set_node_ids.py @@ -7,11 +7,13 @@ from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): +class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate if deformed shapes have related IDs to the original shapes. When a deformer is applied in the scene on a referenced mesh that already @@ -30,10 +32,12 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin): RepairAction ] allow_history_only = False + optional = False def process(self, instance): """Process all meshes""" - + if not self.is_active(instance.data): + return # Ensure all nodes have a cbId and a related ID to the original shapes # if a deformer has been created on the shape invalid = self.get_invalid(instance) @@ -114,6 +118,7 @@ class ValidateSkeletonRigOutSetNodeIds(ValidateRigOutSetNodeIds): families = ["rig.fbx"] hosts = ['maya'] label = 'Skeleton Rig Out Set Node Ids' + optional = False @classmethod def get_node(cls, instance): diff --git a/openpype/hosts/maya/plugins/publish/validate_shape_render_stats.py b/openpype/hosts/maya/plugins/publish/validate_shape_render_stats.py index f58c0aaf81d..a494a6cf45c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_shape_render_stats.py +++ b/openpype/hosts/maya/plugins/publish/validate_shape_render_stats.py @@ -6,10 +6,12 @@ from openpype.pipeline.publish import ( RepairAction, ValidateMeshOrder, + OptionalPyblishPluginMixin ) -class ValidateShapeRenderStats(pyblish.api.Validator): +class ValidateShapeRenderStats(pyblish.api.Validator, + OptionalPyblishPluginMixin): """Ensure all render stats are set to the default values.""" order = ValidateMeshOrder @@ -18,6 +20,7 @@ class ValidateShapeRenderStats(pyblish.api.Validator): label = 'Shape Default Render Stats' actions = [openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction] + optional = True defaults = {'castsShadows': 1, 'receiveShadows': 1, @@ -46,7 +49,8 @@ def get_invalid(cls, instance): return invalid def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py index c7af6a60dbe..7850407e487 100644 --- a/openpype/hosts/maya/plugins/publish/validate_shape_zero.py +++ b/openpype/hosts/maya/plugins/publish/validate_shape_zero.py @@ -7,11 +7,13 @@ from openpype.pipeline.publish import ( ValidateContentsOrder, RepairAction, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateShapeZero(pyblish.api.Validator): +class ValidateShapeZero(pyblish.api.Validator, + OptionalPyblishPluginMixin): """Shape components may not have any "tweak" values To solve this issue, try freezing the shapes. @@ -26,6 +28,7 @@ class ValidateShapeZero(pyblish.api.Validator): openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction ] + optional = True @staticmethod def get_invalid(instance): @@ -65,6 +68,8 @@ def repair(cls, instance): def process(self, instance): """Process all the nodes in the instance "objectSet""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_skeletalmesh_hierarchy.py b/openpype/hosts/maya/plugins/publish/validate_skeletalmesh_hierarchy.py index 9084374c769..eaf74cb810a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_skeletalmesh_hierarchy.py +++ b/openpype/hosts/maya/plugins/publish/validate_skeletalmesh_hierarchy.py @@ -4,20 +4,25 @@ from openpype.pipeline.publish import ( ValidateContentsOrder, PublishXmlValidationError, + OptionalPyblishPluginMixin ) from maya import cmds -class ValidateSkeletalMeshHierarchy(pyblish.api.InstancePlugin): +class ValidateSkeletalMeshHierarchy(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates that nodes has common root.""" order = ValidateContentsOrder hosts = ["maya"] families = ["skeletalMesh"] label = "Skeletal Mesh Top Node" + optional = False def process(self, instance): + if not self.is_active(instance.data): + return geo = instance.data.get("geometry") joints = instance.data.get("joints") diff --git a/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py b/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py index b45d2b120af..384b1c28e0a 100644 --- a/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py +++ b/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py @@ -3,10 +3,11 @@ import pyblish.api import openpype.hosts.maya.api.action -from openpype.pipeline.publish import ValidateContentsOrder +from openpype.pipeline.publish import ValidateContentsOrder,OptionalPyblishPluginMixin -class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin): +class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate skinClusters on meshes have valid member relationships. In rare cases it can happen that a mesh has a skinCluster in its history @@ -20,9 +21,12 @@ class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin): families = ['fbx'] label = "Skincluster Deformer Relationships" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): """Process all the transform nodes in the instance""" + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_step_size.py b/openpype/hosts/maya/plugins/publish/validate_step_size.py index 493a6ee65c6..16c2b6b321c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_step_size.py +++ b/openpype/hosts/maya/plugins/publish/validate_step_size.py @@ -3,11 +3,13 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( PublishValidationError, - ValidateContentsOrder + ValidateContentsOrder, + OptionalPyblishPluginMixin ) -class ValidateStepSize(pyblish.api.InstancePlugin): +class ValidateStepSize(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates the step size for the instance is in a valid range. For example the `step` size should never be lower or equal to zero. @@ -20,7 +22,7 @@ class ValidateStepSize(pyblish.api.InstancePlugin): 'pointcache', 'animation'] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] - + optional = False MIN = 0.01 MAX = 1.0 @@ -40,7 +42,8 @@ def get_invalid(cls, instance): return [] def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( diff --git a/openpype/hosts/maya/plugins/publish/validate_transform_zero.py b/openpype/hosts/maya/plugins/publish/validate_transform_zero.py index 906ff17ec9a..6bdc2d2a598 100644 --- a/openpype/hosts/maya/plugins/publish/validate_transform_zero.py +++ b/openpype/hosts/maya/plugins/publish/validate_transform_zero.py @@ -5,11 +5,13 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateTransformZero(pyblish.api.Validator): +class ValidateTransformZero(pyblish.api.Validator, + OptionalPyblishPluginMixin): """Transforms can't have any values To solve this issue, try freezing the transforms. So long @@ -29,6 +31,7 @@ class ValidateTransformZero(pyblish.api.Validator): 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0] _tolerance = 1e-30 + optional = True @classmethod def get_invalid(cls, instance): @@ -62,7 +65,8 @@ def get_invalid(cls, instance): def process(self, instance): """Process all the nodes in the instance "objectSet""" - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_unique_names.py b/openpype/hosts/maya/plugins/publish/validate_unique_names.py index 05776ee0f37..ce8da0367d9 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unique_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_unique_names.py @@ -2,10 +2,13 @@ import pyblish.api import openpype.hosts.maya.api.action -from openpype.pipeline.publish import ValidateContentsOrder +from openpype.pipeline.publish import ( + ValidateContentsOrder, + OptionalPyblishPluginMixin +) - -class ValidateUniqueNames(pyblish.api.Validator): +class ValidateUniqueNames(pyblish.api.Validator, + OptionalPyblishPluginMixin): """transform names should be unique ie: using cmds.ls(someNodeName) should always return shortname @@ -17,6 +20,7 @@ class ValidateUniqueNames(pyblish.api.Validator): families = ["model"] label = "Unique transform name" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = True @staticmethod def get_invalid(instance): @@ -32,7 +36,8 @@ def get_invalid(instance): def process(self, instance): """Process all the nodes in the instance "objectSet""" - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise ValueError("Nodes found with none unique names. " diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py b/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py index e78962bf97c..70435bfb3dc 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py @@ -3,11 +3,15 @@ from maya import cmds import pyblish.api -from openpype.pipeline.publish import ValidateMeshOrder +from openpype.pipeline.publish import ( + ValidateMeshOrder, + OptionalPyblishPluginMixin +) import openpype.hosts.maya.api.action -class ValidateUnrealMeshTriangulated(pyblish.api.InstancePlugin): +class ValidateUnrealMeshTriangulated(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate if mesh is made of triangles for Unreal Engine""" order = ValidateMeshOrder @@ -30,6 +34,8 @@ def get_invalid(cls, instance): return invalid def process(self, instance): + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) assert len(invalid) == 0, ( "Found meshes without triangles") diff --git a/openpype/hosts/maya/plugins/publish/validate_visible_only.py b/openpype/hosts/maya/plugins/publish/validate_visible_only.py index e72782e552e..4a17e567931 100644 --- a/openpype/hosts/maya/plugins/publish/validate_visible_only.py +++ b/openpype/hosts/maya/plugins/publish/validate_visible_only.py @@ -4,11 +4,13 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateAlembicVisibleOnly(pyblish.api.InstancePlugin): +class ValidateAlembicVisibleOnly(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validates at least a single node is visible in frame range. This validation only validates if the `visibleOnly` flag is enabled @@ -20,9 +22,11 @@ class ValidateAlembicVisibleOnly(pyblish.api.InstancePlugin): hosts = ["maya"] families = ["pointcache", "animation"] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return if not instance.data.get("visibleOnly", False): self.log.debug("Visible only is disabled. Validation skipped..") return diff --git a/openpype/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py b/openpype/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py index 14571203ea6..a11ebb5b8ee 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py +++ b/openpype/hosts/maya/plugins/publish/validate_vray_distributed_rendering.py @@ -3,10 +3,15 @@ from openpype.hosts.maya.api import lib from openpype.pipeline.publish import ( - PublishValidationError, RepairAction, ValidateContentsOrder) + PublishValidationError, + RepairAction, + ValidateContentsOrder, + OptionalPyblishPluginMixin +) -class ValidateVRayDistributedRendering(pyblish.api.InstancePlugin): +class ValidateVRayDistributedRendering(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate V-Ray Distributed Rendering is ignored in batch mode. Whenever Distributed Rendering is enabled for V-Ray in the render settings @@ -20,13 +25,15 @@ class ValidateVRayDistributedRendering(pyblish.api.InstancePlugin): label = "VRay Distributed Rendering" families = ["renderlayer"] actions = [RepairAction] + optional = False # V-Ray attribute names enabled_attr = "vraySettings.sys_distributed_rendering_on" ignored_attr = "vraySettings.sys_distributed_rendering_ignore_batch" def process(self, instance): - + if not self.is_active(instance.data): + return if instance.data.get("renderer") != "vray": # If not V-Ray ignore.. return diff --git a/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py b/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py index 39c721e7175..25fb1fce4cc 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py +++ b/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py @@ -4,10 +4,13 @@ import types from maya import cmds -from openpype.pipeline.publish import RepairContextAction +from openpype.pipeline.publish import ( + RepairContextAction, + OptionalPyblishPluginMixin +) - -class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin): +class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate whether the V-Ray Render Elements (AOVs) include references. This will check if there are AOVs pulled from references. If @@ -21,9 +24,12 @@ class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['renderlayer'] actions = [RepairContextAction] + optional = False def process(self, instance): """Plugin main entry point.""" + if not self.is_active(instance.data): + return if instance.data.get("renderer") != "vray": # If not V-Ray ignore.. return diff --git a/openpype/hosts/maya/plugins/publish/validate_vray_translator_settings.py b/openpype/hosts/maya/plugins/publish/validate_vray_translator_settings.py index 4474f08ba44..1095eda5f49 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vray_translator_settings.py +++ b/openpype/hosts/maya/plugins/publish/validate_vray_translator_settings.py @@ -5,22 +5,27 @@ context_plugin_should_run, RepairContextAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) from maya import cmds -class ValidateVRayTranslatorEnabled(pyblish.api.ContextPlugin): +class ValidateVRayTranslatorEnabled(pyblish.api.ContextPlugin, + OptionalPyblishPluginMixin): """Validate VRay Translator settings for extracting vrscenes.""" order = ValidateContentsOrder label = "VRay Translator Settings" families = ["vrayscene_layer"] actions = [RepairContextAction] + optional = False def process(self, context): """Plugin entry point.""" + if not self.is_active(context.data): + return # Workaround bug pyblish-base#250 if not context_plugin_should_run(self, context): return diff --git a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py index a106b970b47..fc6a6bf4859 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py +++ b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py @@ -1,18 +1,21 @@ import pyblish.api from openpype.pipeline import KnownPublishError +from openpype.pipeline.publish import OptionalPyblishPluginMixin - -class ValidateVrayProxy(pyblish.api.InstancePlugin): +class ValidateVrayProxy(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): order = pyblish.api.ValidatorOrder label = "VRay Proxy Settings" hosts = ["maya"] families = ["vrayproxy"] + optional = False def process(self, instance): data = instance.data - + if not self.is_active(data): + return if not data["setMembers"]: raise KnownPublishError( "'%s' is empty! This is a bug" % instance.name diff --git a/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py b/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py index 7b726de3a80..89d12ffc1ad 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py +++ b/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py @@ -4,12 +4,14 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateVrayProxyMembers(pyblish.api.InstancePlugin): +class ValidateVrayProxyMembers(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate whether the V-Ray Proxy instance has shape members""" order = pyblish.api.ValidatorOrder @@ -17,9 +19,11 @@ class ValidateVrayProxyMembers(pyblish.api.InstancePlugin): hosts = ['maya'] families = ['vrayproxy'] actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: diff --git a/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py b/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py index a8085418e7f..02454a4ddf0 100644 --- a/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py +++ b/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py @@ -1,10 +1,13 @@ from maya import cmds import pyblish.api -from openpype.pipeline.publish import ValidateContentsOrder +from openpype.pipeline.publish import ( + ValidateContentsOrder, + OptionalPyblishPluginMixin +) - -class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin): +class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """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 @@ -24,6 +27,7 @@ class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin): label = "Yeti Render Script Callbacks" hosts = ["maya"] families = ["renderlayer"] + optional = False # Settings per renderer callbacks = { @@ -37,7 +41,8 @@ class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin): } def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise ValueError("Invalid render callbacks found for '%s'!" diff --git a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py index 2b7249ad943..b807fd059a1 100644 --- a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py +++ b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py @@ -3,12 +3,14 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( RepairAction, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateYetiRigCacheState(pyblish.api.InstancePlugin): +class ValidateYetiRigCacheState(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate the I/O attributes of the node Every pgYetiMaya cache node per instance should have: @@ -23,8 +25,11 @@ class ValidateYetiRigCacheState(pyblish.api.InstancePlugin): families = ["yetiRig"] actions = [RepairAction, openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError("Nodes have incorrect I/O settings") diff --git a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_input_in_instance.py b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_input_in_instance.py index 50a27589ad8..4a25485365c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_input_in_instance.py +++ b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_input_in_instance.py @@ -5,11 +5,13 @@ import openpype.hosts.maya.api.action from openpype.pipeline.publish import ( ValidateContentsOrder, - PublishValidationError + PublishValidationError, + OptionalPyblishPluginMixin ) -class ValidateYetiRigInputShapesInInstance(pyblish.api.Validator): +class ValidateYetiRigInputShapesInInstance(pyblish.api.Validator, + OptionalPyblishPluginMixin): """Validate if all input nodes are part of the instance's hierarchy""" order = ValidateContentsOrder @@ -17,9 +19,11 @@ class ValidateYetiRigInputShapesInInstance(pyblish.api.Validator): families = ["yetiRig"] label = "Yeti Rig Input Shapes In Instance" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError("Yeti Rig has invalid input meshes") diff --git a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_settings.py b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_settings.py index 455bf5291a7..fec45f5b0e6 100644 --- a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_settings.py +++ b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_settings.py @@ -1,9 +1,13 @@ import pyblish.api -from openpype.pipeline.publish import PublishValidationError +from openpype.pipeline.publish import ( + PublishValidationError, + OptionalPyblishPluginMixin +) -class ValidateYetiRigSettings(pyblish.api.InstancePlugin): +class ValidateYetiRigSettings(pyblish.api.InstancePlugin, + OptionalPyblishPluginMixin): """Validate Yeti Rig Settings have collected input connections. The input connections are collected for the nodes in the `input_SET`. @@ -15,9 +19,11 @@ class ValidateYetiRigSettings(pyblish.api.InstancePlugin): order = pyblish.api.ValidatorOrder label = "Yeti Rig Settings" families = ["yetiRig"] + optional = False def process(self, instance): - + if not self.is_active(instance.data): + return invalid = self.get_invalid(instance) if invalid: raise PublishValidationError( From 31383625a465e4ee1b55f060d5cc501e81c129a0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 3 Apr 2024 18:35:46 +0800 Subject: [PATCH 119/163] hound shut --- .../maya/plugins/publish/validate_assembly_namespaces.py | 1 + .../publish/validate_current_renderlayer_renderable.py | 1 + .../hosts/maya/plugins/publish/validate_glsl_material.py | 5 ++++- .../hosts/maya/plugins/publish/validate_mesh_lamina_faces.py | 5 ++++- .../hosts/maya/plugins/publish/validate_node_no_ghosting.py | 2 ++ .../hosts/maya/plugins/publish/validate_renderlayer_aovs.py | 1 + .../plugins/publish/validate_skinCluster_deformer_set.py | 5 ++++- openpype/hosts/maya/plugins/publish/validate_unique_names.py | 1 + .../maya/plugins/publish/validate_vray_referenced_aovs.py | 1 + openpype/hosts/maya/plugins/publish/validate_vrayproxy.py | 1 + .../hosts/maya/plugins/publish/validate_vrayproxy_members.py | 1 - .../plugins/publish/validate_yeti_renderscript_callbacks.py | 1 + .../maya/plugins/publish/validate_yeti_rig_cache_state.py | 1 - 13 files changed, 21 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py b/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py index a04aa6edfe8..9f8d3483fd0 100644 --- a/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py +++ b/openpype/hosts/maya/plugins/publish/validate_assembly_namespaces.py @@ -5,6 +5,7 @@ OptionalPyblishPluginMixin ) + class ValidateAssemblyNamespaces(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Ensure namespaces are not nested diff --git a/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py b/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py index 422d3bb8d46..992143e8277 100644 --- a/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py +++ b/openpype/hosts/maya/plugins/publish/validate_current_renderlayer_renderable.py @@ -6,6 +6,7 @@ OptionalPyblishPluginMixin ) + class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin, OptionalPyblishPluginMixin): """Validate if current render layer has a renderable camera diff --git a/openpype/hosts/maya/plugins/publish/validate_glsl_material.py b/openpype/hosts/maya/plugins/publish/validate_glsl_material.py index 5d43718e478..a34f8206ec7 100644 --- a/openpype/hosts/maya/plugins/publish/validate_glsl_material.py +++ b/openpype/hosts/maya/plugins/publish/validate_glsl_material.py @@ -6,7 +6,10 @@ RepairAction, ValidateContentsOrder ) -from openpype.pipeline import PublishValidationError, OptionalPyblishPluginMixin +from openpype.pipeline import ( + PublishValidationError, + OptionalPyblishPluginMixin +) class ValidateGLSLMaterial(pyblish.api.InstancePlugin, diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py index ea7e3fe9aaa..eda1c63ddd6 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_lamina_faces.py @@ -2,7 +2,10 @@ import pyblish.api import openpype.hosts.maya.api.action -from openpype.pipeline.publish import ValidateMeshOrder, OptionalPyblishPluginMixin +from openpype.pipeline.publish import ( + ValidateMeshOrder, + OptionalPyblishPluginMixin +) class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin, diff --git a/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py b/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py index 00610d8a4af..fdf0437d109 100644 --- a/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py +++ b/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py @@ -7,6 +7,8 @@ ValidateContentsOrder, OptionalPyblishPluginMixin ) + + class ValidateNodeNoGhosting(pyblish.api.InstancePlugin. OptionalPyblishPluginMixin): """Ensure nodes do not have ghosting enabled. diff --git a/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py b/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py index 6bc9f2f41ad..3e7f8d9591d 100644 --- a/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py +++ b/openpype/hosts/maya/plugins/publish/validate_renderlayer_aovs.py @@ -8,6 +8,7 @@ OptionalPyblishPluginMixin ) + class ValidateRenderLayerAOVs(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate created AOVs / RenderElement is registered in the database diff --git a/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py b/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py index 384b1c28e0a..1b70a9f38ab 100644 --- a/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py +++ b/openpype/hosts/maya/plugins/publish/validate_skinCluster_deformer_set.py @@ -3,7 +3,10 @@ import pyblish.api import openpype.hosts.maya.api.action -from openpype.pipeline.publish import ValidateContentsOrder,OptionalPyblishPluginMixin +from openpype.pipeline.publish import ( + ValidateContentsOrder, + OptionalPyblishPluginMixin +) class ValidateSkinclusterDeformerSet(pyblish.api.InstancePlugin, diff --git a/openpype/hosts/maya/plugins/publish/validate_unique_names.py b/openpype/hosts/maya/plugins/publish/validate_unique_names.py index ce8da0367d9..4f69ebbfe6e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unique_names.py +++ b/openpype/hosts/maya/plugins/publish/validate_unique_names.py @@ -7,6 +7,7 @@ OptionalPyblishPluginMixin ) + class ValidateUniqueNames(pyblish.api.Validator, OptionalPyblishPluginMixin): """transform names should be unique diff --git a/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py b/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py index 25fb1fce4cc..7cb4fc53195 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py +++ b/openpype/hosts/maya/plugins/publish/validate_vray_referenced_aovs.py @@ -9,6 +9,7 @@ OptionalPyblishPluginMixin ) + class ValidateVrayReferencedAOVs(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate whether the V-Ray Render Elements (AOVs) include references. diff --git a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py index fc6a6bf4859..ecf2a571725 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py +++ b/openpype/hosts/maya/plugins/publish/validate_vrayproxy.py @@ -3,6 +3,7 @@ from openpype.pipeline import KnownPublishError from openpype.pipeline.publish import OptionalPyblishPluginMixin + class ValidateVrayProxy(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): diff --git a/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py b/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py index 89d12ffc1ad..6c1edd72094 100644 --- a/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py +++ b/openpype/hosts/maya/plugins/publish/validate_vrayproxy_members.py @@ -9,7 +9,6 @@ ) - class ValidateVrayProxyMembers(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate whether the V-Ray Proxy instance has shape members""" diff --git a/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py b/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py index 02454a4ddf0..40818ef7d2d 100644 --- a/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py +++ b/openpype/hosts/maya/plugins/publish/validate_yeti_renderscript_callbacks.py @@ -6,6 +6,7 @@ OptionalPyblishPluginMixin ) + class ValidateYetiRenderScriptCallbacks(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Check if the render script callbacks will be used during the rendering diff --git a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py index b807fd059a1..ece6f7f5912 100644 --- a/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py +++ b/openpype/hosts/maya/plugins/publish/validate_yeti_rig_cache_state.py @@ -8,7 +8,6 @@ ) - class ValidateYetiRigCacheState(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Validate the I/O attributes of the node From 4a4fbee4650e7ceaae4347a84f62a4d5fd9eaec3 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 3 Apr 2024 18:37:41 +0800 Subject: [PATCH 120/163] hound shut --- .../hosts/maya/plugins/publish/validate_node_no_ghosting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py b/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py index fdf0437d109..dc0e1aba469 100644 --- a/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py +++ b/openpype/hosts/maya/plugins/publish/validate_node_no_ghosting.py @@ -9,7 +9,7 @@ ) -class ValidateNodeNoGhosting(pyblish.api.InstancePlugin. +class ValidateNodeNoGhosting(pyblish.api.InstancePlugin, OptionalPyblishPluginMixin): """Ensure nodes do not have ghosting enabled. From de28759c3e395d02ba943bee206d814e78e26f3d Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 6 Apr 2024 03:24:53 +0000 Subject: [PATCH 121/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 1511fe14f1a..df97402d1c0 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.3" +__version__ = "3.18.9-nightly.4" From 45ee85ca0d2a992274adfba0ae5c240b60e73825 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 6 Apr 2024 03:25:32 +0000 Subject: [PATCH 122/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 11b1f89bae7..403f102499d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.4 - 3.18.9-nightly.3 - 3.18.9-nightly.2 - 3.18.9-nightly.1 @@ -134,7 +135,6 @@ body: - 3.15.12-nightly.3 - 3.15.12-nightly.2 - 3.15.12-nightly.1 - - 3.15.11 validations: required: true - type: dropdown From 7ecfbae4e07416f82b71ad9946bc68b44f67064c Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 7 Apr 2024 21:36:38 +0100 Subject: [PATCH 123/163] Using role_ colorspaces. --- openpype/settings/defaults/project_settings/hiero.json | 10 +++++----- openpype/settings/defaults/project_settings/nuke.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index ae09b34c988..c2f7736a7ce 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -11,14 +11,14 @@ }, "workfile": { "ocioConfigName": "aces_1.2", - "workingSpace": "scene_linear (ACES - ACEScg)", + "workingSpace": "role_scene_linear", "viewerLut": "ACES/sRGB", "thumbnailLut": "ACES/sRGB", "monitorOutLut": "ACES/sRGB", - "eightBitLut": "matte_paint (Utility - sRGB - Texture)", - "sixteenBitLut": "texture_paint (ACES - ACEScc)", - "logLut": "compositing_log (Input - ADX - ADX10)", - "floatLut": "scene_linear (ACES - ACEScg)" + "eightBitLut": "role_matte_paint", + "sixteenBitLut": "role_texture_paint", + "logLut": "role_compositing_log", + "floatLut": "role_scene_linear" }, "regexInputs": { "inputs": [ diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index b41c1e49903..4c4f4d36dad 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -27,13 +27,13 @@ "workfile": { "colorManagement": "OCIO", "OCIO_config": "aces_1.2", - "workingSpaceLUT": "scene_linear (ACES - ACEScg)", + "workingSpaceLUT": "role_scene_linear", "monitorLut": "sRGB (ACES)", "monitorOutLUT": "sRGB (ACES)", - "int8Lut": "matte_paint (Utility - sRGB - Texture)", - "int16Lut": "texture_paint (ACES - ACEScc)", - "logLut": "compositing_log (Input - ADX - ADX10)", - "floatLut": "scene_linear (ACES - ACEScg)" + "int8Lut": "role_matte_paint", + "int16Lut": "role_texture_paint", + "logLut": "role_compositing_log", + "floatLut": "role_scene_linear" }, "nodes": { "requiredNodes": [ From 3c5924a71a3cb4d42eb049c57519ddcb8c1aca6b Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 7 Apr 2024 21:41:09 +0100 Subject: [PATCH 124/163] Align Nuke with Hiero --- openpype/settings/defaults/project_settings/nuke.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index 4c4f4d36dad..f65bc105c26 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -28,8 +28,8 @@ "colorManagement": "OCIO", "OCIO_config": "aces_1.2", "workingSpaceLUT": "role_scene_linear", - "monitorLut": "sRGB (ACES)", - "monitorOutLUT": "sRGB (ACES)", + "monitorLut": "ACES/sRGB", + "monitorOutLUT": "ACES/sRGB", "int8Lut": "role_matte_paint", "int16Lut": "role_texture_paint", "logLut": "role_compositing_log", From 837dacd73056cfea97171422e1402adb74d79b70 Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Sun, 7 Apr 2024 22:25:33 +0100 Subject: [PATCH 125/163] Make it specific to redshift --- openpype/pipeline/farm/pyblish_functions.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/openpype/pipeline/farm/pyblish_functions.py b/openpype/pipeline/farm/pyblish_functions.py index bd532969d23..355c47783e5 100644 --- a/openpype/pipeline/farm/pyblish_functions.py +++ b/openpype/pipeline/farm/pyblish_functions.py @@ -632,10 +632,13 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, # happens with Redshift that forces Cryptomatte renders to be separate # files even when the rest of the AOVs are merged into a single EXR. # There might be an edge case where the main instance has cryptomatte - # in the name even though its a multipart EXR. - if (instance.data.get("multipartExr") and - "cryptomatte" not in render_file_name.lower()): - log.debug("Adding preview tag because its multipartExr") + # in the name even though it's a multipart EXR. + if ( + instance.data.get("multipartExr") and + instance.data.get("renderer", "") == "redshift" and + "cryptomatte" not in render_file_name.lower() + ): + log.debug("Adding preview tag because it's multipartExr") preview = True else: new_instance["multipartExr"] = False From 6ad2bab2742431ce2bfb5ff1365806c64a4e8eaf Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 9 Apr 2024 12:42:27 +0100 Subject: [PATCH 126/163] Update openpype/pipeline/farm/pyblish_functions.py Co-authored-by: Roy Nieterau --- openpype/pipeline/farm/pyblish_functions.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/openpype/pipeline/farm/pyblish_functions.py b/openpype/pipeline/farm/pyblish_functions.py index 355c47783e5..75bfaebebf7 100644 --- a/openpype/pipeline/farm/pyblish_functions.py +++ b/openpype/pipeline/farm/pyblish_functions.py @@ -633,15 +633,15 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, # files even when the rest of the AOVs are merged into a single EXR. # There might be an edge case where the main instance has cryptomatte # in the name even though it's a multipart EXR. - if ( - instance.data.get("multipartExr") and - instance.data.get("renderer", "") == "redshift" and - "cryptomatte" not in render_file_name.lower() - ): - log.debug("Adding preview tag because it's multipartExr") - preview = True - else: - new_instance["multipartExr"] = False + if instance.data.get("renderer") == "redshift": + if ( + instance.data.get("multipartExr") and + "cryptomatte" not in render_file_name.lower() + ): + log.debug("Adding preview tag because it's multipartExr") + preview = True + else: + new_instance["multipartExr"] = False # explicitly disable review by user preview = preview and not do_not_add_review From c426158ca17c7f05130da9b0b1933cf87859821b Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 9 Apr 2024 12:44:32 +0100 Subject: [PATCH 127/163] Update openpype/settings/defaults/project_settings/nuke.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Ježek --- openpype/settings/defaults/project_settings/nuke.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index f65bc105c26..a4e3b0a97f4 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -22,7 +22,7 @@ "viewerProcess": "sRGB (ACES)" }, "baking": { - "viewerProcess": "rec709 (default)" + "viewerProcess": "Rec.709 (ACES)" }, "workfile": { "colorManagement": "OCIO", From 5fe35c2a0e3a716c22c76976b0e3c4d32d6ed2b3 Mon Sep 17 00:00:00 2001 From: Toke Jepsen Date: Tue, 9 Apr 2024 15:11:09 +0100 Subject: [PATCH 128/163] Update openpype/pipeline/farm/pyblish_functions.py Co-authored-by: Roy Nieterau --- openpype/pipeline/farm/pyblish_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/pipeline/farm/pyblish_functions.py b/openpype/pipeline/farm/pyblish_functions.py index 75bfaebebf7..9bea56697bd 100644 --- a/openpype/pipeline/farm/pyblish_functions.py +++ b/openpype/pipeline/farm/pyblish_functions.py @@ -628,7 +628,7 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, # toggle preview on if multipart is on # Because we cant query the multipartExr data member of each AOV we'll # need to have hardcoded rule of excluding any renders with - # "crytomatte" in the file name from being a multipart EXR. This issue + # "cryptomatte" in the file name from being a multipart EXR. This issue # happens with Redshift that forces Cryptomatte renders to be separate # files even when the rest of the AOVs are merged into a single EXR. # There might be an edge case where the main instance has cryptomatte From 2194d06bc4c93d928e71441425f70b7e3584b29b Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 9 Apr 2024 15:15:12 +0100 Subject: [PATCH 129/163] Backward compatibility --- openpype/pipeline/farm/pyblish_functions.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openpype/pipeline/farm/pyblish_functions.py b/openpype/pipeline/farm/pyblish_functions.py index 9bea56697bd..8872ae64cec 100644 --- a/openpype/pipeline/farm/pyblish_functions.py +++ b/openpype/pipeline/farm/pyblish_functions.py @@ -642,6 +642,9 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data, preview = True else: new_instance["multipartExr"] = False + elif instance.data.get("multipartExr"): + log.debug("Adding preview tag because its multipartExr") + preview = True # explicitly disable review by user preview = preview and not do_not_add_review From cdb050ad07b5944581bfcc3cdfc452493f27952d Mon Sep 17 00:00:00 2001 From: Toke Stuart Jepsen Date: Tue, 9 Apr 2024 16:01:09 +0100 Subject: [PATCH 130/163] Fix viewer process naming convention --- openpype/settings/defaults/project_settings/nuke.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json index a4e3b0a97f4..9b46fe8767f 100644 --- a/openpype/settings/defaults/project_settings/nuke.json +++ b/openpype/settings/defaults/project_settings/nuke.json @@ -19,10 +19,10 @@ "rules": {} }, "viewer": { - "viewerProcess": "sRGB (ACES)" + "viewerProcess": "ACES/sRGB" }, "baking": { - "viewerProcess": "Rec.709 (ACES)" + "viewerProcess": "ACES/Rec.709" }, "workfile": { "colorManagement": "OCIO", From df5c534f7438e38ce17d4eb436236f3d4132f000 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 10 Apr 2024 03:25:41 +0000 Subject: [PATCH 131/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index df97402d1c0..9937010abf3 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.4" +__version__ = "3.18.9-nightly.5" From 777239cb1916b78ada0704d34b8517768acf6fd3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Apr 2024 03:26:15 +0000 Subject: [PATCH 132/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 403f102499d..4ffadeb8e62 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.5 - 3.18.9-nightly.4 - 3.18.9-nightly.3 - 3.18.9-nightly.2 @@ -134,7 +135,6 @@ body: - 3.15.12-nightly.4 - 3.15.12-nightly.3 - 3.15.12-nightly.2 - - 3.15.12-nightly.1 validations: required: true - type: dropdown From 9bdf28f516794cccafae814559b8fbb218af3727 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 13 Apr 2024 03:24:15 +0000 Subject: [PATCH 133/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 9937010abf3..1bbe6e4d09a 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.5" +__version__ = "3.18.9-nightly.6" From 25fa7a93bba55aace09773ac463c90a978777a63 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 13 Apr 2024 03:25:00 +0000 Subject: [PATCH 134/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 4ffadeb8e62..d41c6151ad6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.6 - 3.18.9-nightly.5 - 3.18.9-nightly.4 - 3.18.9-nightly.3 @@ -134,7 +135,6 @@ body: - 3.15.12 - 3.15.12-nightly.4 - 3.15.12-nightly.3 - - 3.15.12-nightly.2 validations: required: true - type: dropdown From 53e8b527d28d6c18a81cc8aa8feff494ff37828c Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 15 Apr 2024 17:21:26 +0800 Subject: [PATCH 135/163] backport from ayon to OP --- openpype/hosts/substancepainter/api/lib.py | 4 +- .../plugins/load/load_mesh.py | 59 +++++++++++++------ 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/openpype/hosts/substancepainter/api/lib.py b/openpype/hosts/substancepainter/api/lib.py index 1cb480b552c..d9084c57d64 100644 --- a/openpype/hosts/substancepainter/api/lib.py +++ b/openpype/hosts/substancepainter/api/lib.py @@ -606,7 +606,7 @@ def _setup_prompt(): mesh_select.setVisible(False) # Ensure UI is visually up-to-date - app.processEvents(QtCore.QEventLoop.ExcludeUserInputEvents) + app.processEvents(QtCore.QEventLoop.ExcludeUserInputEvents, 8000) # Trigger the 'select file' dialog to set the path and have the # new file dialog to use the path. @@ -623,8 +623,6 @@ def _setup_prompt(): "Failed to set mesh path with the prompt dialog:" f"{mesh_filepath}\n\n" "Creating new project directly with the mesh path instead.") - else: - dialog.done(dialog.Accepted) new_action = _get_new_project_action() if not new_action: diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 08c1d5c3918..36eaec1aaed 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -1,3 +1,4 @@ +from openpype.lib import BoolDef, EnumDef from openpype.pipeline import ( load, get_representation_path, @@ -11,7 +12,6 @@ from openpype.hosts.substancepainter.api.lib import prompt_new_file_with_mesh import substance_painter.project -import qargparse class SubstanceLoadProjectMesh(load.LoaderPlugin): @@ -25,27 +25,48 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): icon = "code-fork" color = "orange" - options = [ - qargparse.Boolean( - "preserve_strokes", - default=True, - help="Preserve strokes positions on mesh.\n" - "(only relevant when loading into existing project)" - ), - qargparse.Boolean( - "import_cameras", - default=True, - help="Import cameras from the mesh file." - ) - ] - - def load(self, context, name, namespace, data): + @classmethod + def get_options(cls, contexts): + project_workflow_option = { + substance_painter.project.ProjectWorkflow.Default: "default", + substance_painter.project.ProjectWorkflow.UVTile: "uvTile", + substance_painter.project.ProjectWorkflow.TextureSetPerUVTile: "textureSetPerUVTile" # noqa + } + return [ + BoolDef("preserve_strokes", + default=True, + label="Preserve Strokes", + tooltip=("Preserve strokes positions on mesh.\n" + "(only relevant when loading into " + "existing project)")), + BoolDef("import_cameras", + default=True, + label="Import Cameras", + tooltip="Import cameras from the mesh file." + ), + EnumDef("texture_resolution", + items=[128, 256, 512, 1024, 2048, 4096], + default=1024, + label="Texture Resolution", + tooltip="Set texture resolution when creating new project"), + EnumDef("project_uv_workflow", + items=project_workflow_option, + default="default", + label="UV Workflow", + tooltip="Set UV workflow when creating new project") + ] + + def load(self, context, name, namespace, options=None): # Get user inputs - import_cameras = data.get("import_cameras", True) - preserve_strokes = data.get("preserve_strokes", True) + import_cameras = options.get("import_cameras", True) + preserve_strokes = options.get("preserve_strokes", True) + texture_resolution = options.get("texture_resolution", 1024) + uv_workflow = options.get("project_uv_workflow", "default") sp_settings = substance_painter.project.Settings( - import_cameras=import_cameras + default_texture_resolution=texture_resolution, + import_cameras=import_cameras, + project_workflow=uv_workflow ) if not substance_painter.project.is_open(): # Allow to 'initialize' a new project From 7171dad9ea8363770511c997f432db779617edd4 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 15 Apr 2024 17:29:53 +0800 Subject: [PATCH 136/163] hound --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 36eaec1aaed..f7c33667988 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -42,13 +42,13 @@ def get_options(cls, contexts): BoolDef("import_cameras", default=True, label="Import Cameras", - tooltip="Import cameras from the mesh file." - ), + tooltip="Import cameras from the mesh file."), EnumDef("texture_resolution", items=[128, 256, 512, 1024, 2048, 4096], default=1024, label="Texture Resolution", - tooltip="Set texture resolution when creating new project"), + tooltip=("Set texture resolution " + "when creating new project")), EnumDef("project_uv_workflow", items=project_workflow_option, default="default", From 9e9c90404d5be4b821a1e26fb80623b5c2795ce0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 15 Apr 2024 17:38:12 +0800 Subject: [PATCH 137/163] rename project uv workflow items --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index f7c33667988..fb4f348c1f9 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -27,7 +27,7 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): @classmethod def get_options(cls, contexts): - project_workflow_option = { + project_uv_workflow_items = { substance_painter.project.ProjectWorkflow.Default: "default", substance_painter.project.ProjectWorkflow.UVTile: "uvTile", substance_painter.project.ProjectWorkflow.TextureSetPerUVTile: "textureSetPerUVTile" # noqa @@ -50,7 +50,7 @@ def get_options(cls, contexts): tooltip=("Set texture resolution " "when creating new project")), EnumDef("project_uv_workflow", - items=project_workflow_option, + items=project_uv_workflow_items, default="default", label="UV Workflow", tooltip="Set UV workflow when creating new project") From b1c23244024f4e101937118f3e0d4bcf02ec6758 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Mon, 15 Apr 2024 22:34:43 +0200 Subject: [PATCH 138/163] use getenv to get the value of an env variable --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index ae951cacec9..8b40f5ea911 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -187,7 +187,7 @@ def _submit_deadline_post_job(self, instance, job, instances): create_metadata_path(instance, anatomy) environment = { - "AVALON_DB": os.environ["AVALON_DB"], + "AVALON_DB": os.getenv("AVALON_DB"), "AVALON_PROJECT": instance.context.data["projectName"], "AVALON_ASSET": instance.context.data["asset"], "AVALON_TASK": instance.context.data["task"], From b5633afffabe5e2a8143e5a80371db2217c73683 Mon Sep 17 00:00:00 2001 From: MustafaJafar Date: Tue, 16 Apr 2024 10:00:53 +0200 Subject: [PATCH 139/163] move line to a proper place --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 8b40f5ea911..06166a68ace 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -187,7 +187,6 @@ def _submit_deadline_post_job(self, instance, job, instances): create_metadata_path(instance, anatomy) environment = { - "AVALON_DB": os.getenv("AVALON_DB"), "AVALON_PROJECT": instance.context.data["projectName"], "AVALON_ASSET": instance.context.data["asset"], "AVALON_TASK": instance.context.data["task"], @@ -203,6 +202,7 @@ def _submit_deadline_post_job(self, instance, job, instances): environment["AYON_BUNDLE_NAME"] = os.environ["AYON_BUNDLE_NAME"] deadline_plugin = "Ayon" else: + environment["AVALON_DB"] = os.getenv("AVALON_DB") environment["OPENPYPE_PUBLISH_JOB"] = "1" environment["OPENPYPE_RENDER_JOB"] = "0" environment["OPENPYPE_REMOTE_PUBLISH"] = "0" From 391ddbbf45d9ceafa020ab11ff4cc257bcf675ab Mon Sep 17 00:00:00 2001 From: Mustafa Taher Date: Tue, 16 Apr 2024 10:14:33 +0200 Subject: [PATCH 140/163] use os.environ instead of getenv Co-authored-by: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> --- openpype/modules/deadline/plugins/publish/submit_publish_job.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_job.py index 06166a68ace..1fe678616aa 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_job.py @@ -202,7 +202,7 @@ def _submit_deadline_post_job(self, instance, job, instances): environment["AYON_BUNDLE_NAME"] = os.environ["AYON_BUNDLE_NAME"] deadline_plugin = "Ayon" else: - environment["AVALON_DB"] = os.getenv("AVALON_DB") + environment["AVALON_DB"] = os.environ["AVALON_DB"] environment["OPENPYPE_PUBLISH_JOB"] = "1" environment["OPENPYPE_RENDER_JOB"] = "0" environment["OPENPYPE_REMOTE_PUBLISH"] = "0" From 8b31020a767f0567cf4d18392dbddd6078702300 Mon Sep 17 00:00:00 2001 From: Jakub Trllo <43494761+iLLiCiTiT@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:30:31 +0200 Subject: [PATCH 141/163] fix 2 more places --- .../plugins/publish/submit_maya_remote_publish_deadline.py | 2 +- .../deadline/plugins/publish/submit_publish_cache_job.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py index a9fb10de8b3..01398576ada 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_remote_publish_deadline.py @@ -108,7 +108,6 @@ def get_job_info(self): if key in os.environ}, **legacy_io.Session) # TODO replace legacy_io with context.data - environment["AVALON_DB"] = os.environ.get("AVALON_DB") environment["AVALON_PROJECT"] = project_name environment["AVALON_ASSET"] = instance.context.data["asset"] environment["AVALON_TASK"] = instance.context.data["task"] @@ -122,6 +121,7 @@ def get_job_info(self): environment["AYON_REMOTE_PUBLISH"] = "1" else: environment["OPENPYPE_REMOTE_PUBLISH"] = "1" + environment["AVALON_DB"] = os.environ.get("AVALON_DB") for key, value in environment.items(): job_info.EnvironmentKeyValue[key] = value diff --git a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py index 434a823cfe6..6f826e85671 100644 --- a/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py +++ b/openpype/modules/deadline/plugins/publish/submit_publish_cache_job.py @@ -131,7 +131,6 @@ def _submit_deadline_post_job(self, instance, job): create_metadata_path(instance, anatomy) environment = { - "AVALON_DB": os.environ["AVALON_DB"], "AVALON_PROJECT": instance.context.data["projectName"], "AVALON_ASSET": instance.context.data["asset"], "AVALON_TASK": instance.context.data["task"], @@ -147,6 +146,7 @@ def _submit_deadline_post_job(self, instance, job): environment["AYON_BUNDLE_NAME"] = os.environ["AYON_BUNDLE_NAME"] deadline_plugin = "Ayon" else: + environment["AVALON_DB"] = os.environ["AVALON_DB"] environment["OPENPYPE_PUBLISH_JOB"] = "1" environment["OPENPYPE_RENDER_JOB"] = "0" environment["OPENPYPE_REMOTE_PUBLISH"] = "0" From 337872a02f48d6f68088932c02d6d40739d9e977 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Tue, 16 Apr 2024 17:41:18 +0800 Subject: [PATCH 142/163] update the settings --- .../substancepainter/server/settings/main.py | 79 ++++++++++++++++++- .../substancepainter/server/version.py | 2 +- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/server_addon/substancepainter/server/settings/main.py b/server_addon/substancepainter/server/settings/main.py index f80fa9fe1ec..20cf6d77b22 100644 --- a/server_addon/substancepainter/server/settings/main.py +++ b/server_addon/substancepainter/server/settings/main.py @@ -2,6 +2,78 @@ from .imageio import ImageIOSettings, DEFAULT_IMAGEIO_SETTINGS +def normal_map_format_enum(): + return [ + {"label": "DirectX", "value": "DirectX"}, + {"label": "OpenGL", "value": "OpenGL"}, + ] + + +def tangent_space_enum(): + return [ + {"label": "PerFragment", "value": "PerFragment"}, + {"label": "PerVertex", "value": "PerVertex"}, + ] + + +def uv_workflow_enum(): + return [ + {"label": "Default", "value": "default"}, + {"label": "UV Tile", "value": "uvTile"}, + {"label": "Texture Set Per UV Tile", + "value": "textureSetPerUVTile"} + ] + + +def document_resolution_enum(): + return [ + {"label": "128", "value": 128}, + {"label": "256", "value": 256}, + {"label": "512", "value": 512}, + {"label": "1024", "value": 1024}, + {"label": "2048", "value": 2048}, + {"label": "4096", "value": 4096} + ] + + +class ProjectTemplatesModel(BaseSettingsModel): + _layout = "expanded" + name: str = SettingsField(title="Template Name") + document_resolution: int = SettingsField( + 1024, enum_resolver=document_resolution_enum, + title="Document Resolution", + description=("Set texture resolution when " + "creating new project.") + ) + normal_map_format: str = SettingsField( + "DirectX", enum_resolver=normal_map_format_enum, + title="Normal Map Format", + description=("Set normal map format when " + "creating new project.") + ) + tangent_space: str = SettingsField( + "PerFragment", enum_resolver=tangent_space_enum, + title="Tangent Space", + description=("An option to compute tangent space " + "when creating new project.") + ) + uv_workflow: str = SettingsField( + "default", enum_resolver=uv_workflow_enum, + title="UV Tile Settings", + description=("Set UV workflow when " + "creating new project.") + ) + import_cameras: bool = SettingsField( + True, title="Import Cameras", + description="Import cameras from the mesh file.") + preserve_strokes: bool = SettingsField( + True, title="Preserve Strokes", + description=("Preserve strokes positions on mesh.\n" + "(only relevant when loading into " + "existing project)") + ) + + class ShelvesSettingsModel(BaseSettingsModel): _layout = "compact" name: str = SettingsField(title="Name") @@ -17,9 +89,14 @@ class SubstancePainterSettings(BaseSettingsModel): default_factory=list, title="Shelves" ) + project_templates: list[ProjectTemplatesModel] = SettingsField( + default_factory=ProjectTemplatesModel, + title="Project Templates" + ) DEFAULT_SPAINTER_SETTINGS = { "imageio": DEFAULT_IMAGEIO_SETTINGS, - "shelves": [] + "shelves": [], + "project_templates": [], } diff --git a/server_addon/substancepainter/server/version.py b/server_addon/substancepainter/server/version.py index 3dc1f76bc69..485f44ac21b 100644 --- a/server_addon/substancepainter/server/version.py +++ b/server_addon/substancepainter/server/version.py @@ -1 +1 @@ -__version__ = "0.1.0" +__version__ = "0.1.1" From 4bed0526a580b98088e56e956ce8a2bc83b63407 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 17 Apr 2024 03:25:08 +0000 Subject: [PATCH 143/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 1bbe6e4d09a..3ee0bb9eec8 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.6" +__version__ = "3.18.9-nightly.7" From 25ea4b7ef9b59674ad23dcd982d4e195e845d9a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 17 Apr 2024 03:25:40 +0000 Subject: [PATCH 144/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d41c6151ad6..102b391522b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.7 - 3.18.9-nightly.6 - 3.18.9-nightly.5 - 3.18.9-nightly.4 @@ -134,7 +135,6 @@ body: - 3.16.0-nightly.1 - 3.15.12 - 3.15.12-nightly.4 - - 3.15.12-nightly.3 validations: required: true - type: dropdown From 9125d1d49bf3ed64e9425970dedd949263e8ed12 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 18 Apr 2024 14:00:47 +0800 Subject: [PATCH 145/163] backported the changes to OP --- .../plugins/load/load_mesh.py | 142 +++++++++++------- .../server/settings/load_plugins.py | 120 +++++++++++++++ .../substancepainter/server/settings/main.py | 81 +--------- 3 files changed, 213 insertions(+), 130 deletions(-) create mode 100644 server_addon/substancepainter/server/settings/load_plugins.py diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index fb4f348c1f9..0e3c2817edc 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -1,4 +1,4 @@ -from openpype.lib import BoolDef, EnumDef +from qtpy import QtWidgets, QtCore from openpype.pipeline import ( load, get_representation_path, @@ -14,6 +14,79 @@ import substance_painter.project +def _convert(substance_attr): + """Return Substance Painter Python API Project attribute from string. + + This converts a string like "ProjectWorkflow.Default" to for example + the Substance Painter Python API equivalent object, like: + `substance_painter.project.ProjectWorkflow.Default` + + Args: + substance_attr (str): The `substance_painter.project` attribute, + for example "ProjectWorkflow.Default" + + Returns: + Any: Substance Python API object of the project attribute. + + Raises: + ValueError: If attribute does not exist on the + `substance_painter.project` python api. + """ + root = substance_painter.project + for attr in substance_attr.split("."): + root = getattr(root, attr, None) + if root is None: + raise ValueError( + f"Substance Painter project attribute does not exist: {substance_attr}") + + return root + + +def get_template_by_name(name: str, templates: list[dict]) -> dict: + return next( + template for template in templates + if template["name"] == name + ) + + +class SubstanceProjectConfigurationWindow(QtWidgets.QDialog): + """The pop-up dialog allows users to choose material + duplicate options for importing Max objects when updating + or switching assets. + """ + def __init__(self, project_templates): + super(SubstanceProjectConfigurationWindow, self).__init__() + self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) + + self.template_name = None + self.project_templates = project_templates + + self.widgets = { + "label": QtWidgets.QLabel("Project Configuration"), + "template_options": QtWidgets.QComboBox(), + "buttons": QtWidgets.QWidget(), + "okButton": QtWidgets.QPushButton("Ok"), + } + for template in project_templates: + self.widgets["template_options"].addItem(template) + # Build buttons. + layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) + layout.addWidget(self.widgets["template_options"]) + layout.addWidget(self.widgets["okButton"]) + # Build layout. + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.widgets["label"]) + layout.addWidget(self.widgets["buttons"]) + + self.widgets["okButton"].pressed.connect(self.on_ok_pressed) + + def on_ok_pressed(self): + self.template_name = ( + self.widgets["template_options"].currentText() + ) + self.close() + + class SubstanceLoadProjectMesh(load.LoaderPlugin): """Load mesh for project""" @@ -25,69 +98,32 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): icon = "code-fork" color = "orange" - @classmethod - def get_options(cls, contexts): - project_uv_workflow_items = { - substance_painter.project.ProjectWorkflow.Default: "default", - substance_painter.project.ProjectWorkflow.UVTile: "uvTile", - substance_painter.project.ProjectWorkflow.TextureSetPerUVTile: "textureSetPerUVTile" # noqa - } - return [ - BoolDef("preserve_strokes", - default=True, - label="Preserve Strokes", - tooltip=("Preserve strokes positions on mesh.\n" - "(only relevant when loading into " - "existing project)")), - BoolDef("import_cameras", - default=True, - label="Import Cameras", - tooltip="Import cameras from the mesh file."), - EnumDef("texture_resolution", - items=[128, 256, 512, 1024, 2048, 4096], - default=1024, - label="Texture Resolution", - tooltip=("Set texture resolution " - "when creating new project")), - EnumDef("project_uv_workflow", - items=project_uv_workflow_items, - default="default", - label="UV Workflow", - tooltip="Set UV workflow when creating new project") - ] - def load(self, context, name, namespace, options=None): # Get user inputs - import_cameras = options.get("import_cameras", True) - preserve_strokes = options.get("preserve_strokes", True) - texture_resolution = options.get("texture_resolution", 1024) - uv_workflow = options.get("project_uv_workflow", "default") + template_enum = [template["name"] for template in self.project_templates] + window = SubstanceProjectConfigurationWindow(template_enum) + window.exec_() + template_name = window.template_name + template = get_template_by_name(template_name, self.project_templates) sp_settings = substance_painter.project.Settings( - default_texture_resolution=texture_resolution, - import_cameras=import_cameras, - project_workflow=uv_workflow + normal_map_format=_convert(template["normal_map_format"]), + project_workflow=_convert(template["project_workflow"]), + tangent_space_mode=_convert(template["tangent_space_mode"]), + default_texture_resolution=template["default_texture_resolution"] ) if not substance_painter.project.is_open(): # Allow to 'initialize' a new project path = self.filepath_from_context(context) - # TODO: improve the prompt dialog function to not - # only works for simple polygon scene - result = prompt_new_file_with_mesh(mesh_filepath=path) - if not result: - self.log.info("User cancelled new project prompt." - "Creating new project directly from" - " Substance Painter API Instead.") - settings = substance_painter.project.create( - mesh_file_path=path, settings=sp_settings - ) + settings = substance_painter.project.create( + mesh_file_path=path, settings=sp_settings + ) else: # Reload the mesh settings = substance_painter.project.MeshReloadingSettings( - import_cameras=import_cameras, - preserve_strokes=preserve_strokes - ) + import_cameras=template["import_cameras"], + preserve_strokes=template["preserve_strokes"]) def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa if status == substance_painter.project.ReloadMeshStatus.SUCCESS: # noqa @@ -113,7 +149,7 @@ def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa # from the user's original choice. We don't store 'preserve_strokes' # as we always preserve strokes on updates. container["options"] = { - "import_cameras": import_cameras, + "import_cameras": template["import_cameras"], } set_container_metadata(project_mesh_object_name, container) diff --git a/server_addon/substancepainter/server/settings/load_plugins.py b/server_addon/substancepainter/server/settings/load_plugins.py new file mode 100644 index 00000000000..ed9b6f0d640 --- /dev/null +++ b/server_addon/substancepainter/server/settings/load_plugins.py @@ -0,0 +1,120 @@ +from ayon_server.settings import BaseSettingsModel, SettingsField + + +def normal_map_format_enum(): + return [ + {"label": "DirectX", "value": "NormalMapFormat.DirectX"}, + {"label": "OpenGL", "value": "NormalMapFormat.OpenGL"}, + ] + + +def tangent_space_enum(): + return [ + {"label": "Per Fragment", "value": "TangentSpace.PerFragment"}, + {"label": "Per Vertex", "value": "TangentSpace.PerVertex"}, + ] + + +def uv_workflow_enum(): + return [ + {"label": "Default", "value": "ProjectWorkflow.Default"}, + {"label": "UV Tile", "value": "ProjectWorkflow.UVTile"}, + {"label": "Texture Set Per UV Tile", + "value": "ProjectWorkflow.TextureSetPerUVTile"} + ] + + +def document_resolution_enum(): + return [ + {"label": "128", "value": 128}, + {"label": "256", "value": 256}, + {"label": "512", "value": 512}, + {"label": "1024", "value": 1024}, + {"label": "2048", "value": 2048}, + {"label": "4096", "value": 4096} + ] + + +class ProjectTemplatesModel(BaseSettingsModel): + _layout = "expanded" + name: str = SettingsField("default", title="Template Name") + default_texture_resolution: int = SettingsField( + 1024, enum_resolver=document_resolution_enum, + title="Document Resolution", + description=("Set texture resolution when " + "creating new project.") + ) + import_cameras: bool = SettingsField( + True, title="Import Cameras", + description="Import cameras from the mesh file.") + normal_map_format: str = SettingsField( + "DirectX", enum_resolver=normal_map_format_enum, + title="Normal Map Format", + description=("Set normal map format when " + "creating new project.") + ) + project_workflow: str = SettingsField( + "Default", enum_resolver=uv_workflow_enum, + title="UV Tile Settings", + description=("Set UV workflow when " + "creating new project.") + ) + tangent_space_mode: str = SettingsField( + "PerFragment", enum_resolver=tangent_space_enum, + title="Tangent Space", + description=("An option to compute tangent space " + "when creating new project.") + ) + preserve_strokes: bool = SettingsField( + True, title="Preserve Strokes", + description=("Preserve strokes positions on mesh.\n" + "(only relevant when loading into " + "existing project)") + ) + + +class ProjectTemplateSettingModel(BaseSettingsModel): + project_templates: list[ProjectTemplatesModel] = SettingsField( + default_factory=ProjectTemplatesModel, + title="Project Templates" +) + + +class LoadersModel(BaseSettingsModel): + SubstanceLoadProjectMesh: ProjectTemplateSettingModel = SettingsField( + default_factory=ProjectTemplateSettingModel, + title="Load Mesh" + ) + + +DEFAULT_LOADER_SETTINGS = { + "SubstanceLoadProjectMesh":{ + "project_templates": [{ + "name": "2K(Default)", + "default_texture_resolution": 2048, + "import_cameras": True, + "normal_map_format": "NormalMapFormat.DirectX", + "project_workflow": "ProjectWorkflow.Default", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True + }, + { + "name": "2K(UV tile)", + "default_texture_resolution": 2048, + "import_cameras": True, + "normal_map_format": "NormalMapFormat.DirectX", + "project_workflow": "ProjectWorkflow.UVTile", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True + }, + { + "name": "4K(Custom)", + "default_texture_resolution": 4096, + "import_cameras": True, + "normal_map_format": "NormalMapFormat.OpenGL", + "project_workflow": "ProjectWorkflow.UVTile", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True + }] + } +} diff --git a/server_addon/substancepainter/server/settings/main.py b/server_addon/substancepainter/server/settings/main.py index 20cf6d77b22..93523fd6507 100644 --- a/server_addon/substancepainter/server/settings/main.py +++ b/server_addon/substancepainter/server/settings/main.py @@ -1,77 +1,6 @@ from ayon_server.settings import BaseSettingsModel, SettingsField from .imageio import ImageIOSettings, DEFAULT_IMAGEIO_SETTINGS - - -def normal_map_format_enum(): - return [ - {"label": "DirectX", "value": "DirectX"}, - {"label": "OpenGL", "value": "OpenGL"}, - ] - - -def tangent_space_enum(): - return [ - {"label": "PerFragment", "value": "PerFragment"}, - {"label": "PerVertex", "value": "PerVertex"}, - ] - - -def uv_workflow_enum(): - return [ - {"label": "Default", "value": "default"}, - {"label": "UV Tile", "value": "uvTile"}, - {"label": "Texture Set Per UV Tile", - "value": "textureSetPerUVTile"} - ] - - -def document_resolution_enum(): - return [ - {"label": "128", "value": 128}, - {"label": "256", "value": 256}, - {"label": "512", "value": 512}, - {"label": "1024", "value": 1024}, - {"label": "2048", "value": 2048}, - {"label": "4096", "value": 4096} - ] - - -class ProjectTemplatesModel(BaseSettingsModel): - _layout = "expanded" - name: str = SettingsField(title="Template Name") - document_resolution: int = SettingsField( - 1024, enum_resolver=document_resolution_enum, - title="Document Resolution", - description=("Set texture resolution when " - "creating new project.") - ) - normal_map_format: str = SettingsField( - "DirectX", enum_resolver=normal_map_format_enum, - title="Normal Map Format", - description=("Set normal map format when " - "creating new project.") - ) - tangent_space: str = SettingsField( - "PerFragment", enum_resolver=tangent_space_enum, - title="Tangent Space", - description=("An option to compute tangent space " - "when creating new project.") - ) - uv_workflow: str = SettingsField( - "default", enum_resolver=uv_workflow_enum, - title="UV Tile Settings", - description=("Set UV workflow when " - "creating new project.") - ) - import_cameras: bool = SettingsField( - True, title="Import Cameras", - description="Import cameras from the mesh file.") - preserve_strokes: bool = SettingsField( - True, title="Preserve Strokes", - description=("Preserve strokes positions on mesh.\n" - "(only relevant when loading into " - "existing project)") - ) +from .load_plugins import LoadersModel, DEFAULT_LOADER_SETTINGS class ShelvesSettingsModel(BaseSettingsModel): @@ -89,14 +18,12 @@ class SubstancePainterSettings(BaseSettingsModel): default_factory=list, title="Shelves" ) - project_templates: list[ProjectTemplatesModel] = SettingsField( - default_factory=ProjectTemplatesModel, - title="Project Templates" - ) + load: LoadersModel = SettingsField( + default_factory=DEFAULT_LOADER_SETTINGS, title="Loaders") DEFAULT_SPAINTER_SETTINGS = { "imageio": DEFAULT_IMAGEIO_SETTINGS, "shelves": [], - "project_templates": [], + "load": DEFAULT_LOADER_SETTINGS, } From 43f4c8fa70c0da530183e69fcfb96f56ad3d74c7 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 18 Apr 2024 14:50:34 +0800 Subject: [PATCH 146/163] Backported the settings & code to OP --- .../plugins/load/load_mesh.py | 6 +- .../project_settings/substancepainter.json | 35 +++++++++- .../schema_project_substancepainter.json | 64 +++++++++++++++++++ .../server/settings/load_plugins.py | 62 +++++++++--------- 4 files changed, 134 insertions(+), 33 deletions(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 0e3c2817edc..131b6114526 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -37,7 +37,8 @@ def _convert(substance_attr): root = getattr(root, attr, None) if root is None: raise ValueError( - f"Substance Painter project attribute does not exist: {substance_attr}") + "Substance Painter project attribute" + f" does not exist: {substance_attr}") return root @@ -101,7 +102,8 @@ class SubstanceLoadProjectMesh(load.LoaderPlugin): def load(self, context, name, namespace, options=None): # Get user inputs - template_enum = [template["name"] for template in self.project_templates] + template_enum = [template["name"] for template + in self.project_templates] window = SubstanceProjectConfigurationWindow(template_enum) window.exec_() template_name = window.template_name diff --git a/openpype/settings/defaults/project_settings/substancepainter.json b/openpype/settings/defaults/project_settings/substancepainter.json index 2f9344d435c..f601814fdcf 100644 --- a/openpype/settings/defaults/project_settings/substancepainter.json +++ b/openpype/settings/defaults/project_settings/substancepainter.json @@ -10,5 +10,38 @@ "rules": {} } }, - "shelves": {} + "shelves": {}, + "load": { + "SubstanceLoadProjectMesh": { + "project_templates": [ + { + "name": "2K(Default)", + "default_texture_resolution": 2048, + "import_cameras": true, + "normal_map_format": "NormalMapFormat.DirectX", + "project_workflow": "ProjectWorkflow.Default", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": true + }, + { + "name": "2K(UV tile)", + "default_texture_resolution": 2048, + "import_cameras": true, + "normal_map_format": "NormalMapFormat.DirectX", + "project_workflow": "ProjectWorkflow.UVTile", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": true + }, + { + "name": "4K(Custom)", + "default_texture_resolution": 4096, + "import_cameras": true, + "normal_map_format": "NormalMapFormat.OpenGL", + "project_workflow": "ProjectWorkflow.UVTile", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": true + } + ] + } + } } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json b/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json index 6be8cecad3c..d143aa0820c 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json @@ -25,6 +25,70 @@ "object_type": { "type": "text" } + }, + { + "type": "dict", + "collapsible": true, + "key": "load", + "label": "Loaders", + "use_label_wrap": true, + "children": [ + { + "type": "dict", + "collapsible": true, + "key": "SubstanceLoadProjectMesh", + "label": "Load Mesh", + "children": [ + { + "type": "list", + "collapsible": true, + "key": "project_templates", + "label": "Project Templates", + "object_type": { + "type": "dict", + "children": [ + { + "type": "text", + "key": "name", + "label": "Name" + }, + { + "type": "number", + "key": "default_texture_resolution", + "label": "Document Resolution" + }, + { + "type": "boolean", + "key": "import_cameras", + "label": "Import Cameras" + }, + { + "type": "text", + "key": "normal_map_format", + "label": "Normal Map Format" + }, + { + "type": "text", + "key": "project_workflow", + "label": "UV Tile Settings" + }, + { + "type": "text", + "key": "tangent_space_mode", + "label": "Normal Map Format" + }, + { + "type": "boolean", + "key": "preserve_strokes", + "label": "Preserve Strokes" + } + ] + } + } + ] + } + + ] } ] } diff --git a/server_addon/substancepainter/server/settings/load_plugins.py b/server_addon/substancepainter/server/settings/load_plugins.py index ed9b6f0d640..e6b2fd86c3b 100644 --- a/server_addon/substancepainter/server/settings/load_plugins.py +++ b/server_addon/substancepainter/server/settings/load_plugins.py @@ -74,10 +74,10 @@ class ProjectTemplatesModel(BaseSettingsModel): class ProjectTemplateSettingModel(BaseSettingsModel): - project_templates: list[ProjectTemplatesModel] = SettingsField( + project_templates: list[ProjectTemplatesModel] = SettingsField( default_factory=ProjectTemplatesModel, title="Project Templates" -) + ) class LoadersModel(BaseSettingsModel): @@ -88,33 +88,35 @@ class LoadersModel(BaseSettingsModel): DEFAULT_LOADER_SETTINGS = { - "SubstanceLoadProjectMesh":{ - "project_templates": [{ - "name": "2K(Default)", - "default_texture_resolution": 2048, - "import_cameras": True, - "normal_map_format": "NormalMapFormat.DirectX", - "project_workflow": "ProjectWorkflow.Default", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": True - }, - { - "name": "2K(UV tile)", - "default_texture_resolution": 2048, - "import_cameras": True, - "normal_map_format": "NormalMapFormat.DirectX", - "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": True - }, - { - "name": "4K(Custom)", - "default_texture_resolution": 4096, - "import_cameras": True, - "normal_map_format": "NormalMapFormat.OpenGL", - "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": True - }] + "SubstanceLoadProjectMesh": { + "project_templates": [ + { + "name": "2K(Default)", + "default_texture_resolution": 2048, + "import_cameras": True, + "normal_map_format": "NormalMapFormat.DirectX", + "project_workflow": "ProjectWorkflow.Default", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True + }, + { + "name": "2K(UV tile)", + "default_texture_resolution": 2048, + "import_cameras": True, + "normal_map_format": "NormalMapFormat.DirectX", + "project_workflow": "ProjectWorkflow.UVTile", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True + }, + { + "name": "4K(Custom)", + "default_texture_resolution": 4096, + "import_cameras": True, + "normal_map_format": "NormalMapFormat.OpenGL", + "project_workflow": "ProjectWorkflow.UVTile", + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True + } + ] } } From 9db5782624cb2044b563c40f8cec93ca62799012 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 18 Apr 2024 22:03:30 +0800 Subject: [PATCH 147/163] Backported the settings & code to OP --- openpype/hosts/maya/api/fbx.py | 2 +- .../plugins/load/load_mesh.py | 29 +++++++++++++++---- .../project_settings/substancepainter.json | 12 ++------ .../schema_project_substancepainter.json | 10 ------- .../server/settings/load_plugins.py | 21 ++------------ 5 files changed, 30 insertions(+), 44 deletions(-) diff --git a/openpype/hosts/maya/api/fbx.py b/openpype/hosts/maya/api/fbx.py index c8f4050bc13..0b433c7ba22 100644 --- a/openpype/hosts/maya/api/fbx.py +++ b/openpype/hosts/maya/api/fbx.py @@ -49,7 +49,7 @@ def options(self): "smoothMesh": bool, "instances": bool, # "referencedContainersContent": bool, # deprecated in Maya 2016+ - "bakeComplexAnimation": int, + "bakeComplexAnimation": bool, "bakeComplexStart": int, "bakeComplexEnd": int, "bakeComplexStep": int, diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 131b6114526..11d0589fd2f 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -59,29 +59,44 @@ def __init__(self, project_templates): super(SubstanceProjectConfigurationWindow, self).__init__() self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) + self.import_cameras = False + self.preserve_strokes = False self.template_name = None self.project_templates = project_templates self.widgets = { "label": QtWidgets.QLabel("Project Configuration"), "template_options": QtWidgets.QComboBox(), - "buttons": QtWidgets.QWidget(), + "import_cameras": QtWidgets.QCheckBox("Improve Cameras"), + "preserve_strokes": QtWidgets.QCheckBox("Preserve Strokes"), + "clickbox": QtWidgets.QWidget(), + "combobox": QtWidgets.QWidget(), "okButton": QtWidgets.QPushButton("Ok"), } for template in project_templates: self.widgets["template_options"].addItem(template) + + # Build clickboxes + layout = QtWidgets.QHBoxLayout(self.widgets["clickbox"]) + layout.addWidget(self.widgets["import_cameras"]) + layout.addWidget(self.widgets["preserve_strokes"]) # Build buttons. - layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) + layout = QtWidgets.QHBoxLayout(self.widgets["combobox"]) layout.addWidget(self.widgets["template_options"]) layout.addWidget(self.widgets["okButton"]) # Build layout. layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.widgets["label"]) - layout.addWidget(self.widgets["buttons"]) + layout.addWidget(self.widgets["clickbox"]) + layout.addWidget(self.widgets["combobox"]) self.widgets["okButton"].pressed.connect(self.on_ok_pressed) def on_ok_pressed(self): + if self.widgets["import_cameras"].isChecked(): + self.import_cameras = True + if self.widgets["preserve_strokes"].isChecked(): + self.preserve_strokes = True self.template_name = ( self.widgets["template_options"].currentText() ) @@ -107,6 +122,8 @@ def load(self, context, name, namespace, options=None): window = SubstanceProjectConfigurationWindow(template_enum) window.exec_() template_name = window.template_name + import_cameras = window.import_cameras + preserve_strokes = window.preserve_strokes template = get_template_by_name(template_name, self.project_templates) sp_settings = substance_painter.project.Settings( normal_map_format=_convert(template["normal_map_format"]), @@ -124,8 +141,8 @@ def load(self, context, name, namespace, options=None): else: # Reload the mesh settings = substance_painter.project.MeshReloadingSettings( - import_cameras=template["import_cameras"], - preserve_strokes=template["preserve_strokes"]) + import_cameras=import_cameras, + preserve_strokes=preserve_strokes) def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa if status == substance_painter.project.ReloadMeshStatus.SUCCESS: # noqa @@ -151,7 +168,7 @@ def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa # from the user's original choice. We don't store 'preserve_strokes' # as we always preserve strokes on updates. container["options"] = { - "import_cameras": template["import_cameras"], + "import_cameras": import_cameras, } set_container_metadata(project_mesh_object_name, container) diff --git a/openpype/settings/defaults/project_settings/substancepainter.json b/openpype/settings/defaults/project_settings/substancepainter.json index f601814fdcf..173b65f5b07 100644 --- a/openpype/settings/defaults/project_settings/substancepainter.json +++ b/openpype/settings/defaults/project_settings/substancepainter.json @@ -17,29 +17,23 @@ { "name": "2K(Default)", "default_texture_resolution": 2048, - "import_cameras": true, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.Default", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": true + "tangent_space_mode": "TangentSpace.PerFragment" }, { "name": "2K(UV tile)", "default_texture_resolution": 2048, - "import_cameras": true, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": true + "tangent_space_mode": "TangentSpace.PerFragment" }, { "name": "4K(Custom)", "default_texture_resolution": 4096, - "import_cameras": true, "normal_map_format": "NormalMapFormat.OpenGL", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": true + "tangent_space_mode": "TangentSpace.PerFragment" } ] } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json b/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json index d143aa0820c..f1b305c27c7 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json @@ -57,11 +57,6 @@ "key": "default_texture_resolution", "label": "Document Resolution" }, - { - "type": "boolean", - "key": "import_cameras", - "label": "Import Cameras" - }, { "type": "text", "key": "normal_map_format", @@ -76,11 +71,6 @@ "type": "text", "key": "tangent_space_mode", "label": "Normal Map Format" - }, - { - "type": "boolean", - "key": "preserve_strokes", - "label": "Preserve Strokes" } ] } diff --git a/server_addon/substancepainter/server/settings/load_plugins.py b/server_addon/substancepainter/server/settings/load_plugins.py index e6b2fd86c3b..e5519c97730 100644 --- a/server_addon/substancepainter/server/settings/load_plugins.py +++ b/server_addon/substancepainter/server/settings/load_plugins.py @@ -44,9 +44,6 @@ class ProjectTemplatesModel(BaseSettingsModel): description=("Set texture resolution when " "creating new project.") ) - import_cameras: bool = SettingsField( - True, title="Import Cameras", - description="Import cameras from the mesh file.") normal_map_format: str = SettingsField( "DirectX", enum_resolver=normal_map_format_enum, title="Normal Map Format", @@ -65,12 +62,6 @@ class ProjectTemplatesModel(BaseSettingsModel): description=("An option to compute tangent space " "when creating new project.") ) - preserve_strokes: bool = SettingsField( - True, title="Preserve Strokes", - description=("Preserve strokes positions on mesh.\n" - "(only relevant when loading into " - "existing project)") - ) class ProjectTemplateSettingModel(BaseSettingsModel): @@ -93,29 +84,23 @@ class LoadersModel(BaseSettingsModel): { "name": "2K(Default)", "default_texture_resolution": 2048, - "import_cameras": True, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.Default", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": True + "tangent_space_mode": "TangentSpace.PerFragment" }, { "name": "2K(UV tile)", "default_texture_resolution": 2048, - "import_cameras": True, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": True + "tangent_space_mode": "TangentSpace.PerFragment" }, { "name": "4K(Custom)", "default_texture_resolution": 4096, - "import_cameras": True, "normal_map_format": "NormalMapFormat.OpenGL", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment", - "preserve_strokes": True + "tangent_space_mode": "TangentSpace.PerFragment" } ] } From 7cb7296dd051b51a9d38bee8838a47c692260a3a Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Thu, 18 Apr 2024 22:07:21 +0800 Subject: [PATCH 148/163] backported the setting to OP --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 11d0589fd2f..641b57e7840 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -67,7 +67,7 @@ def __init__(self, project_templates): self.widgets = { "label": QtWidgets.QLabel("Project Configuration"), "template_options": QtWidgets.QComboBox(), - "import_cameras": QtWidgets.QCheckBox("Improve Cameras"), + "import_cameras": QtWidgets.QCheckBox("Import Cameras"), "preserve_strokes": QtWidgets.QCheckBox("Preserve Strokes"), "clickbox": QtWidgets.QWidget(), "combobox": QtWidgets.QWidget(), From 8cbd6ba221599307f8ae5ccd93d65abc5ef19a53 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 19 Apr 2024 19:40:03 +0800 Subject: [PATCH 149/163] backported the code from ayon-core to OP --- .../plugins/load/load_mesh.py | 110 ++++++++++++------ 1 file changed, 73 insertions(+), 37 deletions(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 641b57e7840..a86e762bd71 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -59,90 +59,125 @@ def __init__(self, project_templates): super(SubstanceProjectConfigurationWindow, self).__init__() self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint) - self.import_cameras = False - self.preserve_strokes = False - self.template_name = None + self.configuration = None + self.template_names = [template["name"] for template + in project_templates] self.project_templates = project_templates self.widgets = { - "label": QtWidgets.QLabel("Project Configuration"), + "label": QtWidgets.QLabel( + "Select your template for project configuration"), "template_options": QtWidgets.QComboBox(), "import_cameras": QtWidgets.QCheckBox("Import Cameras"), "preserve_strokes": QtWidgets.QCheckBox("Preserve Strokes"), "clickbox": QtWidgets.QWidget(), "combobox": QtWidgets.QWidget(), - "okButton": QtWidgets.QPushButton("Ok"), + "buttons": QtWidgets.QDialogButtonBox( + QtWidgets.QDialogButtonBox.Ok + | QtWidgets.QDialogButtonBox.Cancel) } - for template in project_templates: - self.widgets["template_options"].addItem(template) + self.widgets["template_options"].addItems(self.template_names) + + template_name = self.widgets["template_options"].currentText() + self._update_to_match_template(template_name) # Build clickboxes layout = QtWidgets.QHBoxLayout(self.widgets["clickbox"]) layout.addWidget(self.widgets["import_cameras"]) layout.addWidget(self.widgets["preserve_strokes"]) - # Build buttons. + # Build combobox layout = QtWidgets.QHBoxLayout(self.widgets["combobox"]) layout.addWidget(self.widgets["template_options"]) - layout.addWidget(self.widgets["okButton"]) + # Build buttons + layout = QtWidgets.QHBoxLayout(self.widgets["buttons"]) # Build layout. layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.widgets["label"]) - layout.addWidget(self.widgets["clickbox"]) layout.addWidget(self.widgets["combobox"]) + layout.addWidget(self.widgets["clickbox"]) + layout.addWidget(self.widgets["buttons"]) - self.widgets["okButton"].pressed.connect(self.on_ok_pressed) + self.widgets["template_options"].currentTextChanged.connect( + self._update_to_match_template) + self.widgets["buttons"].accepted.connect(self.on_accept) + self.widgets["buttons"].rejected.connect(self.on_reject) - def on_ok_pressed(self): - if self.widgets["import_cameras"].isChecked(): - self.import_cameras = True - if self.widgets["preserve_strokes"].isChecked(): - self.preserve_strokes = True - self.template_name = ( - self.widgets["template_options"].currentText() - ) + def on_accept(self): + self.configuration = self.get_project_configuration() + self.close() + + def on_reject(self): self.close() + def _update_to_match_template(self, template_name): + template = get_template_by_name(template_name, self.project_templates) + self.widgets["import_cameras"].setChecked(template["import_cameras"]) + self.widgets["preserve_strokes"].setChecked( + template["preserve_strokes"]) + + def get_project_configuration(self): + templates = self.project_templates + template_name = self.widgets["template_options"].currentText() + template = get_template_by_name(template_name, templates) + template = copy.deepcopy(template) # do not edit the original + template["import_cameras"] = self.widgets["import_cameras"].isChecked() + template["preserve_strokes"] = self.widgets["preserve_strokes"].isChecked() + for key in ["normal_map_format", + "project_workflow", + "tangent_space_mode"]: + template[key] = _convert(template[key]) + return template + + @classmethod + def prompt(cls, templates): + dialog = cls(templates) + dialog.exec_() + dialog.deleteLater() + + return dialog.configuration + class SubstanceLoadProjectMesh(load.LoaderPlugin): """Load mesh for project""" - families = ["*"] - representations = ["abc", "fbx", "obj", "gltf"] + product_types = {"*"} + representations = {"abc", "fbx", "obj", "gltf", "usd", "usda", "usdc"} label = "Load mesh" order = -10 icon = "code-fork" color = "orange" + # Defined via settings + project_templates = [] + def load(self, context, name, namespace, options=None): # Get user inputs - template_enum = [template["name"] for template - in self.project_templates] - window = SubstanceProjectConfigurationWindow(template_enum) - window.exec_() - template_name = window.template_name - import_cameras = window.import_cameras - preserve_strokes = window.preserve_strokes - template = get_template_by_name(template_name, self.project_templates) + result = SubstanceProjectConfigurationWindow.prompt( + self.project_templates) + if not result: + # cancelling loader action + return sp_settings = substance_painter.project.Settings( - normal_map_format=_convert(template["normal_map_format"]), - project_workflow=_convert(template["project_workflow"]), - tangent_space_mode=_convert(template["tangent_space_mode"]), - default_texture_resolution=template["default_texture_resolution"] + import_cameras=result["import_cameras"], + normal_map_format=result["normal_map_format"], + project_workflow=result["project_workflow"], + tangent_space_mode=result["tangent_space_mode"], + default_texture_resolution=result["default_texture_resolution"] ) if not substance_painter.project.is_open(): # Allow to 'initialize' a new project path = self.filepath_from_context(context) + settings = substance_painter.project.create( mesh_file_path=path, settings=sp_settings ) - else: # Reload the mesh settings = substance_painter.project.MeshReloadingSettings( - import_cameras=import_cameras, - preserve_strokes=preserve_strokes) + import_cameras=result["import_cameras"], + preserve_strokes=result["preserve_strokes"]) def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa if status == substance_painter.project.ReloadMeshStatus.SUCCESS: # noqa @@ -167,8 +202,9 @@ def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa # We want store some options for updating to keep consistent behavior # from the user's original choice. We don't store 'preserve_strokes' # as we always preserve strokes on updates. + # TODO: update the code container["options"] = { - "import_cameras": import_cameras, + "import_cameras": result["import_cameras"], } set_container_metadata(project_mesh_object_name, container) From dcdb47cafa502ee13a2b09363facc2713e8ed9c0 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 19 Apr 2024 19:42:30 +0800 Subject: [PATCH 150/163] hound shut --- .../hosts/substancepainter/plugins/load/load_mesh.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index a86e762bd71..a8384f62499 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -1,3 +1,4 @@ +import copy from qtpy import QtWidgets, QtCore from openpype.pipeline import ( load, @@ -121,10 +122,12 @@ def get_project_configuration(self): template = get_template_by_name(template_name, templates) template = copy.deepcopy(template) # do not edit the original template["import_cameras"] = self.widgets["import_cameras"].isChecked() - template["preserve_strokes"] = self.widgets["preserve_strokes"].isChecked() + template["preserve_strokes"] = ( + self.widgets["preserve_strokes"].isChecked() + ) for key in ["normal_map_format", - "project_workflow", - "tangent_space_mode"]: + "project_workflow", + "tangent_space_mode"]: template[key] = _convert(template[key]) return template From a0effbe2d0d81d2dd108d4ab6442883ad9bf7aac Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 19 Apr 2024 19:46:30 +0800 Subject: [PATCH 151/163] hound shut --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index a8384f62499..746fa58a690 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -120,7 +120,7 @@ def get_project_configuration(self): templates = self.project_templates template_name = self.widgets["template_options"].currentText() template = get_template_by_name(template_name, templates) - template = copy.deepcopy(template) # do not edit the original + template = copy.deepcopy(template) # do not edit the original template["import_cameras"] = self.widgets["import_cameras"].isChecked() template["preserve_strokes"] = ( self.widgets["preserve_strokes"].isChecked() From bb1fa1a7fd13f6c2d9882895112f88189fd0fed9 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:24:28 +0800 Subject: [PATCH 152/163] Update openpype/hosts/substancepainter/plugins/load/load_mesh.py Co-authored-by: Roy Nieterau --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 746fa58a690..78616d7fea3 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -135,9 +135,9 @@ def get_project_configuration(self): def prompt(cls, templates): dialog = cls(templates) dialog.exec_() + configuration = dialog.configuration dialog.deleteLater() - - return dialog.configuration + return configuration class SubstanceLoadProjectMesh(load.LoaderPlugin): From b395e725cabb232dc53950ac7e92bdca95fdc0db Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:24:39 +0800 Subject: [PATCH 153/163] Update openpype/hosts/substancepainter/plugins/load/load_mesh.py Co-authored-by: Roy Nieterau --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 78616d7fea3..a329e7c3e52 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -120,7 +120,7 @@ def get_project_configuration(self): templates = self.project_templates template_name = self.widgets["template_options"].currentText() template = get_template_by_name(template_name, templates) - template = copy.deepcopy(template) # do not edit the original + template = copy.deepcopy(template) # do not edit the original template["import_cameras"] = self.widgets["import_cameras"].isChecked() template["preserve_strokes"] = ( self.widgets["preserve_strokes"].isChecked() From a8661b84fc322b425e42b9b04ab811e2b1ebb428 Mon Sep 17 00:00:00 2001 From: Kayla Man <64118225+moonyuet@users.noreply.github.com> Date: Fri, 19 Apr 2024 20:24:52 +0800 Subject: [PATCH 154/163] Update openpype/hosts/substancepainter/plugins/load/load_mesh.py Co-authored-by: Roy Nieterau --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index a329e7c3e52..7cdd8e8d162 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -205,7 +205,6 @@ def on_mesh_reload(status: substance_painter.project.ReloadMeshStatus): # noqa # We want store some options for updating to keep consistent behavior # from the user's original choice. We don't store 'preserve_strokes' # as we always preserve strokes on updates. - # TODO: update the code container["options"] = { "import_cameras": result["import_cameras"], } From 3168a3cb5f0c19b81f967311fe3c93fe37a2dd5b Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 19 Apr 2024 20:27:50 +0800 Subject: [PATCH 155/163] code tweak based on OP & restore unrelated commit --- openpype/hosts/maya/api/fbx.py | 2 +- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/fbx.py b/openpype/hosts/maya/api/fbx.py index 0b433c7ba22..c8f4050bc13 100644 --- a/openpype/hosts/maya/api/fbx.py +++ b/openpype/hosts/maya/api/fbx.py @@ -49,7 +49,7 @@ def options(self): "smoothMesh": bool, "instances": bool, # "referencedContainersContent": bool, # deprecated in Maya 2016+ - "bakeComplexAnimation": bool, + "bakeComplexAnimation": int, "bakeComplexStart": int, "bakeComplexEnd": int, "bakeComplexStep": int, diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 7cdd8e8d162..2422e234992 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -143,8 +143,8 @@ def prompt(cls, templates): class SubstanceLoadProjectMesh(load.LoaderPlugin): """Load mesh for project""" - product_types = {"*"} - representations = {"abc", "fbx", "obj", "gltf", "usd", "usda", "usdc"} + families = ["*"] + representations = ["abc", "fbx", "obj", "gltf"] label = "Load mesh" order = -10 From ce70098d228d2b3fbe7c22f7265ef1c33ef1a5a2 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Fri, 19 Apr 2024 21:43:13 +0800 Subject: [PATCH 156/163] move sp_setting into if condition --- openpype/hosts/substancepainter/plugins/load/load_mesh.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/openpype/hosts/substancepainter/plugins/load/load_mesh.py b/openpype/hosts/substancepainter/plugins/load/load_mesh.py index 2422e234992..014dbd214f0 100644 --- a/openpype/hosts/substancepainter/plugins/load/load_mesh.py +++ b/openpype/hosts/substancepainter/plugins/load/load_mesh.py @@ -172,7 +172,13 @@ def load(self, context, name, namespace, options=None): if not substance_painter.project.is_open(): # Allow to 'initialize' a new project path = self.filepath_from_context(context) - + sp_settings = substance_painter.project.Settings( + import_cameras=result["import_cameras"], + normal_map_format=result["normal_map_format"], + project_workflow=result["project_workflow"], + tangent_space_mode=result["tangent_space_mode"], + default_texture_resolution=result["default_texture_resolution"] + ) settings = substance_painter.project.create( mesh_file_path=path, settings=sp_settings ) From c7db820402a98a7be419fb974726ae0b51949ad6 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 20 Apr 2024 03:25:21 +0000 Subject: [PATCH 157/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 3ee0bb9eec8..bf1f845dcb7 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.7" +__version__ = "3.18.9-nightly.8" From f6a68a0828c03f77bf8752f6d7a53953ce74bd01 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 20 Apr 2024 03:26:03 +0000 Subject: [PATCH 158/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 102b391522b..de25b489415 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.8 - 3.18.9-nightly.7 - 3.18.9-nightly.6 - 3.18.9-nightly.5 @@ -134,7 +135,6 @@ body: - 3.16.0-nightly.2 - 3.16.0-nightly.1 - 3.15.12 - - 3.15.12-nightly.4 validations: required: true - type: dropdown From 9356578e03f819ab0d728607c51ea9fbf3987223 Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Mon, 22 Apr 2024 18:37:36 +0800 Subject: [PATCH 159/163] update the settings align with the dialog --- .../project_settings/substancepainter.json | 12 ++++++++--- .../schema_project_substancepainter.json | 10 +++++++++ .../server/settings/load_plugins.py | 21 ++++++++++++++++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/openpype/settings/defaults/project_settings/substancepainter.json b/openpype/settings/defaults/project_settings/substancepainter.json index 173b65f5b07..f601814fdcf 100644 --- a/openpype/settings/defaults/project_settings/substancepainter.json +++ b/openpype/settings/defaults/project_settings/substancepainter.json @@ -17,23 +17,29 @@ { "name": "2K(Default)", "default_texture_resolution": 2048, + "import_cameras": true, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.Default", - "tangent_space_mode": "TangentSpace.PerFragment" + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": true }, { "name": "2K(UV tile)", "default_texture_resolution": 2048, + "import_cameras": true, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment" + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": true }, { "name": "4K(Custom)", "default_texture_resolution": 4096, + "import_cameras": true, "normal_map_format": "NormalMapFormat.OpenGL", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment" + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": true } ] } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json b/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json index f1b305c27c7..d143aa0820c 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_substancepainter.json @@ -57,6 +57,11 @@ "key": "default_texture_resolution", "label": "Document Resolution" }, + { + "type": "boolean", + "key": "import_cameras", + "label": "Import Cameras" + }, { "type": "text", "key": "normal_map_format", @@ -71,6 +76,11 @@ "type": "text", "key": "tangent_space_mode", "label": "Normal Map Format" + }, + { + "type": "boolean", + "key": "preserve_strokes", + "label": "Preserve Strokes" } ] } diff --git a/server_addon/substancepainter/server/settings/load_plugins.py b/server_addon/substancepainter/server/settings/load_plugins.py index e5519c97730..e6b2fd86c3b 100644 --- a/server_addon/substancepainter/server/settings/load_plugins.py +++ b/server_addon/substancepainter/server/settings/load_plugins.py @@ -44,6 +44,9 @@ class ProjectTemplatesModel(BaseSettingsModel): description=("Set texture resolution when " "creating new project.") ) + import_cameras: bool = SettingsField( + True, title="Import Cameras", + description="Import cameras from the mesh file.") normal_map_format: str = SettingsField( "DirectX", enum_resolver=normal_map_format_enum, title="Normal Map Format", @@ -62,6 +65,12 @@ class ProjectTemplatesModel(BaseSettingsModel): description=("An option to compute tangent space " "when creating new project.") ) + preserve_strokes: bool = SettingsField( + True, title="Preserve Strokes", + description=("Preserve strokes positions on mesh.\n" + "(only relevant when loading into " + "existing project)") + ) class ProjectTemplateSettingModel(BaseSettingsModel): @@ -84,23 +93,29 @@ class LoadersModel(BaseSettingsModel): { "name": "2K(Default)", "default_texture_resolution": 2048, + "import_cameras": True, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.Default", - "tangent_space_mode": "TangentSpace.PerFragment" + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True }, { "name": "2K(UV tile)", "default_texture_resolution": 2048, + "import_cameras": True, "normal_map_format": "NormalMapFormat.DirectX", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment" + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True }, { "name": "4K(Custom)", "default_texture_resolution": 4096, + "import_cameras": True, "normal_map_format": "NormalMapFormat.OpenGL", "project_workflow": "ProjectWorkflow.UVTile", - "tangent_space_mode": "TangentSpace.PerFragment" + "tangent_space_mode": "TangentSpace.PerFragment", + "preserve_strokes": True } ] } From 3a5f72ea9d4e1d9a4b3d6c3882d9675f44ab15e6 Mon Sep 17 00:00:00 2001 From: Ynbot Date: Wed, 24 Apr 2024 03:25:37 +0000 Subject: [PATCH 160/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index bf1f845dcb7..535b9f7036e 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.8" +__version__ = "3.18.9-nightly.9" From f976e5b58f29d8f2cf067eab481265e0233f5034 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 24 Apr 2024 03:26:14 +0000 Subject: [PATCH 161/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index de25b489415..8c80242e0dc 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.9 - 3.18.9-nightly.8 - 3.18.9-nightly.7 - 3.18.9-nightly.6 @@ -134,7 +135,6 @@ body: - 3.16.0 - 3.16.0-nightly.2 - 3.16.0-nightly.1 - - 3.15.12 validations: required: true - type: dropdown From e246ebf37961a934ad560a01e34524ddf156f51b Mon Sep 17 00:00:00 2001 From: Ynbot Date: Sat, 27 Apr 2024 03:27:23 +0000 Subject: [PATCH 162/163] [Automated] Bump version --- openpype/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/version.py b/openpype/version.py index 535b9f7036e..2210cf404d1 100644 --- a/openpype/version.py +++ b/openpype/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- """Package declaring Pype version.""" -__version__ = "3.18.9-nightly.9" +__version__ = "3.18.9-nightly.10" From e0078d7bbf36e90b736e73965093e82f88708530 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 27 Apr 2024 03:28:00 +0000 Subject: [PATCH 163/163] chore(): update bug report / version --- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 8c80242e0dc..2f253fea825 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -35,6 +35,7 @@ body: label: Version description: What version are you running? Look to OpenPype Tray options: + - 3.18.9-nightly.10 - 3.18.9-nightly.9 - 3.18.9-nightly.8 - 3.18.9-nightly.7 @@ -134,7 +135,6 @@ body: - 3.16.1 - 3.16.0 - 3.16.0-nightly.2 - - 3.16.0-nightly.1 validations: required: true - type: dropdown