Merge pull request #5322 from ynput/enhancement/OP-5600_Houdini-manage-colorspaces-in-review-ROP

This commit is contained in:
Ondřej Samohel 2023-09-07 21:07:16 +02:00 committed by GitHub
commit ea94bacef6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 283 additions and 7 deletions

View file

@ -1,7 +1,7 @@
import attr
import hou
from openpype.hosts.houdini.api.lib import get_color_management_preferences
from openpype.pipeline.colorspace import get_display_view_colorspace_name
@attr.s
class LayerMetadata(object):
@ -54,3 +54,16 @@ class ARenderProduct(object):
)
]
return colorspace_data
def get_default_display_view_colorspace():
"""Returns the colorspace attribute of the default (display, view) pair.
It's used for 'ociocolorspace' parm in OpenGL Node."""
prefs = get_color_management_preferences()
return get_display_view_colorspace_name(
config_path=prefs["config"],
display=prefs["display"],
view=prefs["view"]
)

View file

@ -3,6 +3,9 @@
from openpype.hosts.houdini.api import plugin
from openpype.lib import EnumDef, BoolDef, NumberDef
import os
import hou
class CreateReview(plugin.HoudiniCreator):
"""Review with OpenGL ROP"""
@ -13,7 +16,6 @@ class CreateReview(plugin.HoudiniCreator):
icon = "video-camera"
def create(self, subset_name, instance_data, pre_create_data):
import hou
instance_data.pop("active", None)
instance_data.update({"node_type": "opengl"})
@ -82,6 +84,11 @@ class CreateReview(plugin.HoudiniCreator):
instance_node.setParms(parms)
# Set OCIO Colorspace to the default output colorspace
# if there's OCIO
if os.getenv("OCIO"):
self.set_colorcorrect_to_default_view_space(instance_node)
to_lock = ["id", "family"]
self.lock_parameters(instance_node, to_lock)
@ -123,3 +130,23 @@ class CreateReview(plugin.HoudiniCreator):
minimum=0.0001,
decimals=3)
]
def set_colorcorrect_to_default_view_space(self,
instance_node):
"""Set ociocolorspace to the default output space."""
from openpype.hosts.houdini.api.colorspace import get_default_display_view_colorspace # noqa
# set Color Correction parameter to OpenColorIO
instance_node.setParms({"colorcorrect": 2})
# Get default view space for ociocolorspace parm.
default_view_space = get_default_display_view_colorspace()
instance_node.setParms(
{"ociocolorspace": default_view_space}
)
self.log.debug(
"'OCIO Colorspace' parm on '{}' has been set to "
"the default view color space '{}'"
.format(instance_node, default_view_space)
)

View file

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
import pyblish.api
from openpype.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
)
from openpype.pipeline.publish import RepairAction
from openpype.hosts.houdini.api.action import SelectROPAction
import os
import hou
class SetDefaultViewSpaceAction(RepairAction):
label = "Set default view colorspace"
icon = "mdi.monitor"
class ValidateReviewColorspace(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Validate Review Colorspace parameters.
It checks if 'OCIO Colorspace' parameter was set to valid value.
"""
order = pyblish.api.ValidatorOrder + 0.1
families = ["review"]
hosts = ["houdini"]
label = "Validate Review Colorspace"
actions = [SetDefaultViewSpaceAction, SelectROPAction]
optional = True
def process(self, instance):
if not self.is_active(instance.data):
return
if os.getenv("OCIO") is None:
self.log.debug(
"Using Houdini's Default Color Management, "
" skipping check.."
)
return
rop_node = hou.node(instance.data["instance_node"])
if rop_node.evalParm("colorcorrect") != 2:
# any colorspace settings other than default requires
# 'Color Correct' parm to be set to 'OpenColorIO'
raise PublishValidationError(
"'Color Correction' parm on '{}' ROP must be set to"
" 'OpenColorIO'".format(rop_node.path())
)
if rop_node.evalParm("ociocolorspace") not in \
hou.Color.ocio_spaces():
raise PublishValidationError(
"Invalid value: Colorspace name doesn't exist.\n"
"Check 'OCIO Colorspace' parameter on '{}' ROP"
.format(rop_node.path())
)
@classmethod
def repair(cls, instance):
"""Set Default View Space Action.
It is a helper action more than a repair action,
used to set colorspace on opengl node to the default view.
"""
from openpype.hosts.houdini.api.colorspace import get_default_display_view_colorspace # noqa
rop_node = hou.node(instance.data["instance_node"])
if rop_node.evalParm("colorcorrect") != 2:
rop_node.setParms({"colorcorrect": 2})
cls.log.debug(
"'Color Correction' parm on '{}' has been set to"
" 'OpenColorIO'".format(rop_node.path())
)
# Get default view colorspace name
default_view_space = get_default_display_view_colorspace()
rop_node.setParms({"ociocolorspace": default_view_space})
cls.log.info(
"'OCIO Colorspace' parm on '{}' has been set to "
"the default view color space '{}'"
.format(rop_node, default_view_space)
)

View file

@ -721,3 +721,58 @@ def set_colorspace_data_to_representation(
# update data key
representation["colorspaceData"] = colorspace_data
def get_display_view_colorspace_name(config_path, display, view):
"""Returns the colorspace attribute of the (display, view) pair.
Args:
config_path (str): path string leading to config.ocio
display (str): display name e.g. "ACES"
view (str): view name e.g. "sRGB"
Returns:
view color space name (str) e.g. "Output - sRGB"
"""
if not compatibility_check():
# python environment is not compatible with PyOpenColorIO
# needs to be run in subprocess
return get_display_view_colorspace_subprocess(config_path,
display, view)
from openpype.scripts.ocio_wrapper import _get_display_view_colorspace_name # noqa
return _get_display_view_colorspace_name(config_path, display, view)
def get_display_view_colorspace_subprocess(config_path, display, view):
"""Returns the colorspace attribute of the (display, view) pair
via subprocess.
Args:
config_path (str): path string leading to config.ocio
display (str): display name e.g. "ACES"
view (str): view name e.g. "sRGB"
Returns:
view color space name (str) e.g. "Output - sRGB"
"""
with _make_temp_json_file() as tmp_json_path:
# Prepare subprocess arguments
args = [
"run", get_ocio_config_script_path(),
"config", "get_display_view_colorspace_name",
"--in_path", config_path,
"--out_path", tmp_json_path,
"--display", display,
"--view", view
]
log.debug("Executing: {}".format(" ".join(args)))
run_openpype_process(*args, logger=log)
# return default view colorspace name
with open(tmp_json_path, "r") as f:
return json.load(f)

View file

@ -174,5 +174,79 @@ def _get_views_data(config_path):
return data
def _get_display_view_colorspace_name(config_path, display, view):
"""Returns the colorspace attribute of the (display, view) pair.
Args:
config_path (str): path string leading to config.ocio
display (str): display name e.g. "ACES"
view (str): view name e.g. "sRGB"
Raises:
IOError: Input config does not exist.
Returns:
view color space name (str) e.g. "Output - sRGB"
"""
config_path = Path(config_path)
if not config_path.is_file():
raise IOError("Input path should be `config.ocio` file")
config = ocio.Config.CreateFromFile(str(config_path))
colorspace = config.getDisplayViewColorSpaceName(display, view)
return colorspace
@config.command(
name="get_display_view_colorspace_name",
help=(
"return default view colorspace name "
"for the given display and view "
"--path input arg is required"
)
)
@click.option("--in_path", required=True,
help="path where to read ocio config file",
type=click.Path(exists=True))
@click.option("--out_path", required=True,
help="path where to write output json file",
type=click.Path())
@click.option("--display", required=True,
help="display name",
type=click.STRING)
@click.option("--view", required=True,
help="view name",
type=click.STRING)
def get_display_view_colorspace_name(in_path, out_path,
display, view):
"""Aggregate view colorspace name to file.
Wrapper command for processes without access to OpenColorIO
Args:
in_path (str): config file path string
out_path (str): temp json file path string
display (str): display name e.g. "ACES"
view (str): view name e.g. "sRGB"
Example of use:
> pyton.exe ./ocio_wrapper.py config \
get_display_view_colorspace_name --in_path=<path> \
--out_path=<path> --display=<display> --view=<view>
"""
out_data = _get_display_view_colorspace_name(in_path,
display,
view)
with open(out_path, "w") as f:
json.dump(out_data, f)
print(f"Display view colorspace saved to '{out_path}'")
if __name__ == '__main__':
main()

View file

@ -93,6 +93,11 @@
"$JOB"
]
},
"ValidateReviewColorspace": {
"enabled": true,
"optional": true,
"active": true
},
"ValidateContainers": {
"enabled": true,
"optional": true,

View file

@ -40,6 +40,10 @@
"type": "schema_template",
"name": "template_publish_plugin",
"template_data": [
{
"key": "ValidateReviewColorspace",
"label": "Validate Review Colorspace"
},
{
"key": "ValidateContainers",
"label": "ValidateContainers"
@ -47,4 +51,4 @@
]
}
]
}
}

View file

@ -120,7 +120,7 @@ class ValidateWorkfilePathsModel(BaseSettingsModel):
)
class ValidateContainersModel(BaseSettingsModel):
class BasicValidateModel(BaseSettingsModel):
enabled: bool = Field(title="Enabled")
optional: bool = Field(title="Optional")
active: bool = Field(title="Active")
@ -130,8 +130,11 @@ class PublishPluginsModel(BaseSettingsModel):
ValidateWorkfilePaths: ValidateWorkfilePathsModel = Field(
default_factory=ValidateWorkfilePathsModel,
title="Validate workfile paths settings.")
ValidateContainers: ValidateContainersModel = Field(
default_factory=ValidateContainersModel,
ValidateReviewColorspace: BasicValidateModel = Field(
default_factory=BasicValidateModel,
title="Validate Review Colorspace.")
ValidateContainers: BasicValidateModel = Field(
default_factory=BasicValidateModel,
title="Validate Latest Containers.")
@ -148,6 +151,11 @@ DEFAULT_HOUDINI_PUBLISH_SETTINGS = {
"$JOB"
]
},
"ValidateReviewColorspace": {
"enabled": True,
"optional": True,
"active": True
},
"ValidateContainers": {
"enabled": True,
"optional": True,

View file

@ -1 +1 @@
__version__ = "0.1.1"
__version__ = "0.1.2"