From 6199f6e6654ee0f672935f767e5bde22dbf2c25f Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Wed, 5 Jan 2022 10:36:36 +0100 Subject: [PATCH 01/26] Collect 'fps' animation data only for "review" instances --- openpype/hosts/maya/api/lib.py | 7 ++++--- openpype/hosts/maya/plugins/create/create_review.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 52ebcaff64..d1054988d1 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -280,7 +280,7 @@ def shape_from_element(element): return node -def collect_animation_data(): +def collect_animation_data(fps=False): """Get the basic animation data Returns: @@ -291,7 +291,6 @@ def collect_animation_data(): # get scene values as defaults start = cmds.playbackOptions(query=True, animationStartTime=True) end = cmds.playbackOptions(query=True, animationEndTime=True) - fps = mel.eval('currentTimeUnitToFPS()') # build attributes data = OrderedDict() @@ -299,7 +298,9 @@ def collect_animation_data(): data["frameEnd"] = end data["handles"] = 0 data["step"] = 1.0 - data["fps"] = fps + + if fps: + data["fps"] = mel.eval('currentTimeUnitToFPS()') return data diff --git a/openpype/hosts/maya/plugins/create/create_review.py b/openpype/hosts/maya/plugins/create/create_review.py index 05b05be7a5..ae636ec691 100644 --- a/openpype/hosts/maya/plugins/create/create_review.py +++ b/openpype/hosts/maya/plugins/create/create_review.py @@ -22,7 +22,7 @@ class CreateReview(plugin.Creator): # get basic animation data : start / end / handles / steps data = OrderedDict(**self.data) - animation_data = lib.collect_animation_data() + animation_data = lib.collect_animation_data(fps=True) for key, value in animation_data.items(): data[key] = value From 969dfdc69e2f820e67acf26ca783fef6af56e71c Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Fri, 14 Jan 2022 19:03:46 +0100 Subject: [PATCH 02/26] add basic support for extended static mesh workflow wip --- .../create/create_unreal_staticmesh.py | 33 +++++- .../hosts/maya/plugins/publish/clean_nodes.py | 27 +++++ .../publish/collect_unreal_staticmesh.py | 20 ++-- .../publish/extract_unreal_staticmesh.py | 28 +++++ .../validate_unreal_staticmesh_naming.py | 109 +++++++++--------- .../defaults/project_settings/global.json | 2 +- .../defaults/project_settings/maya.json | 22 +++- .../schemas/schema_maya_create.json | 36 +++++- .../schemas/schema_maya_publish.json | 25 ++++ 9 files changed, 230 insertions(+), 72 deletions(-) create mode 100644 openpype/hosts/maya/plugins/publish/clean_nodes.py create mode 100644 openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py diff --git a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py index db1684bbc8..30f024a160 100644 --- a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -1,11 +1,42 @@ -from openpype.hosts.maya.api import plugin +# -*- coding: utf-8 -*- +"""Creator for Unreal Static Meshes.""" +from openpype.hosts.maya.api import plugin, lib +from avalon.api import CreatorError, Session +from openpype.api import get_project_settings +from maya import cmds # noqa class CreateUnrealStaticMesh(plugin.Creator): + """Unreal Static Meshes with collisions.""" name = "staticMeshMain" label = "Unreal - Static Mesh" family = "unrealStaticMesh" icon = "cube" + dynamic_subset_keys = ["asset"] def __init__(self, *args, **kwargs): + """Constructor.""" super(CreateUnrealStaticMesh, self).__init__(*args, **kwargs) + self._project_settings = get_project_settings( + Session["AVALON_PROJECT"]) + + @classmethod + def get_dynamic_data( + cls, variant, task_name, asset_id, project_name, host_name + ): + dynamic_data = super(CreateUnrealStaticMesh, cls).get_dynamic_data( + variant, task_name, asset_id, project_name, host_name + ) + dynamic_data["asset"] = Session.get("AVALON_ASSET") + + return dynamic_data + + def process(self): + with lib.undo_chunk(): + instance = super(CreateUnrealStaticMesh, self).process() + content = cmds.sets(instance, query=True) + geometry = cmds.sets(name="geometry_SET", empty=True) + collisions = cmds.sets(name="collisions_SET", empty=True) + cmds.sets([geometry, collisions], forceElement=instance) + # todo: Iterate over collision prefixes and add them to correct + # sets. Put rest to the geometry set. diff --git a/openpype/hosts/maya/plugins/publish/clean_nodes.py b/openpype/hosts/maya/plugins/publish/clean_nodes.py new file mode 100644 index 0000000000..e6667b7036 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/clean_nodes.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +"""Cleanup leftover nodes.""" +from maya import cmds # noqa +import pyblish.api + + +class CleanNodesUp(pyblish.api.InstancePlugin): + """Cleans up the staging directory after a successful publish. + + This will also clean published renders and delete their parent directories. + + """ + + order = pyblish.api.IntegratorOrder + 10 + label = "Clean Nodes" + optional = True + active = True + + def process(self, instance): + if not instance.data.get("cleanNodes"): + self.log.info("nothing to clean") + + nodes_to_clean = instance.data.pop("cleanNodes") + self.log.info("Removing {} nodes".format(len(nodes_to_clean))) + for node in nodes_to_clean: + cmds.remove(node) + \ No newline at end of file diff --git a/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py b/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py index 5ab9643f4b..ad6398041b 100644 --- a/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py @@ -4,25 +4,31 @@ import pyblish.api class CollectUnrealStaticMesh(pyblish.api.InstancePlugin): - """Collect unreal static mesh + """Collect Unreal Static Mesh Ensures always only a single frame is extracted (current frame). This also sets correct FBX options for later extraction. - Note: - This is a workaround so that the `pype.model` family can use the - same pointcache extractor implementation as animation and pointcaches. - This always enforces the "current" frame to be published. - """ order = pyblish.api.CollectorOrder + 0.2 - label = "Collect Model Data" + label = "Collect Unreal Static Meshes" families = ["unrealStaticMesh"] def process(self, instance): # add fbx family to trigger fbx extractor instance.data["families"].append("fbx") + # take the name from instance (without the `S_` prefix) + instance.data["staticMeshCombinedName"] = instance.name[1:] + + geometry_set = [i for i in instance if i == "geometry_SET"] + instance.data["membersToCombine"] = cmds.sets( + geometry_set, query=True) + + collision_set = [i for i in instance if i == "collisions_SET"] + instance.data["collisionMembers"] = cmds.sets( + collision_set, query=True) + # set fbx overrides on instance instance.data["smoothingGroups"] = True instance.data["smoothMesh"] = True diff --git a/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py new file mode 100644 index 0000000000..fd9cf69612 --- /dev/null +++ b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +"""Create Unreal Static Mesh data to be extracted as FBX.""" +import openpype.api +import pyblish.api +from maya import cmds # noqa + + +class ExtractUnrealStaticMesh(openpype.api.Extractor): + """Extract FBX from Maya. """ + + order = pyblish.api.ExtractorOrder - 0.1 + label = "Extract Unreal Static Mesh" + families = ["unrealStaticMesh"] + + def process(self, instance): + to_combine = instance.data.get("membersToCombine") + static_mesh_name = instance.data.get("staticMeshCombinedName") + self.log.info( + "merging {] into {}".format( + "+ ".join(to_combine), static_mesh_name)) + cmds.polyUnite( + *to_combine, + n=static_mesh_name) + + if not instance.data.get("cleanNodes"): + instance.data["cleanNodes"] = [] + + instance.data["cleanNodes"].append(static_mesh_name) diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py index 99d6cfd4c5..e7df7c8cbb 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py @@ -1,18 +1,19 @@ # -*- coding: utf-8 -*- -from maya import cmds +from maya import cmds # noqa import pyblish.api import openpype.api import openpype.hosts.maya.api.action import re -class ValidateUnrealStaticmeshName(pyblish.api.InstancePlugin): +class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): """Validate name of Unreal Static Mesh - Unreals naming convention states that staticMesh sould start with `SM` - prefix - SM_[Name]_## (Eg. SM_sube_01). This plugin also validates other - types of meshes - collision meshes: + Unreals naming convention states that staticMesh should start with `SM` + prefix - SM_[Name]_## (Eg. SM_sube_01).These prefixes can be configured + in Settings UI. This plugin also validates other types of + meshes - collision meshes: UBX_[RenderMeshName]_##: Boxes are created with the Box objects type in @@ -52,69 +53,69 @@ class ValidateUnrealStaticmeshName(pyblish.api.InstancePlugin): families = ["unrealStaticMesh"] label = "Unreal StaticMesh Name" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] - regex_mesh = r"SM_(?P.*)_(\d{2})" - regex_collision = r"((UBX)|(UCP)|(USP)|(UCX))_(?P.*)_(\d{2})" + regex_mesh = r"(?P.*)_(\d{2})" + regex_collision = r"_(?P.*)_(\d{2})" @classmethod def get_invalid(cls, instance): - # find out if supplied transform is group or not - def is_group(groupName): - try: - children = cmds.listRelatives(groupName, children=True) - for child in children: - if not cmds.ls(child, transforms=True): - return False + invalid = [] + + combined_geometry_name = instance.data.get( + "staticMeshCombinedName", None) + if cls.validate_mesh: + # compile regex for testing names + regex_mesh = "{}{}".format( + ("_" + cls.static_mesh_prefix) or "", cls.regex_mesh + ) + sm_r = re.compile(regex_mesh) + if not sm_r.match(combined_geometry_name): + cls.log.error("Mesh doesn't comply with name validation.") return True - except Exception: + + if cls.validate_collision: + collision_set = instance.data.get("collisionMembers", None) + # soft-fail is there are no collision objects + if not collision_set: + cls.log.warning("No collision objects to validate.") return False - invalid = [] - content_instance = instance.data.get("setMembers", None) - if not content_instance: - cls.log.error("Instance has no nodes!") - return True - pass - descendants = cmds.listRelatives(content_instance, - allDescendents=True, - fullPath=True) or [] + regex_collision = "{}{}".format( + "({})_".format( + "|".join("(0}".format(p) for p in cls.collision_prefixes) + ) or "", cls.regex_collision + ) + cl_r = re.compile(regex_collision) - descendants = cmds.ls(descendants, noIntermediate=True, long=True) - trns = cmds.ls(descendants, long=False, type=('transform')) - - # filter out groups - filter = [node for node in trns if not is_group(node)] - - # compile regex for testing names - sm_r = re.compile(cls.regex_mesh) - cl_r = re.compile(cls.regex_collision) - - sm_names = [] - col_names = [] - for obj in filter: - sm_m = sm_r.match(obj) - if sm_m is None: - # test if it matches collision mesh - cl_r = sm_r.match(obj) - if cl_r is None: - cls.log.error("invalid mesh name on: {}".format(obj)) + for obj in collision_set: + cl_m = cl_r.match(obj) + if not cl_m: + cls.log.error("{} is invalid".format(obj)) + invalid.append(obj) + elif cl_m.group("renderName") != combined_geometry_name: + cls.log.error( + "Collision object name doesn't match" + "static mesh name: {} != {}".format( + cl_m.group("renderName"), + combined_geometry_name) + ) invalid.append(obj) - else: - col_names.append((cl_r.group("renderName"), obj)) - else: - sm_names.append(sm_m.group("renderName")) - - for c_mesh in col_names: - if c_mesh[0] not in sm_names: - cls.log.error(("collision name {} doesn't match any " - "static mesh names.").format(obj)) - invalid.append(c_mesh[1]) return invalid def process(self, instance): + # todo: load prefixes from creator settings. + + if not self.validate_mesh and not self.validate_collision: + self.log.info("Validation of both mesh and collision names" + "is disabled.") + return + + if not instance.data.get("collisionMembers", None): + self.log.info("There are no collision objects to validate") + return invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Model naming is invalid. See log.") + raise RuntimeError("Model naming is invalid. See log.") \ No newline at end of file diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index cff1259c98..2169a62746 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -219,7 +219,7 @@ "hosts": [], "task_types": [], "tasks": [], - "template": "{family}{Variant}" + "template": "{family}{variant}" }, { "families": [ diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index a756071106..67a7b84cdc 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -127,6 +127,13 @@ "enabled": true, "defaults": [ "Main" + ], + "static_mesh_prefix": "S_", + "collision_prefixes": [ + "UBX", + "UCP", + "USP", + "UCX" ] }, "CreateVrayProxy": { @@ -180,6 +187,11 @@ "whitelist_native_plugins": false, "authorized_plugins": [] }, + "ValidateUnrealStaticMeshName": { + "enabled": true, + "validate_mesh": false, + "validate_collision": true + }, "ValidateRenderSettings": { "arnold_render_attributes": [], "vray_render_attributes": [], @@ -197,6 +209,11 @@ "regex": "(.*)_(\\d)*_(?P.*)_(GEO)", "top_level_regex": ".*_GRP" }, + "ValidateModelContent": { + "enabled": true, + "optional": false, + "validate_top_group": true + }, "ValidateTransformNamingSuffix": { "enabled": true, "SUFFIX_NAMING_TABLE": { @@ -281,11 +298,6 @@ "optional": true, "active": true }, - "ValidateModelContent": { - "enabled": true, - "optional": false, - "validate_top_group": true - }, "ValidateNoAnimation": { "enabled": false, "optional": 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 088d5d1f96..0544b4bab7 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 @@ -66,6 +66,38 @@ } ] }, + { + "type": "dict", + "collapsible": true, + "key": "CreateUnrealStaticMesh", + "label": "Create Unreal - Static Mesh", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "list", + "key": "defaults", + "label": "Default Subsets", + "object_type": "text" + }, + { + "type": "text", + "key": "static_mesh_prefix", + "label": "Static Mesh Prefix" + }, + { + "type": "list", + "key": "collision_prefixes", + "label": "Collision Mesh Prefixes", + "object_type": "text" + } + ] + + }, { "type": "schema_template", "name": "template_create_plugin", @@ -118,10 +150,6 @@ "key": "CreateSetDress", "label": "Create Set Dress" }, - { - "key": "CreateUnrealStaticMesh", - "label": "Create Unreal - Static Mesh" - }, { "key": "CreateVrayProxy", "label": "Create VRay Proxy" 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 7c9a5a6b46..f4a371c6de 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 @@ -129,6 +129,31 @@ ] }, + { + "type": "dict", + "collapsible": true, + "key": "ValidateUnrealStaticMeshName", + "label": "Validate Unreal Static Mesh Name", + "checkbox_key": "enabled", + "children": [ + { + "type": "boolean", + "key": "enabled", + "label": "Enabled" + }, + { + "type": "boolean", + "key": "validate_mesh", + "label": "Validate mesh Names " + }, + { + "type": "boolean", + "key": "validate_collision", + "label": "Validate collision names" + } + ] + }, + { "type": "dict", "collapsible": true, From cb489c055e987423104301493b71d05747a5729e Mon Sep 17 00:00:00 2001 From: karimmozlia Date: Mon, 17 Jan 2022 11:03:00 +0200 Subject: [PATCH 03/26] add family and representation --- openpype/hosts/maya/plugins/load/load_vrayproxy.py | 4 ++-- openpype/hosts/maya/vendor/studiolibrary | 1 + repos/avalon-core | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) create mode 160000 openpype/hosts/maya/vendor/studiolibrary diff --git a/openpype/hosts/maya/plugins/load/load_vrayproxy.py b/openpype/hosts/maya/plugins/load/load_vrayproxy.py index e70f40bf5a..2e86012d8d 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayproxy.py +++ b/openpype/hosts/maya/plugins/load/load_vrayproxy.py @@ -17,8 +17,8 @@ from openpype.api import get_project_settings class VRayProxyLoader(api.Loader): """Load VRay Proxy with Alembic or VrayMesh.""" - families = ["vrayproxy"] - representations = ["vrmesh"] + families = ["vrayproxy", "model"] + representations = ["vrmesh", "abc"] label = "Import VRay Proxy" order = -10 diff --git a/openpype/hosts/maya/vendor/studiolibrary b/openpype/hosts/maya/vendor/studiolibrary new file mode 160000 index 0000000000..f29e350da9 --- /dev/null +++ b/openpype/hosts/maya/vendor/studiolibrary @@ -0,0 +1 @@ +Subproject commit f29e350da9e9508522a740a4f30efb93b99c89d3 diff --git a/repos/avalon-core b/repos/avalon-core index ffe9e910f1..7e5efd6885 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit ffe9e910f1f382e222d457d8e4a8426c41ed43ae +Subproject commit 7e5efd6885330d84bb8495975bcab84df49bfa3d From 582b4a7aafc9a02baac9985c1dcbf103d950ba76 Mon Sep 17 00:00:00 2001 From: karimmozlia Date: Mon, 17 Jan 2022 12:17:42 +0200 Subject: [PATCH 04/26] test without adding family --- openpype/hosts/maya/plugins/load/load_vrayproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_vrayproxy.py b/openpype/hosts/maya/plugins/load/load_vrayproxy.py index 2e86012d8d..1e3222873f 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayproxy.py +++ b/openpype/hosts/maya/plugins/load/load_vrayproxy.py @@ -17,7 +17,7 @@ from openpype.api import get_project_settings class VRayProxyLoader(api.Loader): """Load VRay Proxy with Alembic or VrayMesh.""" - families = ["vrayproxy", "model"] + families = ["vrayproxy"] representations = ["vrmesh", "abc"] label = "Import VRay Proxy" From 918d93b3391a621fadb683b2b6e82217a6e5fe98 Mon Sep 17 00:00:00 2001 From: karimmozlia Date: Mon, 17 Jan 2022 12:35:48 +0200 Subject: [PATCH 05/26] add model family --- openpype/hosts/maya/plugins/load/load_vrayproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_vrayproxy.py b/openpype/hosts/maya/plugins/load/load_vrayproxy.py index 1e3222873f..2e86012d8d 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayproxy.py +++ b/openpype/hosts/maya/plugins/load/load_vrayproxy.py @@ -17,7 +17,7 @@ from openpype.api import get_project_settings class VRayProxyLoader(api.Loader): """Load VRay Proxy with Alembic or VrayMesh.""" - families = ["vrayproxy"] + families = ["vrayproxy", "model"] representations = ["vrmesh", "abc"] label = "Import VRay Proxy" From 987c1bc52560646bd818ed07c6e2d8d32850d838 Mon Sep 17 00:00:00 2001 From: karimmozlia Date: Mon, 17 Jan 2022 12:56:08 +0200 Subject: [PATCH 06/26] add animation and pointcache --- openpype/hosts/maya/plugins/load/load_vrayproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/hosts/maya/plugins/load/load_vrayproxy.py b/openpype/hosts/maya/plugins/load/load_vrayproxy.py index 2e86012d8d..806cf1fd18 100644 --- a/openpype/hosts/maya/plugins/load/load_vrayproxy.py +++ b/openpype/hosts/maya/plugins/load/load_vrayproxy.py @@ -17,7 +17,7 @@ from openpype.api import get_project_settings class VRayProxyLoader(api.Loader): """Load VRay Proxy with Alembic or VrayMesh.""" - families = ["vrayproxy", "model"] + families = ["vrayproxy", "model", "pointcache", "animation"] representations = ["vrmesh", "abc"] label = "Import VRay Proxy" From d18ef2c51256413e1ca32bb655463d07dd808782 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Wed, 19 Jan 2022 19:25:11 +0100 Subject: [PATCH 07/26] fix hound --- .../maya/plugins/create/create_unreal_staticmesh.py | 9 ++++++--- openpype/hosts/maya/plugins/publish/clean_nodes.py | 1 - .../plugins/publish/validate_unreal_staticmesh_naming.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py index 30f024a160..296116caae 100644 --- a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Creator for Unreal Static Meshes.""" from openpype.hosts.maya.api import plugin, lib -from avalon.api import CreatorError, Session +from avalon.api import Session from openpype.api import get_project_settings from maya import cmds # noqa @@ -38,5 +38,8 @@ class CreateUnrealStaticMesh(plugin.Creator): geometry = cmds.sets(name="geometry_SET", empty=True) collisions = cmds.sets(name="collisions_SET", empty=True) cmds.sets([geometry, collisions], forceElement=instance) - # todo: Iterate over collision prefixes and add them to correct - # sets. Put rest to the geometry set. + for node in content: + if [n for n in self.collision_prefixes if node.startswith(n)]: + cmds.sets(node, forceElement=collisions) + else: + cmds.sets(node, forceElement=geometry) diff --git a/openpype/hosts/maya/plugins/publish/clean_nodes.py b/openpype/hosts/maya/plugins/publish/clean_nodes.py index e6667b7036..cd3613cc4f 100644 --- a/openpype/hosts/maya/plugins/publish/clean_nodes.py +++ b/openpype/hosts/maya/plugins/publish/clean_nodes.py @@ -24,4 +24,3 @@ class CleanNodesUp(pyblish.api.InstancePlugin): self.log.info("Removing {} nodes".format(len(nodes_to_clean))) for node in nodes_to_clean: cmds.remove(node) - \ No newline at end of file diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py index e7df7c8cbb..c5aa14ec0c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py @@ -118,4 +118,4 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError("Model naming is invalid. See log.") \ No newline at end of file + raise RuntimeError("Model naming is invalid. See log.") From eca0e02a91ba4c2b02c417191b22d179e6681870 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 20 Jan 2022 01:26:02 +0100 Subject: [PATCH 08/26] create duplicates --- .../create/create_unreal_staticmesh.py | 26 ++++++--- .../hosts/maya/plugins/publish/clean_nodes.py | 11 +++- .../publish/collect_unreal_staticmesh.py | 2 +- .../publish/extract_unreal_staticmesh.py | 23 ++++++-- .../plugins/publish/validate_assembly_name.py | 2 +- .../validate_unreal_staticmesh_naming.py | 55 +++++++++++++------ openpype/plugins/publish/integrate_new.py | 1 + 7 files changed, 86 insertions(+), 34 deletions(-) diff --git a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py index 296116caae..d69cc6f0a1 100644 --- a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -35,11 +35,21 @@ class CreateUnrealStaticMesh(plugin.Creator): with lib.undo_chunk(): instance = super(CreateUnrealStaticMesh, self).process() content = cmds.sets(instance, query=True) - geometry = cmds.sets(name="geometry_SET", empty=True) - collisions = cmds.sets(name="collisions_SET", empty=True) - cmds.sets([geometry, collisions], forceElement=instance) - for node in content: - if [n for n in self.collision_prefixes if node.startswith(n)]: - cmds.sets(node, forceElement=collisions) - else: - cmds.sets(node, forceElement=geometry) + + # empty set and process its former content + cmds.sets(content, rm=instance) + geometry_set = cmds.sets(name="geometry_SET", empty=True) + collisions_set = cmds.sets(name="collisions_SET", empty=True) + + cmds.sets([geometry_set, collisions_set], forceElement=instance) + + members = cmds.ls(content, long=True) or [] + children = cmds.listRelatives(members, allDescendents=True, + fullPath=True) or [] + children = cmds.ls(children, type="transform") + for node in children: + if cmds.listRelatives(node, type="shape"): + if [n for n in self.collision_prefixes if node.startswith(n)]: + cmds.sets(node, forceElement=collisions_set) + else: + cmds.sets(node, forceElement=geometry_set) diff --git a/openpype/hosts/maya/plugins/publish/clean_nodes.py b/openpype/hosts/maya/plugins/publish/clean_nodes.py index cd3613cc4f..03995cdabe 100644 --- a/openpype/hosts/maya/plugins/publish/clean_nodes.py +++ b/openpype/hosts/maya/plugins/publish/clean_nodes.py @@ -18,9 +18,14 @@ class CleanNodesUp(pyblish.api.InstancePlugin): def process(self, instance): if not instance.data.get("cleanNodes"): - self.log.info("nothing to clean") + self.log.info("Nothing to clean.") + return - nodes_to_clean = instance.data.pop("cleanNodes") + nodes_to_clean = instance.data.pop("cleanNodes", []) self.log.info("Removing {} nodes".format(len(nodes_to_clean))) for node in nodes_to_clean: - cmds.remove(node) + try: + cmds.delete(node) + except ValueError: + # object might be already deleted, don't complain about it + pass diff --git a/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py b/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py index ad6398041b..b1fb0542f2 100644 --- a/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py @@ -19,7 +19,7 @@ class CollectUnrealStaticMesh(pyblish.api.InstancePlugin): # add fbx family to trigger fbx extractor instance.data["families"].append("fbx") # take the name from instance (without the `S_` prefix) - instance.data["staticMeshCombinedName"] = instance.name[1:] + instance.data["staticMeshCombinedName"] = instance.name[2:] geometry_set = [i for i in instance if i == "geometry_SET"] instance.data["membersToCombine"] = cmds.sets( diff --git a/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py index fd9cf69612..7867952de6 100644 --- a/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py @@ -3,6 +3,7 @@ import openpype.api import pyblish.api from maya import cmds # noqa +from uuid import uuid4 class ExtractUnrealStaticMesh(openpype.api.Extractor): @@ -16,13 +17,27 @@ class ExtractUnrealStaticMesh(openpype.api.Extractor): to_combine = instance.data.get("membersToCombine") static_mesh_name = instance.data.get("staticMeshCombinedName") self.log.info( - "merging {] into {}".format( - "+ ".join(to_combine), static_mesh_name)) + "merging {} into {}".format( + " + ".join(to_combine), static_mesh_name)) + duplicates = cmds.duplicate(to_combine, ic=True) cmds.polyUnite( - *to_combine, - n=static_mesh_name) + *duplicates, + n=static_mesh_name, ch=False) + + collision_duplicates = cmds.duplicate( + instance.data.get("collisionMembers"), ic=True) + cmds.parent(collision_duplicates, a=True, w=True) + instance.data["collisionMembers"] = collision_duplicates + + self.log.info( + "collision members: {}".format(instance.data["collisionMembers"])) if not instance.data.get("cleanNodes"): instance.data["cleanNodes"] = [] instance.data["cleanNodes"].append(static_mesh_name) + instance.data["cleanNodes"] += duplicates + instance.data["cleanNodes"] += collision_duplicates + + instance.data["setMembers"] = [static_mesh_name] + instance.data["setMembers"] += instance.data["collisionMembers"] diff --git a/openpype/hosts/maya/plugins/publish/validate_assembly_name.py b/openpype/hosts/maya/plugins/publish/validate_assembly_name.py index 8f7a3dfaf9..41349553fc 100644 --- a/openpype/hosts/maya/plugins/publish/validate_assembly_name.py +++ b/openpype/hosts/maya/plugins/publish/validate_assembly_name.py @@ -30,7 +30,7 @@ class ValidateAssemblyName(pyblish.api.InstancePlugin): descendants = cmds.listRelatives(content_instance, allDescendents=True, fullPath=True) or [] - descendants = cmds.ls(descendants, noIntermediate=True, long=True) + descendants = cmds.ls(descendants, noIntermediate=True, type="transform") content_instance = list(set(content_instance + descendants)) assemblies = cmds.ls(content_instance, assemblies=True, long=True) diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py index c5aa14ec0c..901a2ec75e 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py @@ -4,6 +4,8 @@ from maya import cmds # noqa import pyblish.api import openpype.api import openpype.hosts.maya.api.action +from avalon.api import Session +from openpype.api import get_project_settings import re @@ -15,14 +17,14 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): in Settings UI. This plugin also validates other types of meshes - collision meshes: - UBX_[RenderMeshName]_##: + UBX_[RenderMeshName]*: Boxes are created with the Box objects type in Max or with the Cube polygonal primitive in Maya. You cannot move the vertices around or deform it in any way to make it something other than a rectangular prism, or else it will not work. - UCP_[RenderMeshName]_##: + UCP_[RenderMeshName]*: Capsules are created with the Capsule object type. The capsule does not need to have many segments (8 is a good number) at all because it is @@ -30,7 +32,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): boxes, you should not move the individual vertices around. - USP_[RenderMeshName]_##: + USP_[RenderMeshName]*: Spheres are created with the Sphere object type. The sphere does not need to have many segments (8 is a good number) at all because it is @@ -38,7 +40,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): boxes, you should not move the individual vertices around. - UCX_[RenderMeshName]_##: + UCX_[RenderMeshName]*: Convex objects can be any completely closed convex 3D shape. For example, a box can also be a convex object @@ -53,14 +55,23 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): families = ["unrealStaticMesh"] label = "Unreal StaticMesh Name" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] - regex_mesh = r"(?P.*)_(\d{2})" - regex_collision = r"_(?P.*)_(\d{2})" + regex_mesh = r"(?P.*))" + regex_collision = r"(?P.*)" @classmethod def get_invalid(cls, instance): invalid = [] + project_settings = get_project_settings(Session["AVALON_PROJECT"]) + collision_prefixes = ( + project_settings + ["maya"] + ["create"] + ["CreateUnrealStaticMesh"] + ["collision_prefixes"] + ) + combined_geometry_name = instance.data.get( "staticMeshCombinedName", None) if cls.validate_mesh: @@ -81,10 +92,11 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): return False regex_collision = "{}{}".format( - "({})_".format( - "|".join("(0}".format(p) for p in cls.collision_prefixes) + "(?P({}))_".format( + "|".join("{0}".format(p) for p in collision_prefixes) ) or "", cls.regex_collision ) + cl_r = re.compile(regex_collision) for obj in collision_set: @@ -92,20 +104,29 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): if not cl_m: cls.log.error("{} is invalid".format(obj)) invalid.append(obj) - elif cl_m.group("renderName") != combined_geometry_name: - cls.log.error( - "Collision object name doesn't match" - "static mesh name: {} != {}".format( - cl_m.group("renderName"), - combined_geometry_name) + else: + expected_collision = "{}_{}".format( + cl_m.group("prefix"), + combined_geometry_name ) - invalid.append(obj) + + if not obj.startswith(expected_collision): + + cls.log.error( + "Collision object name doesn't match " + "static mesh name" + ) + cls.log.error("{}_{} != {}_{}".format( + cl_m.group("prefix"), + cl_m.group("renderName"), + cl_m.group("prefix"), + combined_geometry_name, + )) + invalid.append(obj) return invalid def process(self, instance): - # todo: load prefixes from creator settings. - if not self.validate_mesh and not self.validate_collision: self.log.info("Validation of both mesh and collision names" "is disabled.") diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index cec2e470b3..bf214d9139 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -389,6 +389,7 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): repre["ext"] = ext template_data["ext"] = ext + self.log.info(template_name) template = os.path.normpath( anatomy.templates[template_name]["path"]) From 47244a39176b1ea6371aaec05de4663cd1727977 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 20 Jan 2022 17:13:21 +0100 Subject: [PATCH 09/26] create duplicates --- openpype/hosts/maya/api/lib.py | 25 +++++++++++++++++++ .../hosts/maya/plugins/publish/extract_fbx.py | 24 +++++++++++------- .../publish/extract_unreal_staticmesh.py | 10 -------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 3f93bc2ab5..0858c205ea 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -2853,3 +2853,28 @@ def set_colorspace(): cmds.colorManagementPrefs(e=True, renderingSpaceName=renderSpace) viewTransform = root_dict["viewTransform"] cmds.colorManagementPrefs(e=True, viewTransformName=viewTransform) + + +@contextlib.contextmanager +def root_parent(nodes): + # type: (list) -> list + """Context manager to un-parent provided nodes and return then back.""" + import pymel.core as pm # noqa + + node_parents = [] + for node in nodes: + n = pm.PyNode(node) + try: + root = pm.listRelatives(n, parent=1)[0] + except IndexError: + root = None + node_parents.append((n, root)) + try: + for node in node_parents: + node[0].setParent(world=True) + yield + finally: + for node in node_parents: + if node[1]: + node[0].setParent(node[1]) + diff --git a/openpype/hosts/maya/plugins/publish/extract_fbx.py b/openpype/hosts/maya/plugins/publish/extract_fbx.py index 720a61b0a7..e4894f28cd 100644 --- a/openpype/hosts/maya/plugins/publish/extract_fbx.py +++ b/openpype/hosts/maya/plugins/publish/extract_fbx.py @@ -1,7 +1,9 @@ +# -*- coding: utf-8 -*- import os -from maya import cmds -import maya.mel as mel +from maya import cmds # noqa +import maya.mel as mel # noqa +from openpype.hosts.maya.api.lib import root_parent import pyblish.api import avalon.maya @@ -192,10 +194,7 @@ class ExtractFBX(openpype.api.Extractor): if isinstance(value, bool): value = str(value).lower() - template = "FBXExport{0} -v {1}" - if key == "UpAxis": - template = "FBXExport{0} {1}" - + template = "FBXExport{0} {1}" if key == "UpAxis" else "FBXExport{0} -v {1}" # noqa cmd = template.format(key, value) self.log.info(cmd) mel.eval(cmd) @@ -205,9 +204,16 @@ class ExtractFBX(openpype.api.Extractor): mel.eval("FBXExportGenerateLog -v false") # Export - with avalon.maya.maintained_selection(): - cmds.select(members, r=1, noExpand=True) - mel.eval('FBXExport -f "{}" -s'.format(path)) + if "unrealStaticMesh" in instance.data["families"]: + with avalon.maya.maintained_selection(): + with root_parent(members): + self.log.info("Un-parenting: {}".format(members)) + cmds.select(members, r=1, noExpand=True) + mel.eval('FBXExport -f "{}" -s'.format(path)) + else: + with avalon.maya.maintained_selection(): + cmds.select(members, r=1, noExpand=True) + mel.eval('FBXExport -f "{}" -s'.format(path)) if "representations" not in instance.data: instance.data["representations"] = [] diff --git a/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py index 7867952de6..32dc9d1d1c 100644 --- a/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py @@ -3,7 +3,6 @@ import openpype.api import pyblish.api from maya import cmds # noqa -from uuid import uuid4 class ExtractUnrealStaticMesh(openpype.api.Extractor): @@ -24,20 +23,11 @@ class ExtractUnrealStaticMesh(openpype.api.Extractor): *duplicates, n=static_mesh_name, ch=False) - collision_duplicates = cmds.duplicate( - instance.data.get("collisionMembers"), ic=True) - cmds.parent(collision_duplicates, a=True, w=True) - instance.data["collisionMembers"] = collision_duplicates - - self.log.info( - "collision members: {}".format(instance.data["collisionMembers"])) - if not instance.data.get("cleanNodes"): instance.data["cleanNodes"] = [] instance.data["cleanNodes"].append(static_mesh_name) instance.data["cleanNodes"] += duplicates - instance.data["cleanNodes"] += collision_duplicates instance.data["setMembers"] = [static_mesh_name] instance.data["setMembers"] += instance.data["collisionMembers"] From c88f95fa6f289b0fef1daaccf79da019eb20417e Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 20 Jan 2022 17:18:06 +0100 Subject: [PATCH 10/26] =?UTF-8?q?fix=20=F0=9F=90=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openpype/hosts/maya/api/lib.py | 1 - .../hosts/maya/plugins/create/create_unreal_staticmesh.py | 5 ++++- .../hosts/maya/plugins/publish/validate_assembly_name.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index 0858c205ea..8e50c3c00a 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -2877,4 +2877,3 @@ def root_parent(nodes): for node in node_parents: if node[1]: node[0].setParent(node[1]) - diff --git a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py index d69cc6f0a1..9ad560ab7c 100644 --- a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -49,7 +49,10 @@ class CreateUnrealStaticMesh(plugin.Creator): children = cmds.ls(children, type="transform") for node in children: if cmds.listRelatives(node, type="shape"): - if [n for n in self.collision_prefixes if node.startswith(n)]: + if [ + n for n in self.collision_prefixes + if node.startswith(n) + ]: cmds.sets(node, forceElement=collisions_set) else: cmds.sets(node, forceElement=geometry_set) diff --git a/openpype/hosts/maya/plugins/publish/validate_assembly_name.py b/openpype/hosts/maya/plugins/publish/validate_assembly_name.py index 41349553fc..02464b2302 100644 --- a/openpype/hosts/maya/plugins/publish/validate_assembly_name.py +++ b/openpype/hosts/maya/plugins/publish/validate_assembly_name.py @@ -30,7 +30,8 @@ class ValidateAssemblyName(pyblish.api.InstancePlugin): descendants = cmds.listRelatives(content_instance, allDescendents=True, fullPath=True) or [] - descendants = cmds.ls(descendants, noIntermediate=True, type="transform") + descendants = cmds.ls( + descendants, noIntermediate=True, type="transform") content_instance = list(set(content_instance + descendants)) assemblies = cmds.ls(content_instance, assemblies=True, long=True) From 239ab2cbaaa70b165cd1a316fd359f44af7907eb Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 11:37:37 +0100 Subject: [PATCH 11/26] pype info will also show build version --- openpype/lib/pype_info.py | 8 +++++--- openpype/tools/tray/pype_info_widget.py | 15 +++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/openpype/lib/pype_info.py b/openpype/lib/pype_info.py index 848a505187..8370ecc88f 100644 --- a/openpype/lib/pype_info.py +++ b/openpype/lib/pype_info.py @@ -10,11 +10,12 @@ from .execute import get_openpype_execute_args from .local_settings import get_local_site_id from .openpype_version import ( is_running_from_build, - get_openpype_version + get_openpype_version, + get_build_version ) -def get_pype_info(): +def get_openpype_info(): """Information about currently used Pype process.""" executable_args = get_openpype_execute_args() if is_running_from_build(): @@ -23,6 +24,7 @@ def get_pype_info(): version_type = "code" return { + "build_verison": get_build_version(), "version": get_openpype_version(), "version_type": version_type, "executable": executable_args[-1], @@ -51,7 +53,7 @@ def get_workstation_info(): def get_all_current_info(): """All information about current process in one dictionary.""" return { - "pype": get_pype_info(), + "pype": get_openpype_info(), "workstation": get_workstation_info(), "env": os.environ.copy(), "local_settings": get_local_settings() diff --git a/openpype/tools/tray/pype_info_widget.py b/openpype/tools/tray/pype_info_widget.py index 2ca625f307..e68793b18c 100644 --- a/openpype/tools/tray/pype_info_widget.py +++ b/openpype/tools/tray/pype_info_widget.py @@ -9,7 +9,7 @@ from openpype.api import resources from openpype.settings.lib import get_local_settings from openpype.lib.pype_info import ( get_all_current_info, - get_pype_info, + get_openpype_info, get_workstation_info, extract_pype_info_to_file ) @@ -426,7 +426,7 @@ class PypeInfoSubWidget(QtWidgets.QWidget): """Create widget with information about OpenPype application.""" # Get pype info data - pype_info = get_pype_info() + pype_info = get_openpype_info() # Modify version key/values version_value = "{} ({})".format( pype_info.pop("version", self.not_applicable), @@ -435,13 +435,20 @@ class PypeInfoSubWidget(QtWidgets.QWidget): pype_info["version_value"] = version_value # Prepare lable mapping key_label_mapping = { - "version_value": "OpenPype version:", + "version_value": "Running version:", + "build_verison": "Build version:", "executable": "OpenPype executable:", "pype_root": "OpenPype location:", "mongo_url": "OpenPype Mongo URL:" } # Prepare keys order - keys_order = ["version_value", "executable", "pype_root", "mongo_url"] + keys_order = [ + "version_value", + "build_verison", + "executable", + "pype_root", + "mongo_url" + ] for key in pype_info.keys(): if key not in keys_order: keys_order.append(key) From e9d92ef96930334fd0ebfb49c227046d42de0c97 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 11:37:51 +0100 Subject: [PATCH 12/26] trigger version check on start --- openpype/tools/tray/pype_tray.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index c9b8aaa842..4abf0f5a83 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -328,8 +328,8 @@ class TrayManager: self.main_thread_timer = main_thread_timer version_check_timer = QtCore.QTimer() - version_check_timer.timeout.connect(self._on_version_check_timer) if self._version_check_interval > 0: + version_check_timer.timeout.connect(self._on_version_check_timer) version_check_timer.setInterval(self._version_check_interval) version_check_timer.start() self._version_check_timer = version_check_timer @@ -341,6 +341,9 @@ class TrayManager: def _startup_validations(self): """Run possible startup validations.""" + # Trigger version validation on start + self._version_check_timer.timeout.emit() + self._validate_settings_defaults() def _validate_settings_defaults(self): From ac79f24e403a8f1e606323b4208a9bb6e8281cb0 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 12:11:33 +0100 Subject: [PATCH 13/26] handle situations when current version is higher --- openpype/lib/__init__.py | 3 +- openpype/lib/openpype_version.py | 29 +++++++++++++ openpype/tools/tray/pype_tray.py | 74 +++++++++++++++++++++++++------- 3 files changed, 90 insertions(+), 16 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index 1c8f7a57af..7dd9a8793b 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -175,7 +175,8 @@ from .openpype_version import ( get_expected_version, is_running_from_build, is_running_staging, - is_current_version_studio_latest + is_current_version_studio_latest, + is_current_version_higher_than_expected ) terminal = Terminal diff --git a/openpype/lib/openpype_version.py b/openpype/lib/openpype_version.py index 201bf646e9..d547d34755 100644 --- a/openpype/lib/openpype_version.py +++ b/openpype/lib/openpype_version.py @@ -195,3 +195,32 @@ def is_current_version_studio_latest(): expected_version = get_expected_version() # Check if current version is expected version return current_version == expected_version + + +def is_current_version_higher_than_expected(): + """Is current OpenPype version higher than version defined by studio. + + Returns: + None: Can't determine. e.g. when running from code or the build is + too old. + bool: True when is higher than studio version. + """ + output = None + # Skip if is not running from build or build does not support version + # control or path to folder with zip files is not accessible + if ( + not is_running_from_build() + or not op_version_control_available() + or not openpype_path_is_accessible() + ): + return output + + # Get OpenPypeVersion class + OpenPypeVersion = get_OpenPypeVersion() + # Convert current version to OpenPypeVersion object + current_version = OpenPypeVersion(version=get_openpype_version()) + + # Get expected version (from settings) + expected_version = get_expected_version() + # Check if current version is expected version + return current_version > expected_version diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 4abf0f5a83..a6ce5d3a89 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -18,6 +18,7 @@ from openpype.lib import ( get_openpype_execute_args, op_version_control_available, is_current_version_studio_latest, + is_current_version_higher_than_expected, is_running_from_build, is_running_staging, get_expected_version, @@ -104,13 +105,12 @@ class VersionDialog(QtWidgets.QDialog): label_widget.setWordWrap(True) top_layout = QtWidgets.QHBoxLayout(top_widget) - # top_layout.setContentsMargins(0, 0, 0, 0) top_layout.setSpacing(10) top_layout.addWidget(gift_icon_label, 0, QtCore.Qt.AlignCenter) top_layout.addWidget(label_widget, 1) - ignore_btn = QtWidgets.QPushButton("Later", self) - restart_btn = QtWidgets.QPushButton("Restart && Update", self) + ignore_btn = QtWidgets.QPushButton(self) + restart_btn = QtWidgets.QPushButton(self) restart_btn.setObjectName("TrayRestartButton") btns_layout = QtWidgets.QHBoxLayout() @@ -127,7 +127,12 @@ class VersionDialog(QtWidgets.QDialog): restart_btn.clicked.connect(self._on_reset) self._label_widget = label_widget + self._gift_icon_label = gift_icon_label + self._ignore_btn = ignore_btn + self._restart_btn = restart_btn + self._restart_accepted = False + self._current_is_higher = False self.setStyleSheet(style.load_stylesheet()) @@ -152,15 +157,37 @@ class VersionDialog(QtWidgets.QDialog): def closeEvent(self, event): super().closeEvent(event) - if not self._restart_accepted: - self.ignore_requested.emit() + if self._restart_accepted or self._current_is_higher: + return + # Trigger ignore requested only if restart was not clicked and current + # version is lower + self.ignore_requested.emit() - def update_versions(self, current_version, expected_version): - message = ( - "Running OpenPype version is {}." - " Your production has been updated to version {}." - ).format(str(current_version), str(expected_version)) - self._label_widget.setText(message) + def update_versions( + self, current_version, expected_version, current_is_higher + ): + if not current_is_higher: + label_message = ( + "Running OpenPype version is {}." + " Your production has been updated to version {}." + ).format(str(current_version), str(expected_version)) + ignore_label = "Later" + restart_label = "Restart && Update" + else: + label_message = ( + "Running OpenPype version is {}." + " Your production should use version {}." + ).format(str(current_version), str(expected_version)) + ignore_label = "I know" + restart_label = "Restart && Change" + + self._current_is_higher = current_is_higher + + self._gift_icon_label.setVisible(not current_is_higher) + + self._label_widget.setText(label_message) + self._ignore_btn.setText(ignore_label) + self._restart_btn.setText(restart_label) def _on_ignore(self): self.reject() @@ -247,15 +274,17 @@ class TrayManager: expected_version = get_expected_version() current_version = get_openpype_version() + current_is_higher = is_current_version_higher_than_expected() + self._version_dialog.update_versions( - current_version, expected_version + current_version, expected_version, current_is_higher ) self._version_dialog.show() self._version_dialog.raise_() self._version_dialog.activateWindow() def _restart_and_install(self): - self.restart() + self.restart(use_expected_version=True) def _outdated_version_ignored(self): self.show_tray_message( @@ -432,12 +461,18 @@ class TrayManager: self._restart_action = restart_action def _on_restart_action(self): - self.restart() + self.restart(use_expected_version=True) - def restart(self, reset_version=True): + def restart(self, use_expected_version=False, reset_version=False): """Restart Tray tool. First creates new process with same argument and close current tray. + + Args: + use_expected_version(bool): OpenPype version is set to expected + version. + reset_version(bool): OpenPype version is cleaned up so igniters + logic will decide which version will be used. """ args = get_openpype_execute_args() kwargs = { @@ -451,6 +486,15 @@ class TrayManager: if args[-1] == additional_args[0]: additional_args.pop(0) + if use_expected_version: + expected_version = get_expected_version() + if expected_version is not None: + reset_version = False + kwargs["env"]["OPENPYPE_VERSION"] = str(expected_version) + else: + # Trigger reset of version if expected version was not found + reset_version = True + # Pop OPENPYPE_VERSION if reset_version: # Add staging flag if was running from staging From 2a848bbb97c061205eead8dde7e4a95261494cc5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 12:25:09 +0100 Subject: [PATCH 14/26] change title --- openpype/tools/tray/pype_tray.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index a6ce5d3a89..1fd4b3ae97 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -85,7 +85,7 @@ class VersionDialog(QtWidgets.QDialog): def __init__(self, parent=None): super(VersionDialog, self).__init__(parent) - self.setWindowTitle("OpenPype update is needed") + icon = QtGui.QIcon(resources.get_openpype_icon_filepath()) self.setWindowIcon(icon) self.setWindowFlags( @@ -167,6 +167,7 @@ class VersionDialog(QtWidgets.QDialog): self, current_version, expected_version, current_is_higher ): if not current_is_higher: + title = "OpenPype update is needed" label_message = ( "Running OpenPype version is {}." " Your production has been updated to version {}." @@ -174,6 +175,7 @@ class VersionDialog(QtWidgets.QDialog): ignore_label = "Later" restart_label = "Restart && Update" else: + title = "OpenPype version is higher" label_message = ( "Running OpenPype version is {}." " Your production should use version {}." @@ -181,6 +183,8 @@ class VersionDialog(QtWidgets.QDialog): ignore_label = "I know" restart_label = "Restart && Change" + self.setWindowTitle(title) + self._current_is_higher = current_is_higher self._gift_icon_label.setVisible(not current_is_higher) From 69dea46dccaa79860aa4eb1cf4068ef93cf9f5a5 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 12:41:11 +0100 Subject: [PATCH 15/26] removed 'should' from label --- openpype/tools/tray/pype_tray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 1fd4b3ae97..284a6a31df 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -178,7 +178,7 @@ class VersionDialog(QtWidgets.QDialog): title = "OpenPype version is higher" label_message = ( "Running OpenPype version is {}." - " Your production should use version {}." + " Your production use version {}." ).format(str(current_version), str(expected_version)) ignore_label = "I know" restart_label = "Restart && Change" From aedfb53c4e1e4851726671d3b519101c5984c639 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 13:22:14 +0100 Subject: [PATCH 16/26] fix grammar --- openpype/tools/tray/pype_tray.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 284a6a31df..4ad5bc19ba 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -178,7 +178,7 @@ class VersionDialog(QtWidgets.QDialog): title = "OpenPype version is higher" label_message = ( "Running OpenPype version is {}." - " Your production use version {}." + " Your production uses version {}." ).format(str(current_version), str(expected_version)) ignore_label = "I know" restart_label = "Restart && Change" From a06b13604f3f2bd9fa51efc98c6d809eec610dae Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 18:39:35 +0100 Subject: [PATCH 17/26] handle situations when version can't be detected --- openpype/tools/tray/pype_tray.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index 4ad5bc19ba..a21a9de705 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -258,6 +258,10 @@ class TrayManager: def validate_openpype_version(self): using_requested = is_current_version_studio_latest() + # TODO Handle situations when version can't be detected + if using_requested is None: + using_requested = True + self._restart_action.setVisible(not using_requested) if using_requested: if ( From c78eafa8eb1217a89af473bbe7b7fd93ab6291a2 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 18:48:59 +0100 Subject: [PATCH 18/26] changed order of version settings in UI --- .../defaults/system_settings/general.json | 8 +- .../schemas/system_schema/schema_general.json | 79 ++++++++++--------- .../settings/settings/wrapper_widgets.py | 3 +- 3 files changed, 48 insertions(+), 42 deletions(-) diff --git a/openpype/settings/defaults/system_settings/general.json b/openpype/settings/defaults/system_settings/general.json index 7c78de9a5c..5a3e39e5b6 100644 --- a/openpype/settings/defaults/system_settings/general.json +++ b/openpype/settings/defaults/system_settings/general.json @@ -2,9 +2,6 @@ "studio_name": "Studio name", "studio_code": "stu", "admin_password": "", - "production_version": "", - "staging_version": "", - "version_check_interval": 5, "environment": { "__environment_keys__": { "global": [] @@ -19,5 +16,8 @@ "windows": [], "darwin": [], "linux": [] - } + }, + "production_version": "", + "staging_version": "", + "version_check_interval": 5 } \ No newline at end of file diff --git a/openpype/settings/entities/schemas/system_schema/schema_general.json b/openpype/settings/entities/schemas/system_schema/schema_general.json index 3af3f5ce35..6306317df8 100644 --- a/openpype/settings/entities/schemas/system_schema/schema_general.json +++ b/openpype/settings/entities/schemas/system_schema/schema_general.json @@ -30,36 +30,6 @@ { "type": "splitter" }, - { - "type": "label", - "label": "Define explicit OpenPype version that should be used. Keep empty to use latest available version." - }, - { - "type": "production-versions-text", - "key": "production_version", - "label": "Production version" - }, - { - "type": "staging-versions-text", - "key": "staging_version", - "label": "Staging version" - }, - { - "type": "splitter" - }, - { - "type": "label", - "label": "Trigger validation if running OpenPype is using studio defined version each 'n' minutes. Validation happens in OpenPype tray application." - }, - { - "type": "number", - "key": "version_check_interval", - "label": "Version check interval", - "minimum": 0 - }, - { - "type": "splitter" - }, { "key": "environment", "label": "Environment", @@ -141,12 +111,49 @@ "type": "splitter" }, { - "type": "path", - "key": "openpype_path", - "label": "Versions Repository", - "multiplatform": true, - "multipath": true, - "require_restart": true + "type": "collapsible-wrap", + "label": "OpenPype deployment control", + "collapsible": false, + "children": [ + { + "type": "path", + "key": "openpype_path", + "label": "Versions Repository", + "multiplatform": true, + "multipath": true, + "require_restart": true + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Define explicit OpenPype version that should be used. Keep empty to use latest available version." + }, + { + "type": "production-versions-text", + "key": "production_version", + "label": "Production version" + }, + { + "type": "staging-versions-text", + "key": "staging_version", + "label": "Staging version" + }, + { + "type": "splitter" + }, + { + "type": "label", + "label": "Trigger validation if running OpenPype is using studio defined version each 'n' minutes. Validation happens in OpenPype tray application." + }, + { + "type": "number", + "key": "version_check_interval", + "label": "Version check interval", + "minimum": 0 + } + ] } ] } diff --git a/openpype/tools/settings/settings/wrapper_widgets.py b/openpype/tools/settings/settings/wrapper_widgets.py index b14a226912..7370fcf945 100644 --- a/openpype/tools/settings/settings/wrapper_widgets.py +++ b/openpype/tools/settings/settings/wrapper_widgets.py @@ -92,8 +92,7 @@ class CollapsibleWrapper(WrapperWidget): self.content_layout = content_layout if self.collapsible: - if not self.collapsed: - body_widget.toggle_content() + body_widget.toggle_content(self.collapsed) else: body_widget.hide_toolbox(hide_content=False) From 83204dde615ae723e1df7e15bc4b45ddf4a1a9ca Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Fri, 21 Jan 2022 19:08:54 +0100 Subject: [PATCH 19/26] added OpenPype deployment control to website docs --- website/docs/admin_settings_system.md | 21 +++++++++++++++++- .../settings/settings_system_general.png | Bin 33586 -> 45859 bytes .../settings_system_version_downgrade.png | Bin 0 -> 7777 bytes .../settings_system_version_update.png | Bin 0 -> 8435 bytes 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 website/docs/assets/settings/settings_system_version_downgrade.png create mode 100644 website/docs/assets/settings/settings_system_version_update.png diff --git a/website/docs/admin_settings_system.md b/website/docs/admin_settings_system.md index 6057ed0830..78be9fb01e 100644 --- a/website/docs/admin_settings_system.md +++ b/website/docs/admin_settings_system.md @@ -11,6 +11,8 @@ import TabItem from '@theme/TabItem'; Settings applicable to the full studio. +![general_settings](assets/settings/settings_system_general.png) + **`Studio Name`** - Full name of the studio (can be used as variable on some places) **`Studio Code`** - Studio acronym or a short code (can be used as variable on some places) @@ -24,10 +26,27 @@ as a naive barier to prevent artists from accidental setting changes. **`Disk mapping`** - Platform dependent configuration for mapping of virtual disk(s) on an artist's OpenPype machines before OP starts up. Uses `subst` command, if configured volume character in `Destination` field already exists, no re-mapping is done for that character(volume). +### OpenPype deployment control **`Versions Repository`** - Location where automatic update mechanism searches for zip files with OpenPype update packages. To read more about preparing OpenPype for automatic updates go to [Admin Distribute docs](admin_distribute#2-openpype-codebase) -![general_settings](assets/settings/settings_system_general.png) +**`Production version`** - Define what is current production version. When value is not set then latest version available in versions repository is resolved as production version. + +**`Staging version`** - Define what is current staging version. When value is not set then latest staging version available in versions repository is resolved as staging version. + +For more information about Production and Staging go to [Distribute](admin_distribute#staging-vs-production). + +**Production version** and **Staging version** fields will define which version will be used in studio. Filling explicit version will force new OpenPype processes to use it. That gives more control over studio deployment especially when some workstations don't have access to version repository (e.g. remote users). It can be also used to downgrade studio version when newer version have production breaking bug. + +When fields are not filled the latest version in versions repository is used as studio version. That makes updating easier as it is not needed to modify settings but workstations without access to versions repository can't find out which OpenPype version should be used. + +If version repository is not set or is not accessible for workstation the latest available version on workstation is used or version inside build. + +**`Version check interval`** - OpenPype tray application check if currently used OpenPype version is up to date with production/staging version. It is possible to modify how often the validation is triggered in minutes. It is possible to set the interval to `0`. That will turn off version validations but it is not recommend. + +A dialog asking for restart is shown when OpenPype tray application detect that different version should be used. +![general_settings](assets/settings/settings_system_version_update.png) +![general_settings](assets/settings/settings_system_version_downgrade.png) ## Modules diff --git a/website/docs/assets/settings/settings_system_general.png b/website/docs/assets/settings/settings_system_general.png index d04586205d24ee30445a80ce54330b2c3c03e77c..a2a684caeab6ce6538e8b7a5d8aaf2e76dfe902b 100644 GIT binary patch literal 45859 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 diff --git a/website/docs/assets/settings/settings_system_version_downgrade.png b/website/docs/assets/settings/settings_system_version_downgrade.png new file mode 100644 index 0000000000000000000000000000000000000000..277f2c6bc89a2f565c265e311a233b55d12fba01 GIT binary patch literal 7777 zcmdUUXIN8Bw{{ej2SFj!AVM%Sk=~^%5CLflN^d42AWfu~Br3h;ks>M}(p3luh7JKV zfb>p)&?NNGJE5KE^PcmbbDbaO=XZTS_MTa@XU(2j)dq1Lnixw>jQ6xD5c5py`huou|@t&vY!j0D#Ls zem`egJU%@J0JwK`wQfJ~w_Qm;PPBz4x6G$t#=*xd*5m6C`&*)fOIa^nVa(DpQ7;QW zo_CEJ$?{bgIMI-NC|X@3S1bJPI`G`J`wYM{_g}H-Nb7>Gxx*l#Nlc*Fi{$pAqUoij z8%rZ<$IsUmaq|=N0doO!xf#JmgZa{XzI%(S{p0h>dtHg|UYZ!HN1V*g5f^uBD*9{K z#dGU#x*=OXdz95G&L({uojXoBcx5c{8yYbX2c#Qg|h$Z^aJVJ0zbX%q)Lr95TWs9O- zAASY1Ob@K6{ld*n+mbvxXX_e*_J<7iwgo2zDR7@wmx0>;uDq84`56Xa?h&oj)k-vb zZBWz=V~^4_U61z!MRIZd>!IMO8)f>WC6u)X(aB-FLCvFjMP@r?Ud*`t@J#$xp0e8H z{n~sQTTPyb(6zAVwls#a?H34IE2A%o&(0>EJl9IquJK&l;aq&8n#bi=eqA8?z?UO;&VE0Y)ISG%Qh z#nOkaQ+CGsg{wulBSpeWm-|tpgEv<~9SpmCTCT-LR#f zdt6X;AH@Y7(|qJFT`tbsnn2vnb*=tHR1Q`>!pU*Zo_v=6I-(L^Wb4J*XfFw3c#~LS z&$!OVi8>QPrOgeSjdGZzKnxc`9Gs6R-Y-zME&%_0aG(2t=Y>%jF0}XT)9}+&OqN{& zFTeQBA^S|dmLuAup5%-O(fW5r3%^RrG5wub5~lwF}@#XwouMj z-1kH1BnG)nKHU=gJ%bCWJYEp_VgxW~`%pd4WmiShOzqhVQxBf}HqruZeoB(yDjrMhA@zCXy6x&=zRquvq zLQiKnn(GX4|K=r+QRt6Uzy6-_Ag6VlD)y+oX0fg&<72&AX`=1HN^!8uZ%O$)ojhLk zTa5Dv=DWneV9qXXt)qT=(2ZCeljfQH^&{f=;b3JfdAm)Cy+opRA3-6~W@Ker#l0AR z@}c>TUtdIpVbA=B4>{;KRXt6s-QKBP1oo&Gfqi^>)8^SW-RN!%3(hAb4kN}MX(6e! zNT2tk*$K1YVsC%Oz{RiMAu${KF$KQ%6WsDO&z*?865L?__51D$QCnf`SqNK^HctS4 z4im?vKi~M|oN=YG~!AMhGJ^{SDfnCn0~g|NDH)VjjV=));$MFmZ%GPW-LmwV1^DnT4}g z%q5=&hLw5LG4V!%806WM50}4TK6O6q$3Im%7aLbrzy~C5ey5{Mr3g8#Q+4A%Pm=ZC zbEnLwcMq4^o$UHlZ@&H^yZ=+c>7a-PJ2Pd1UC%xC-jaB))j>BjDuv?gKq}M^fWzbZMd{=7PF{ zp$l%EA9J`_u~15Txqm(7a_>~UN8e`MWR5^Y{UfOBVKHvrO2kyeRMh=io}v94PoeK6}=st<>PX%8xS0r=ujNjJ`bSaC-w}oLnqw#d0gjX-yIQNW9BT8vPT! z+)XfnUo)WeQxb<*(G*WH5I38Hfm&=CkOQ>*xKzxle0Zzjbk6>EZJu7kFx4h%&_;cS ze#`l6d#blZ%2)ClSAv|I=3AT>zOi68k$f;lRLq5z51F%vj8;Ia&CPSi`+M~p7&ekt zF0L&xDuX3O_woHcBj!iX?D35P8k2&L?GD?dfj1|g7~>3Q0wHiTX5|UP_bbOk==$|U zte+sUngUI3+h2lRGuSsdOq@fsRes)xYjJ?5#~0)>;tHea8GM;XxF*)a6%W5jcFyHQ zjt3k;s~@|9#ZLRgs~0jos~27$V~>zqYc_{Yvozg(>t^x2Q(f6%k%%2f^=&bi4nZri z%+ko#$3o2SOEk{81||L_P|oYdS8M|jCH_3A(&z8{+kWNPu!R@X>`_D7+nJsbTe|L9 z$kx{8^L4;3(OmXG|AdL7e+b>af ztH2N4U#jibUs}v5MO3`42OiVYQ&`LTCBdEhtC%NyZzV8(Nd1r*u~kM0E)X=aEh~ZQ zDjHa4#U<3H%klrbYvGzW=ln9!Z=W#opqVLksbXbAnN(i;T4XwBfAKE9w&_^cWm6b8 z=V@SpA4O9|BMqVlp~jF!#79spHmL5a%p^triuqP!*)CzE0$yQ+Of_Ad)La{f57%V8 zSw%$`DN|IAmn!PBPmjmf774|tFHd?FJr+OJPE2bFWSmVPUvJFy3CV3Ww9R*j8o}2L zsfgvSD06GV!Y1>oE);&M`OX@&*0^I2#|}5&7-f0E&Erk17?#?p1NN_m+0pF0lsB2 zJ}$MQ{Mc$)0l9WvOnLLBjlZ{qSfKlN`ww`W2{QBqeFt0Qzb$ZuGU|ohZn!pjIBFEE zt02D>RS?X^!O;3m=t-C^3zQ1B7DjY$f(N=c$aX@HUTClXyo8$WDJ6u2 z8yI)q^^)w2bnV}o9j;3M2qzW|wu@BW z^y5K)E~gRuCFdG+tYkkZIO=oce8nO+$pMdz86G71!}Xp>JIV0EuIYBP9Wf-I96rR( z{~bB<1Kd(8wVGadKi54ai|J^`9KT!@AMDEqTTN@;a!15}f!pVKW^}OEhSxDkk%UJ5 zm`Zl1;@Ho3@q9VicXw+NGcsdLHF_rF8+r4?p?q)jv@Mutx+*d%2nesqH_(V1%ETl1 zICi6qcAt*_-GLaN@>9am7w2+IZTbX?!_vz!t3E-3jL$^#W$Y0vL7?ZGxSOi3lQl^NfQ<(siKI)KZZ z5)~UoI}u}i3f==Ld>tcQG9yQ`s;m8YMgF^b30et4m*p9j%hUcQ&hC6+LURG}g z6e^M9$KG!3fu8!~)q^c^kWsJCli|MSao~qUH^)K?zahf*@-)Lxb0jCm7e z8Wu1ZM;s_t)x_1_)Xo&$-_F@GJU;M(wI|eB4y33>pQz+vaQXcp*{1fE>}!NB_HRf0 zZ!YxS8_^LohrP@)AeR1Kf4ua%y9^tlclf(cr$(-ifbZo;wsLU&IrHZ0Jc$J&FxCV4 z^_!t)MB;PYArg=|%pw!1t~IT|5T%#liFiiriQ^6xSZB zy4EL`xmrrty=nf&hP)h@K)-L!F)*;>lG0q@I|i-NSO+$z*yi(-iv~-`tj*U;aGJ`% zmaGoz2;Zl!$!akmY}Ob@uZ03WNMc-O%(3+Hb1*hmmDD0e`HILZiJe5i1ri zomT(2f8+I5UvT7e2a~qW2L?+~>H@;a2w|TXsftL11G3BPDp*_P&SJ%=Hg=;7Ga^j&#=Yo`>s}jb(f)VT5Zt*o!Vkm8#+NP*e6)1Vs&9n0Nc zM@p#Y6PEgbR+)~w_LBpV3FqOj?}XUgqrHTX;o75 zy>dGEMUo$(^RPZ$CTX<_Yln$rE*O2T9{%_k>zTiy(?T$;h1743bwFe!U?;Y~!kJr@ zMoDt6IoF?IXt;K|BXZ4IbH{iYfdp=;+0pRppZL$a^}i3Q_!&rPeuLD=@Kxz;)|)Hr z`1}g(slv!H4^*6N3*V;A;pMKt6XKeezLLd`WHH*TsJwME8*-()O8F8lhS4>2OlC3g zX^FvZVRozy$gH>CSl!ajLLn}IG#ZZJn`5I~^rYVck|6{*K`hl81y#>Pm2lKDa&|iz zU_5fuow&OBIQ_18Y33F;-*h6|Oh_CH!u5u79acY2EfF%Vi4%6M-_{GITd^|sfAES> zF>_W#!R}FX=5kjw{fd=v6o283uO|s_yUzBD!vgxd?zlrGg1w`vN0u&w7F-g9Iw2)x zOi`bW*%py!9i7AhK$q{xmi`sNbT^>av036@m#1{2HV0(5AuQ?w-6wwJewwZxYXC%0 zm*>a3)h5SXtwMh}UOOo36yQVB^&sm>@c8{`bM-Z;w9G2N|CKRxypJ3_p2tu0P*`4$ zofe)D}P8J&^bdl4z@aNOt}Wv;VL=W}XN zo{jdV?G;{hkIU1>FSoQL9_(u2qs@Q8i2!U2TS<(>F%$K0!c;_`gQ+AR5|^6M*HfSA~33cE1u4OCbCU(4n9+_3r_P7;JBClI< z1Y4NIW=rwcV=|5BNX7%Y%s$*j1{DzYw!7+&M3zS-cZ)m2cbJY_L2nPL>TR!5 zSV=Lkhvi1_^rAC++;gHCH{maL;lR+U<-0Hdz^OTUxVh8HdFOrmz(t$JsE>lcYRwG6 z6n={w-H+>onUMlGO^8V%oF4d%#6-H`iFl)XgI!QpsUjK${PwAc+X_LP{t!WACzZH4 zbR=v%gc+-!B=9otv{tc(GmqypF`*>*PpYLtH*Yj*gX{v+6QbX~imU3IXqi}zvE45b)*Vqw3C0onY&}zm820quco)>O8`% z@*^U;cbqxs{^tK|6>r3uveHc$Y<{v<7GENW>s5s^6nL`u_tFmx$qe6Q^+_JK=)>DZ zvmVBuYe#>pG>cWjYmpsiI~g?|gDs@5l1{Q-Na@?A?{3u_mdSI#Ui7Ov3I&e%LI?1nh|#r2MkI0fTvj zm95^xKUP*m-&5}?MFDQ{$xs?E2lXJNKMDSneV0#L1+8Lx{daASjuWD378reRSSyiV zEMg0eAY6=%-)Sw0#AiRU*5^VDG*vDb4aBYbT!V=NU3@f+#apf}J@vxEImqvA^B)`< z9L7L9R)AjV_?r7#p&%#$qou$9Tz$TeAcie4R`&qhief9t6%o(Ne3ush)K-J<}R| zoB@6`uEb4*1uorP4((`B!vCPxeeIpd??LFbR#sPglj+XHCY)1leU#Z)Z4T0yoCWnG zm?(P1CK}cnLBM>;448zVT0(&7ko17wUTKvX7ZHo5hNrP(>9EDYJWh8{M_|gc#e~DJ@{c*_Bv;?I1E%APzyFz;*bi7O_`)k zk1{V+Lw3^`&;Y>Iu*eM#%kuyV?bg7Rs4R$OT@GXc@WPe`NFxLs z%-7H>h6)Buy9HjM1$fhHF{EiZ6>H@GdYH~svLvDjxFzv2@(cj*lRXjy0K9Ph|BVoV z(GOwr{f;_m2Yz1yG*~x94+j;28xw!8X89|me7mcgr2Bif=W7mB-lGA3|F19pB*%qq zuZbd4Dj#6(hADpE5$2!%7XZMkP#F1(ZWhfS9_hGdZoZ?#Y;siKiBxs|WKRio5irQH=-NMU0GnS?I#NCa?{SbO<4i zfF|6xniQlCv@InH;1I7fXN$6)+EAe56v9Ed#7z_Ve$v~v7=}^zwYR`OjHKU7&Rpo$ zOa=d#Y;>Um&d?R-mv{M4cdiTY=RslSEgD<0d7bD+5&X&Yck&X!lIAyCHl0S1(-s`7 z_1ESdt9hkA|9TIaiI!KtqxXu}1M5@tPq(Psf4cGik8ao}p{we+kf8#HZxMeOKDPM; z8TRb|AbfK0y5yElei3=5r^N~Ob%Da|y}VC{7BoG+21~=X2T0R8dp{^9su6k?GP%9e zEr-<1RD%C5pDajGtOQH#s`B0CjsuSJbdEe*;Hmze^m{Kpk{R`K_Dhgct?4Il&^qZ$qoCcAf7*^Wi`*FK1?9p|p)e%K<+S{8n>FdxzZN^|o1+OGno>Vp~@yrvejoxu89EUJp6D|hs3hiGn?f; zB7Y|poGoxW4+4|dO=;~HoPpfe13t4Wn1-DfF=!z4utiC-A76Nx_^M~7zTr@Dh8g8$ z6S9(Tv5*FcDiPW+S9v-I#O)Rf#31V$l1V*nCX+j}rajESBqy>p)uCUg-J!}v8S;e8 zZ)8y}TIJaElm7LQrErRor6`h=svZ06JSy7b3*Yl^RX1T6$n^7#KQ_R zcrE3!2*&tV`%q>lkFdA*lFWLzi zTGB0aLe(v*JAYG)eCI0CGuPsz`*&1gxn9n!&z6Z!C~IW&b=^i!?_+*bK2Xc<@~7>G z30=i^vm=Eepux4BM05IoTm8#i17Hj)kFD*BR>vE{v3|$+w_@OdJsnH{l z7@c%7{@syvc>9i-&!(fbzDcQ3Q%{toGQ>1>z%5m)aCtaxQcDLCoUY_qcu`kLLdsA* zJPSz=)Pq~S)3ls8``@GmjbO=wvPcWVE{OOGKCm5Vq@ QY7;;gYN%DDVgKqs0NafSyZ`_I literal 0 HcmV?d00001 diff --git a/website/docs/assets/settings/settings_system_version_update.png b/website/docs/assets/settings/settings_system_version_update.png new file mode 100644 index 0000000000000000000000000000000000000000..50adbab1736700d433ff8627900152627cc5cd4f GIT binary patch literal 8435 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 Date: Mon, 24 Jan 2022 10:22:31 +0700 Subject: [PATCH 20/26] Variables exports --- tools/create_zip.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/create_zip.sh b/tools/create_zip.sh index 85ee18a839..46393f78b1 100755 --- a/tools/create_zip.sh +++ b/tools/create_zip.sh @@ -130,8 +130,8 @@ main () { fi echo -e "${BIGreen}>>>${RST} Generating zip from current sources ..." - PYTHONPATH="$openpype_root:$PYTHONPATH" - OPENPYPE_ROOT="$openpype_root" + export PYTHONPATH="$openpype_root:$PYTHONPATH" + export OPENPYPE_ROOT="$openpype_root" "$POETRY_HOME/bin/poetry" run python3 "$openpype_root/tools/create_zip.py" "$@" } From f0fcb1bfefd197cd00b694cc391565ff28d9be50 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Mon, 24 Jan 2022 09:40:08 +0100 Subject: [PATCH 21/26] add defaults --- .../defaults/project_anatomy/templates.json | 5 ++++ .../defaults/project_settings/global.json | 11 ++++++++ .../defaults/project_settings/maya.json | 27 ++++++++++--------- .../schemas/schema_anatomy_templates.json | 22 +++++++++++++++ 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index 9a03b893bf..d46d449c77 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -27,5 +27,10 @@ "path": "{@folder}/{@file}" }, "delivery": {}, + "unreal": { + "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", + "file": "{subset}_{@version}<_{output}><.{@frame}>.{ext}", + "path": "{@folder}/{@file}" + }, "others": {} } \ No newline at end of file diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index 2169a62746..da0bd454f3 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -264,6 +264,17 @@ "task_types": [], "tasks": [], "template": "render{Task}{Variant}" + }, + { + "families": [ + "unrealStaticMesh" + ], + "hosts": [ + "maya" + ], + "task_types": [], + "tasks": [], + "template": "S_{asset}{variant}" } ] }, diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index 67a7b84cdc..9490724ee8 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -46,6 +46,20 @@ "aov_separator": "underscore", "default_render_image_folder": "renders" }, + "CreateUnrealStaticMesh": { + "enabled": true, + "defaults": [ + "", + "_Main" + ], + "static_mesh_prefix": "S_", + "collision_prefixes": [ + "UBX", + "UCP", + "USP", + "UCX" + ] + }, "CreateAnimation": { "enabled": true, "defaults": [ @@ -123,19 +137,6 @@ "Anim" ] }, - "CreateUnrealStaticMesh": { - "enabled": true, - "defaults": [ - "Main" - ], - "static_mesh_prefix": "S_", - "collision_prefixes": [ - "UBX", - "UCP", - "USP", - "UCX" - ] - }, "CreateVrayProxy": { "enabled": true, "defaults": [ diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json index e208069e6f..0548824ee1 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_anatomy_templates.json @@ -143,6 +143,28 @@ "label": "Delivery", "object_type": "text" }, + { + "type": "dict", + "key": "unreal", + "label": "Unreal", + "children": [ + { + "type": "text", + "key": "folder", + "label": "Folder" + }, + { + "type": "text", + "key": "file", + "label": "File" + }, + { + "type": "text", + "key": "path", + "label": "Path" + } + ] + }, { "type": "dict-modifiable", "key": "others", From 105a9a4097753ed0a037869073d53c72374ccedf Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 24 Jan 2022 17:41:02 +0100 Subject: [PATCH 22/26] update avalon-core --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index ffe9e910f1..159d2f23e4 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit ffe9e910f1f382e222d457d8e4a8426c41ed43ae +Subproject commit 159d2f23e4c79c04dfac57b68d2ee6ac67adec1b From 506e61f05154c345025ae65d0f62a6cd5cf9adbb Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 24 Jan 2022 17:48:41 +0100 Subject: [PATCH 23/26] Updated submodule repos/avalon-core --- repos/avalon-core | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repos/avalon-core b/repos/avalon-core index 7e5efd6885..159d2f23e4 160000 --- a/repos/avalon-core +++ b/repos/avalon-core @@ -1 +1 @@ -Subproject commit 7e5efd6885330d84bb8495975bcab84df49bfa3d +Subproject commit 159d2f23e4c79c04dfac57b68d2ee6ac67adec1b From a68f4c46452a9f8018783a0c51688c692dfb3701 Mon Sep 17 00:00:00 2001 From: Milan Kolar Date: Mon, 24 Jan 2022 17:49:38 +0100 Subject: [PATCH 24/26] Removed submodule openpype/hosts/maya/vendor/studiolibrary --- openpype/hosts/maya/vendor/studiolibrary | 1 - 1 file changed, 1 deletion(-) delete mode 160000 openpype/hosts/maya/vendor/studiolibrary diff --git a/openpype/hosts/maya/vendor/studiolibrary b/openpype/hosts/maya/vendor/studiolibrary deleted file mode 160000 index f29e350da9..0000000000 --- a/openpype/hosts/maya/vendor/studiolibrary +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f29e350da9e9508522a740a4f30efb93b99c89d3 From f6fd68940b999894d122c91ecc116aaef38963cc Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Jan 2022 17:50:14 +0100 Subject: [PATCH 25/26] change label from "I know" to "Later" --- openpype/tools/tray/pype_tray.py | 2 +- .../settings_system_version_downgrade.png | Bin 7777 -> 7012 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index a21a9de705..cfd0aea2a1 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -180,7 +180,7 @@ class VersionDialog(QtWidgets.QDialog): "Running OpenPype version is {}." " Your production uses version {}." ).format(str(current_version), str(expected_version)) - ignore_label = "I know" + ignore_label = "Later" restart_label = "Restart && Change" self.setWindowTitle(title) diff --git a/website/docs/assets/settings/settings_system_version_downgrade.png b/website/docs/assets/settings/settings_system_version_downgrade.png index 277f2c6bc89a2f565c265e311a233b55d12fba01..b5e35fd1db56cd59e6ee827a63e2be41a6179d41 100644 GIT binary patch literal 7012 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 literal 7777 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 QY7;;gYN%DDVgKqs0NafSyZ`_I From cfd33342aefdeb4f8bb8a628cadaffee7e66bea4 Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Mon, 24 Jan 2022 18:01:06 +0100 Subject: [PATCH 26/26] Change label "Later" to "Ignore" --- openpype/tools/tray/pype_tray.py | 2 +- .../settings_system_version_downgrade.png | Bin 7012 -> 6937 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/openpype/tools/tray/pype_tray.py b/openpype/tools/tray/pype_tray.py index cfd0aea2a1..99d431172a 100644 --- a/openpype/tools/tray/pype_tray.py +++ b/openpype/tools/tray/pype_tray.py @@ -180,7 +180,7 @@ class VersionDialog(QtWidgets.QDialog): "Running OpenPype version is {}." " Your production uses version {}." ).format(str(current_version), str(expected_version)) - ignore_label = "Later" + ignore_label = "Ignore" restart_label = "Restart && Change" self.setWindowTitle(title) diff --git a/website/docs/assets/settings/settings_system_version_downgrade.png b/website/docs/assets/settings/settings_system_version_downgrade.png index b5e35fd1db56cd59e6ee827a63e2be41a6179d41..e3a5d7f499320eccda56909050b3666f6691f385 100644 GIT binary patch literal 6937 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