From 18efd5276df132909d8eae592f09cd6d7b011cbb Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 30 Mar 2023 11:57:36 +0200 Subject: [PATCH 1/5] :art: add camera loader --- .../hosts/maya/plugins/load/load_camera.py | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 openpype/hosts/maya/plugins/load/load_camera.py diff --git a/openpype/hosts/maya/plugins/load/load_camera.py b/openpype/hosts/maya/plugins/load/load_camera.py new file mode 100644 index 0000000000..5e26e8c02f --- /dev/null +++ b/openpype/hosts/maya/plugins/load/load_camera.py @@ -0,0 +1,76 @@ +import openpype.hosts.maya.api.plugin +from openpype.hosts.maya.api.lib import get_container_members + + +class CameraLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): + """Reference Camera""" + + families = ["camera", "camerarig"] + label = "Reference camera" + representations = ["abc", "ma"] + order = -10 + icon = "code-fork" + color = "orange" + + def process_reference(self, context, name, namespace, data): + + import maya.cmds as cmds + # Get family type from the context + + cmds.loadPlugin("AbcImport.mll", quiet=True) + nodes = cmds.file(self.fname, + namespace=namespace, + sharedReferenceFile=False, + groupReference=True, + groupName="{}:{}".format(namespace, name), + reference=True, + returnNewNodes=True) + + cameras = cmds.ls(nodes, type="camera") + + # Check the Maya version, lockTransform has been introduced since + # Maya 2016.5 Ext 2 + version = int(cmds.about(version=True)) + if version >= 2016: + for camera in cameras: + cmds.camera(camera, edit=True, lockTransform=True) + else: + self.log.warning("This version of Maya does not support locking of" + " transforms of cameras.") + + self[:] = nodes + + return nodes + + def update(self, container, representation): + + from maya import cmds + + # Get the modelPanels that used the old camera + members = get_container_members(container) + old_cameras = cmds.ls(members, type="camera", long=True) + update_panels = [] + for panel in cmds.getPanel(type="modelPanel"): + cam = cmds.ls(cmds.modelPanel(panel, query=True, camera=True), + long=True) + + # Often but not always maya returns the transform from the + # modelPanel as opposed to the camera shape, so we convert it + # to explicitly be the camera shape + if cmds.nodeType(cam) != "camera": + cam = cmds.listRelatives(cam, + children=True, + fullPath=True, + type="camera")[0] + if cam in old_cameras: + update_panels.append(panel) + + # Perform regular reference update + super(CameraLoader, self).update(container, representation) + + # Update the modelPanels to contain the new camera + members = get_container_members(container) + new_cameras = cmds.ls(members, type="camera", long=True) + new_camera = new_cameras[0] + for panel in update_panels: + cmds.modelPanel(panel, edit=True, camera=new_camera) From 951f868cef89ad5c778511c4b7f3f95b7fc7ecb4 Mon Sep 17 00:00:00 2001 From: Ondrej Samohel Date: Thu, 30 Mar 2023 15:14:21 +0200 Subject: [PATCH 2/5] :recycle: move logic from camera loader to reference loader --- .../hosts/maya/plugins/load/load_camera.py | 76 ------------------- .../hosts/maya/plugins/load/load_reference.py | 57 +++++++++++++- 2 files changed, 56 insertions(+), 77 deletions(-) delete mode 100644 openpype/hosts/maya/plugins/load/load_camera.py diff --git a/openpype/hosts/maya/plugins/load/load_camera.py b/openpype/hosts/maya/plugins/load/load_camera.py deleted file mode 100644 index 5e26e8c02f..0000000000 --- a/openpype/hosts/maya/plugins/load/load_camera.py +++ /dev/null @@ -1,76 +0,0 @@ -import openpype.hosts.maya.api.plugin -from openpype.hosts.maya.api.lib import get_container_members - - -class CameraLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): - """Reference Camera""" - - families = ["camera", "camerarig"] - label = "Reference camera" - representations = ["abc", "ma"] - order = -10 - icon = "code-fork" - color = "orange" - - def process_reference(self, context, name, namespace, data): - - import maya.cmds as cmds - # Get family type from the context - - cmds.loadPlugin("AbcImport.mll", quiet=True) - nodes = cmds.file(self.fname, - namespace=namespace, - sharedReferenceFile=False, - groupReference=True, - groupName="{}:{}".format(namespace, name), - reference=True, - returnNewNodes=True) - - cameras = cmds.ls(nodes, type="camera") - - # Check the Maya version, lockTransform has been introduced since - # Maya 2016.5 Ext 2 - version = int(cmds.about(version=True)) - if version >= 2016: - for camera in cameras: - cmds.camera(camera, edit=True, lockTransform=True) - else: - self.log.warning("This version of Maya does not support locking of" - " transforms of cameras.") - - self[:] = nodes - - return nodes - - def update(self, container, representation): - - from maya import cmds - - # Get the modelPanels that used the old camera - members = get_container_members(container) - old_cameras = cmds.ls(members, type="camera", long=True) - update_panels = [] - for panel in cmds.getPanel(type="modelPanel"): - cam = cmds.ls(cmds.modelPanel(panel, query=True, camera=True), - long=True) - - # Often but not always maya returns the transform from the - # modelPanel as opposed to the camera shape, so we convert it - # to explicitly be the camera shape - if cmds.nodeType(cam) != "camera": - cam = cmds.listRelatives(cam, - children=True, - fullPath=True, - type="camera")[0] - if cam in old_cameras: - update_panels.append(panel) - - # Perform regular reference update - super(CameraLoader, self).update(container, representation) - - # Update the modelPanels to contain the new camera - members = get_container_members(container) - new_cameras = cmds.ls(members, type="camera", long=True) - new_camera = new_cameras[0] - for panel in update_panels: - cmds.modelPanel(panel, edit=True, camera=new_camera) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index d93702a16d..a0345dceb0 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -8,7 +8,10 @@ from openpype.pipeline.create import ( get_legacy_creator_by_name, ) import openpype.hosts.maya.api.plugin -from openpype.hosts.maya.api.lib import maintained_selection +from openpype.hosts.maya.api.lib import ( + maintained_selection, + get_container_members +) class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): @@ -68,6 +71,9 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): new_nodes = (list(set(nodes) - set(shapes))) + # if there are cameras, try to lock their transforms + self._lock_camera_transforms(new_nodes) + current_namespace = pm.namespaceInfo(currentNamespace=True) if current_namespace != ":": @@ -136,6 +142,11 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): def switch(self, container, representation): self.update(container, representation) + def update(self, container, representation): + update_panels = self._update_cameras(container) + super(ReferenceLoader, self).update(container, representation) + self._update_model_panels(container, update_panels) + def _post_process_rig(self, name, namespace, context, options): output = next((node for node in self if @@ -168,3 +179,47 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): options={"useSelection": True}, data={"dependencies": dependency} ) + + def _lock_camera_transforms(self, nodes): + cameras = cmds.ls(nodes, type="camera") + + # Check the Maya version, lockTransform has been introduced since + # Maya 2016.5 Ext 2 + version = int(cmds.about(version=True)) + if version >= 2016: + for camera in cameras: + cmds.camera(camera, edit=True, lockTransform=True) + else: + self.log.warning("This version of Maya does not support locking of" + " transforms of cameras.") + + @staticmethod + def _update_cameras(container): + # Get the modelPanels that used the old camera + members = get_container_members(container) + old_cameras = cmds.ls(members, type="camera", long=True) + update_panels = [] + for panel in cmds.getPanel(type="modelPanel"): + cam = cmds.ls(cmds.modelPanel(panel, query=True, camera=True), + long=True) + + # Often but not always maya returns the transform from the + # modelPanel as opposed to the camera shape, so we convert it + # to explicitly be the camera shape + if cmds.nodeType(cam) != "camera": + cam = cmds.listRelatives(cam, + children=True, + fullPath=True, + type="camera")[0] + if cam in old_cameras: + update_panels.append(panel) + return update_panels + + @staticmethod + def _update_model_panels(container, update_panels): + # Update the modelPanels to contain the new camera + members = get_container_members(container) + new_cameras = cmds.ls(members, type="camera", long=True) + new_camera = new_cameras[0] + for panel in update_panels: + cmds.modelPanel(panel, edit=True, camera=new_camera) From e62b0e48cb5c984595b36f20fb577cc84d5e1998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Samohel?= <33513211+antirotor@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:50:12 +0200 Subject: [PATCH 3/5] Update openpype/hosts/maya/plugins/load/load_reference.py Co-authored-by: Roy Nieterau --- openpype/hosts/maya/plugins/load/load_reference.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index a0345dceb0..60cae131bc 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -218,6 +218,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): @staticmethod def _update_model_panels(container, update_panels): # Update the modelPanels to contain the new camera + if not update_panels: + return members = get_container_members(container) new_cameras = cmds.ls(members, type="camera", long=True) new_camera = new_cameras[0] From 52b45f0384ea06be303f6e9392101f41c1b470d3 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Mar 2023 16:36:44 +0200 Subject: [PATCH 4/5] Correctly preserve cameras in modelPanels with multiple cameras in a mayaScene --- .../hosts/maya/plugins/load/load_reference.py | 114 ++++++++++++------ 1 file changed, 78 insertions(+), 36 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 60cae131bc..235bee8ae1 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -1,4 +1,6 @@ import os +import difflib +import contextlib from maya import cmds from openpype.settings import get_project_settings @@ -14,6 +16,78 @@ from openpype.hosts.maya.api.lib import ( ) +@contextlib.contextmanager +def preserve_modelpanel_cameras(container, log=None): + """Preserve camera members of container in the modelPanels. + + This is used to ensure a camera remains in the modelPanels after updating + to a new version. + + """ + + # Get the modelPanels that used the old camera + members = get_container_members(container) + old_cameras = set(cmds.ls(members, type="camera", long=True)) + if not old_cameras: + # No need to manage anything + yield + return + + panel_cameras = {} + for panel in cmds.getPanel(type="modelPanel"): + cam = cmds.ls(cmds.modelPanel(panel, query=True, camera=True), + long=True) + + # Often but not always maya returns the transform from the + # modelPanel as opposed to the camera shape, so we convert it + # to explicitly be the camera shape + if cmds.nodeType(cam) != "camera": + cam = cmds.listRelatives(cam, + children=True, + fullPath=True, + type="camera")[0] + if cam in old_cameras: + panel_cameras[panel] = cam + + if not panel_cameras: + # No need to manage anything + yield + return + + try: + yield + finally: + new_members = get_container_members(container) + new_cameras = set(cmds.ls(new_members, type="camera", long=True)) + if not new_cameras: + return + + for panel, cam_name in panel_cameras.items(): + new_camera = None + if cam_name in new_cameras: + new_camera = cam_name + elif len(new_cameras) == 1: + new_camera = next(iter(new_cameras)) + else: + # Multiple cameras in the updated container but not an exact + # match detected by name. Find the closest match + matches = difflib.get_close_matches(word=cam_name, + possibilities=new_cameras, + n=1) + if matches: + new_camera = matches[0] # best match + if log: + log.info("Camera in '{}' restored with " + "closest match camera: {} (before: {})" + .format(panel, new_camera, cam_name)) + + if not new_camera: + # Unable to find the camera to re-apply in the modelpanel + continue + + cmds.modelPanel(panel, edit=True, camera=new_camera) + + class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): """Reference file""" @@ -143,9 +217,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): self.update(container, representation) def update(self, container, representation): - update_panels = self._update_cameras(container) - super(ReferenceLoader, self).update(container, representation) - self._update_model_panels(container, update_panels) + with preserve_modelpanel_cameras(container, log=self.log): + super(ReferenceLoader, self).update(container, representation) def _post_process_rig(self, name, namespace, context, options): @@ -182,6 +255,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): def _lock_camera_transforms(self, nodes): cameras = cmds.ls(nodes, type="camera") + if not cameras: + return # Check the Maya version, lockTransform has been introduced since # Maya 2016.5 Ext 2 @@ -192,36 +267,3 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): else: self.log.warning("This version of Maya does not support locking of" " transforms of cameras.") - - @staticmethod - def _update_cameras(container): - # Get the modelPanels that used the old camera - members = get_container_members(container) - old_cameras = cmds.ls(members, type="camera", long=True) - update_panels = [] - for panel in cmds.getPanel(type="modelPanel"): - cam = cmds.ls(cmds.modelPanel(panel, query=True, camera=True), - long=True) - - # Often but not always maya returns the transform from the - # modelPanel as opposed to the camera shape, so we convert it - # to explicitly be the camera shape - if cmds.nodeType(cam) != "camera": - cam = cmds.listRelatives(cam, - children=True, - fullPath=True, - type="camera")[0] - if cam in old_cameras: - update_panels.append(panel) - return update_panels - - @staticmethod - def _update_model_panels(container, update_panels): - # Update the modelPanels to contain the new camera - if not update_panels: - return - members = get_container_members(container) - new_cameras = cmds.ls(members, type="camera", long=True) - new_camera = new_cameras[0] - for panel in update_panels: - cmds.modelPanel(panel, edit=True, camera=new_camera) From 945d31876685fbe1264732748e8f9fbeed8d8d29 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Thu, 30 Mar 2023 16:38:45 +0200 Subject: [PATCH 5/5] Also lock new cameras on update --- openpype/hosts/maya/plugins/load/load_reference.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 235bee8ae1..82c15ab899 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -220,6 +220,11 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): with preserve_modelpanel_cameras(container, log=self.log): super(ReferenceLoader, self).update(container, representation) + # We also want to lock camera transforms on any new cameras in the + # reference or for a camera which might have changed names. + members = get_container_members(container) + self._lock_camera_transforms(members) + def _post_process_rig(self, name, namespace, context, options): output = next((node for node in self if