From 4ac950aa604b5107d68f314df94472148148f2f5 Mon Sep 17 00:00:00 2001 From: Roy Nieterau Date: Mon, 6 Mar 2023 15:26:55 +0100 Subject: [PATCH] Set color space from publish using representation color space data or falling back to imageio settings file rules in OP settings --- .../hosts/maya/plugins/load/load_image.py | 142 ++++++++++++++---- 1 file changed, 110 insertions(+), 32 deletions(-) diff --git a/openpype/hosts/maya/plugins/load/load_image.py b/openpype/hosts/maya/plugins/load/load_image.py index c08724dca0..0e535f1692 100644 --- a/openpype/hosts/maya/plugins/load/load_image.py +++ b/openpype/hosts/maya/plugins/load/load_image.py @@ -1,16 +1,24 @@ +import os import copy + +from openpype.lib import EnumDef from openpype.pipeline import ( load, get_representation_context ) -from openpype.lib import EnumDef +from openpype.pipeline.load.utils import get_representation_path_from_context +from openpype.pipeline.colorspace import ( + get_imageio_colorspace_from_filepath, + get_imageio_config, + get_imageio_file_rules +) +from openpype.settings import get_project_settings + from openpype.hosts.maya.api.pipeline import containerise from openpype.hosts.maya.api.lib import ( unique_namespace, namespaced ) -from openpype.pipeline.load.utils import get_representation_path_from_context - from maya import cmds @@ -83,7 +91,6 @@ def create_stencil(): class FileNodeLoader(load.LoaderPlugin): """File node loader.""" - # TODO: Implement color space manamagent OCIO (set correct color space) families = ["image", "plate", "render"] label = "Load file node" @@ -107,7 +114,6 @@ class FileNodeLoader(load.LoaderPlugin): def load(self, context, name, namespace, data): - path = self._format_path(context) asset = context['asset']['name'] namespace = namespace or unique_namespace( asset + "_", @@ -115,10 +121,6 @@ class FileNodeLoader(load.LoaderPlugin): suffix="_", ) - repre_context = context["representation"]["context"] - has_frames = repre_context.get("frame") is not None - has_udim = repre_context.get("udim") is not None - with namespaced(namespace, new=True) as namespace: # Create the nodes within the namespace nodes = { @@ -126,31 +128,14 @@ class FileNodeLoader(load.LoaderPlugin): "projection": create_projection, "stencil": create_stencil }[data.get("mode", "texture")]() - file_node = cmds.ls(nodes, type="file")[0] - # Set UV tiling mode if UDIM tiles - if has_udim: - cmds.setAttr(file_node + ".uvTilingMode", 3) # UDIM-tiles + file_node = cmds.ls(nodes, type="file")[0] - # Enable sequence if publish has `startFrame` and `endFrame` and - # `startFrame != endFrame` - if has_frames: - is_sequence = self._is_sequence(context) - if is_sequence: - # When enabling useFrameExtension maya automatically - # connects an expression to .frameExtension to set - # the current frame. However, this expression is generated - # with some delay and thus it'll show a warning if frame 0 - # doesn't exist because we're explicitly setting the - # token. - cmds.setAttr(file_node + ".useFrameExtension", True) + self._apply_representation_context(context, file_node) - # Set the file node path attribute - cmds.setAttr(file_node + ".fileTextureName", path, type="string") - - # For ease of access for the user select all the nodes and select - # the file node last so that UI shows its attributes by default - cmds.select(list(nodes) + [file_node], replace=True) + # For ease of access for the user select all the nodes and select + # the file node last so that UI shows its attributes by default + cmds.select(list(nodes) + [file_node], replace=True) return containerise( name=name, @@ -167,7 +152,7 @@ class FileNodeLoader(load.LoaderPlugin): members = cmds.sets(container['objectName'], query=True) file_node = cmds.ls(members, type="file")[0] - cmds.setAttr(file_node + ".fileTextureName", path, type="string") + self._apply_representation_context(context, file_node) # Update representation cmds.setAttr( @@ -191,6 +176,51 @@ class FileNodeLoader(load.LoaderPlugin): except RuntimeError: pass + def _apply_representation_context(self, context, file_node): + """Update the file node to match the context. + + This sets the file node's attributes for: + - file path + - udim tiling mode (if it is an udim tile) + - use frame extension (if it is a sequence) + - colorspace + + """ + + repre_context = context["representation"]["context"] + has_frames = repre_context.get("frame") is not None + has_udim = repre_context.get("udim") is not None + + # Set UV tiling mode if UDIM tiles + if has_udim: + cmds.setAttr(file_node + ".uvTilingMode", 3) # UDIM-tiles + else: + cmds.setAttr(file_node + ".uvTilingMode", 0) # off + + # Enable sequence if publish has `startFrame` and `endFrame` and + # `startFrame != endFrame` + if has_frames and self._is_sequence(context): + # When enabling useFrameExtension maya automatically + # connects an expression to .frameExtension to set + # the current frame. However, this expression is generated + # with some delay and thus it'll show a warning if frame 0 + # doesn't exist because we're explicitly setting the + # token. + cmds.setAttr(file_node + ".useFrameExtension", True) + else: + cmds.setAttr(file_node + ".useFrameExtension", False) + + # Set the file node path attribute + path = self._format_path(context) + cmds.setAttr(file_node + ".fileTextureName", path, type="string") + + # Set colorspace + colorspace = self._get_colorspace(context) + if colorspace: + cmds.setAttr(file_node + ".colorSpace", colorspace, type="string") + else: + self.log.debug("Unknown colorspace - setting colorspace skipped.") + def _is_sequence(self, context): """Check whether frameStart and frameEnd are not the same.""" version = context.get("version", {}) @@ -213,6 +243,54 @@ class FileNodeLoader(load.LoaderPlugin): return False + def _get_colorspace(self, context): + """Return colorspace of the file to load. + + Retrieves the explicit colorspace from the publish. If no colorspace + data is stored with published content then project imageio settings + are used to make an assumption of the colorspace based on the file + rules. If no file rules match then None is returned. + + Returns: + str or None: The colorspace of the file or None if not detected. + + """ + + # We can't apply color spaces if management is not enabled + if not cmds.colorManagementPrefs(query=True, cmEnabled=True): + return + + representation = context["representation"] + colorspace_data = representation.get("data", {}).get("colorspaceData") + if colorspace_data: + return colorspace_data["colorspace"] + + # Assume colorspace from filepath based on project settings + project_name = context["project"]["name"] + host_name = os.environ.get("AVALON_APP") + project_settings = get_project_settings(project_name) + + config_data = get_imageio_config( + project_name, host_name, + project_settings=project_settings + ) + file_rules = get_imageio_file_rules( + project_name, host_name, + project_settings=project_settings + ) + + path = get_representation_path_from_context(context) + colorspace = get_imageio_colorspace_from_filepath( + path=path, + host_name=host_name, + project_name=project_name, + config_data=config_data, + file_rules=file_rules, + project_settings=project_settings + ) + + return colorspace + def _format_path(self, context): """Format the path with correct tokens for frames and udim tiles."""