Merge branch 'bugfix/OP-2513_Houdini-Camera-Loader-fix-mismatch-for-Maya-cameras-render-gates-using-aspect-ratio' of https://github.com/ynput/OpenPype into bugfix/OP-2513_Houdini-Camera-Loader-fix-mismatch-for-Maya-cameras-render-gates-using-aspect-ratio

This commit is contained in:
Mustafa-Zarkash 2023-09-07 22:30:54 +03:00
commit 613498894b
70 changed files with 542 additions and 293 deletions

View file

@ -663,10 +663,13 @@ def convert_v4_representation_to_v3(representation):
if isinstance(context, six.string_types):
context = json.loads(context)
if "folder" in context:
_c_folder = context.pop("folder")
if "asset" not in context and "folder" in context:
_c_folder = context["folder"]
context["asset"] = _c_folder["name"]
elif "asset" in context and "folder" not in context:
context["folder"] = {"name": context["asset"]}
if "product" in context:
_c_product = context.pop("product")
context["family"] = _c_product["type"]
@ -959,9 +962,11 @@ def convert_create_representation_to_v4(representation, con):
converted_representation["files"] = new_files
context = representation["context"]
context["folder"] = {
"name": context.pop("asset", None)
}
if "folder" not in context:
context["folder"] = {
"name": context.get("asset")
}
context["product"] = {
"type": context.pop("family", None),
"name": context.pop("subset", None),
@ -1285,7 +1290,7 @@ def convert_update_representation_to_v4(
if "context" in update_data:
context = update_data["context"]
if "asset" in context:
if "folder" not in context and "asset" in context:
context["folder"] = {"name": context.pop("asset")}
if "family" in context or "subset" in context:

View file

@ -119,7 +119,7 @@ class BlendLoader(plugin.AssetLoader):
context: Full parenthood of representation to load
options: Additional settings dictionary
"""
libpath = self.fname
libpath = self.filepath_from_context(context)
asset = context["asset"]["name"]
subset = context["subset"]["name"]

View file

@ -100,7 +100,7 @@ class AbcCameraLoader(plugin.AssetLoader):
asset_group = bpy.data.objects.new(group_name, object_data=None)
avalon_container.objects.link(asset_group)
objects = self._process(libpath, asset_group, group_name)
self._process(libpath, asset_group, group_name)
objects = []
nodes = list(asset_group.children)

View file

@ -103,7 +103,7 @@ class FbxCameraLoader(plugin.AssetLoader):
asset_group = bpy.data.objects.new(group_name, object_data=None)
avalon_container.objects.link(asset_group)
objects = self._process(libpath, asset_group, group_name)
self._process(libpath, asset_group, group_name)
objects = []
nodes = list(asset_group.children)

View file

@ -21,8 +21,6 @@ class ExtractABC(publish.Extractor):
filename = f"{instance.name}.abc"
filepath = os.path.join(stagingdir, filename)
context = bpy.context
# Perform extraction
self.log.info("Performing extraction..")

View file

@ -20,8 +20,6 @@ class ExtractAnimationABC(publish.Extractor):
filename = f"{instance.name}.abc"
filepath = os.path.join(stagingdir, filename)
context = bpy.context
# Perform extraction
self.log.info("Performing extraction..")

View file

@ -21,16 +21,11 @@ class ExtractCameraABC(publish.Extractor):
filename = f"{instance.name}.abc"
filepath = os.path.join(stagingdir, filename)
context = bpy.context
# Perform extraction
self.log.info("Performing extraction..")
plugin.deselect_all()
selected = []
active = None
asset_group = None
for obj in instance:
if obj.get(AVALON_PROPERTY):

View file

@ -48,7 +48,6 @@ class LoadClip(opfapi.ClipLoader):
self.fpd = fproject.current_workspace.desktop
# load clip to timeline and get main variables
namespace = namespace
version = context['version']
version_data = version.get("data", {})
version_name = version.get("name", None)

View file

@ -45,7 +45,6 @@ class LoadClipBatch(opfapi.ClipLoader):
self.batch = options.get("batch") or flame.batch
# load clip to timeline and get main variables
namespace = namespace
version = context['version']
version_data = version.get("data", {})
version_name = version.get("name", None)

View file

@ -325,7 +325,6 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
def _create_shot_instance(self, context, clip_name, **data):
master_layer = data.get("heroTrack")
hierarchy_data = data.get("hierarchyData")
asset = data.get("asset")
if not master_layer:
return

View file

@ -82,7 +82,6 @@ class TemplateLoader(load.LoaderPlugin):
node = harmony.find_node_by_name(node_name, "GROUP")
self_name = self.__class__.__name__
update_and_replace = False
if is_representation_from_latest(representation):
self._set_green(node)
else:

View file

@ -317,20 +317,6 @@ class Spacer(QtWidgets.QWidget):
self.setLayout(layout)
def get_reference_node_parents(ref):
"""Return all parent reference nodes of reference node
Args:
ref (str): reference node.
Returns:
list: The upstream parent reference nodes.
"""
parents = []
return parents
class SequenceLoader(LoaderPlugin):
"""A basic SequenceLoader for Resolve

View file

@ -43,7 +43,6 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
if review and review_track_index == _track_index:
continue
for sitem in sub_track_items:
effect = None
# make sure this subtrack item is relative of track item
if ((track_item not in sitem.linkedItems())
and (len(sitem.linkedItems()) > 0)):
@ -53,7 +52,6 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
continue
effect = self.add_effect(_track_index, sitem)
if effect:
effects.update(effect)

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

@ -34,7 +34,6 @@ class BgeoLoader(load.LoaderPlugin):
# Create a new geo node
container = obj.createNode("geo", node_name=node_name)
is_sequence = bool(context["representation"]["context"].get("frame"))
# Remove the file node, it only loads static meshes
# Houdini 17 has removed the file node from the geo node

View file

@ -80,14 +80,9 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
def get_beauty_render_product(self, prefix, suffix="<reName>"):
"""Return the beauty output filename if render element enabled
"""
# Remove aov suffix from the product: `prefix.aov_suffix` -> `prefix`
aov_parm = ".{}".format(suffix)
beauty_product = None
if aov_parm in prefix:
beauty_product = prefix.replace(aov_parm, "")
else:
beauty_product = prefix
return beauty_product
return prefix.replace(aov_parm, "")
def get_render_element_name(self, node, prefix, suffix="<reName>"):
"""Return the output filename using the AOV prefix and suffix

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

@ -37,13 +37,10 @@ class RenderSettings(object):
def set_render_camera(self, selection):
for sel in selection:
# to avoid Attribute Error from pymxs wrapper
found = False
if rt.classOf(sel) in rt.Camera.classes:
found = True
rt.viewport.setCamera(sel)
break
if not found:
raise RuntimeError("Active Camera not found")
return
raise RuntimeError("Active Camera not found")
def render_output(self, container):
folder = rt.maxFilePath

View file

@ -14,7 +14,6 @@ class CreateRender(plugin.MaxCreator):
def create(self, subset_name, instance_data, pre_create_data):
from pymxs import runtime as rt
sel_obj = list(rt.selection)
file = rt.maxFileName
filename, _ = os.path.splitext(file)
instance_data["AssetName"] = filename

View file

@ -30,7 +30,6 @@ class CollectRender(pyblish.api.InstancePlugin):
asset = get_current_asset_name()
files_by_aov = RenderProducts().get_beauty(instance.name)
folder = folder.replace("\\", "/")
aovs = RenderProducts().get_aovs(instance.name)
files_by_aov.update(aovs)

View file

@ -22,8 +22,6 @@ class ExtractCameraAlembic(publish.Extractor, OptionalPyblishPluginMixin):
start = float(instance.data.get("frameStartHandle", 1))
end = float(instance.data.get("frameEndHandle", 1))
container = instance.data["instance_node"]
self.log.info("Extracting Camera ...")
stagingdir = self.staging_dir(instance)

View file

@ -19,9 +19,8 @@ class ExtractCameraFbx(publish.Extractor, OptionalPyblishPluginMixin):
def process(self, instance):
if not self.is_active(instance.data):
return
container = instance.data["instance_node"]
self.log.info("Extracting Camera ...")
self.log.debug("Extracting Camera ...")
stagingdir = self.staging_dir(instance)
filename = "{name}.fbx".format(**instance.data)

View file

@ -18,10 +18,9 @@ class ExtractMaxSceneRaw(publish.Extractor, OptionalPyblishPluginMixin):
def process(self, instance):
if not self.is_active(instance.data):
return
container = instance.data["instance_node"]
# publish the raw scene for camera
self.log.info("Extracting Raw Max Scene ...")
self.log.debug("Extracting Raw Max Scene ...")
stagingdir = self.staging_dir(instance)
filename = "{name}.max".format(**instance.data)

View file

@ -20,9 +20,7 @@ class ExtractModel(publish.Extractor, OptionalPyblishPluginMixin):
if not self.is_active(instance.data):
return
container = instance.data["instance_node"]
self.log.info("Extracting Geometry ...")
self.log.debug("Extracting Geometry ...")
stagingdir = self.staging_dir(instance)
filename = "{name}.abc".format(**instance.data)

View file

@ -20,10 +20,7 @@ class ExtractModelFbx(publish.Extractor, OptionalPyblishPluginMixin):
if not self.is_active(instance.data):
return
container = instance.data["instance_node"]
self.log.info("Extracting Geometry ...")
self.log.debug("Extracting Geometry ...")
stagingdir = self.staging_dir(instance)
filename = "{name}.fbx".format(**instance.data)

View file

@ -20,9 +20,7 @@ class ExtractModelObj(publish.Extractor, OptionalPyblishPluginMixin):
if not self.is_active(instance.data):
return
container = instance.data["instance_node"]
self.log.info("Extracting Geometry ...")
self.log.debug("Extracting Geometry ...")
stagingdir = self.staging_dir(instance)
filename = "{name}.obj".format(**instance.data)

View file

@ -54,8 +54,6 @@ class ExtractAlembic(publish.Extractor):
start = float(instance.data.get("frameStartHandle", 1))
end = float(instance.data.get("frameEndHandle", 1))
container = instance.data["instance_node"]
self.log.debug("Extracting pointcache ...")
parent_dir = self.staging_dir(instance)

View file

@ -16,11 +16,10 @@ class ExtractRedshiftProxy(publish.Extractor):
families = ["redshiftproxy"]
def process(self, instance):
container = instance.data["instance_node"]
start = int(instance.context.data.get("frameStart"))
end = int(instance.context.data.get("frameEnd"))
self.log.info("Extracting Redshift Proxy...")
self.log.debug("Extracting Redshift Proxy...")
stagingdir = self.staging_dir(instance)
rs_filename = "{name}.rs".format(**instance.data)
rs_filepath = os.path.join(stagingdir, rs_filename)

View file

@ -6,11 +6,6 @@ from openpype.pipeline import (
from pymxs import runtime as rt
from openpype.hosts.max.api.lib import reset_scene_resolution
from openpype.pipeline.context_tools import (
get_current_project_asset,
get_current_project
)
class ValidateResolutionSetting(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
@ -43,22 +38,16 @@ class ValidateResolutionSetting(pyblish.api.InstancePlugin,
"on asset or shot.")
def get_db_resolution(self, instance):
data = ["data.resolutionWidth", "data.resolutionHeight"]
project_resolution = get_current_project(fields=data)
project_resolution_data = project_resolution["data"]
asset_resolution = get_current_project_asset(fields=data)
asset_resolution_data = asset_resolution["data"]
# Set project resolution
project_width = int(
project_resolution_data.get("resolutionWidth", 1920))
project_height = int(
project_resolution_data.get("resolutionHeight", 1080))
width = int(
asset_resolution_data.get("resolutionWidth", project_width))
height = int(
asset_resolution_data.get("resolutionHeight", project_height))
asset_doc = instance.data["assetEntity"]
project_doc = instance.context.data["projectEntity"]
for data in [asset_doc["data"], project_doc["data"]]:
if "resolutionWidth" in data and "resolutionHeight" in data:
width = data["resolutionWidth"]
height = data["resolutionHeight"]
return int(width), int(height)
return width, height
# Defaults if not found in asset document or project document
return 1920, 1080
@classmethod
def repair(cls, instance):

View file

@ -177,12 +177,7 @@ class RenderSettings(object):
# list all the aovs
all_rs_aovs = cmds.ls(type='RedshiftAOV')
for rs_aov in redshift_aovs:
rs_layername = rs_aov
if " " in rs_aov:
rs_renderlayer = rs_aov.replace(" ", "")
rs_layername = "rsAov_{}".format(rs_renderlayer)
else:
rs_layername = "rsAov_{}".format(rs_aov)
rs_layername = "rsAov_{}".format(rs_aov.replace(" ", ""))
if rs_layername in all_rs_aovs:
continue
cmds.rsCreateAov(type=rs_aov)
@ -317,7 +312,7 @@ class RenderSettings(object):
separators = [cmds.menuItem(i, query=True, label=True) for i in items] # noqa: E501
try:
sep_idx = separators.index(aov_separator)
except ValueError as e:
except ValueError:
six.reraise(
CreatorError,
CreatorError(

View file

@ -683,7 +683,6 @@ class ReferenceLoader(Loader):
loaded_containers.append(container)
self._organize_containers(nodes, container)
c += 1
namespace = None
return loaded_containers

View file

@ -12,7 +12,6 @@ class ImportReference(InventoryAction):
color = "#d8d8d8"
def process(self, containers):
references = cmds.ls(type="reference")
for container in containers:
if container["loader"] != "ReferenceLoader":
print("Not a reference, skipping")

View file

@ -43,8 +43,6 @@ class MultiverseUsdLoader(load.LoaderPlugin):
import multiverse
# Create the shape
shape = None
transform = None
with maintained_selection():
cmds.namespace(addNamespace=namespace)
with namespaced(namespace, new=False):

View file

@ -205,7 +205,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
cmds.setAttr("{}.selectHandleZ".format(group_name), cz)
if family == "rig":
self._post_process_rig(name, namespace, context, options)
self._post_process_rig(namespace, context, options)
else:
if "translate" in options:
if not attach_to_root and new_nodes:
@ -229,7 +229,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
members = get_container_members(container)
self._lock_camera_transforms(members)
def _post_process_rig(self, name, namespace, context, options):
def _post_process_rig(self, namespace, context, options):
nodes = self[:]
create_rig_animation_instance(

View file

@ -53,8 +53,6 @@ class XgenLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
)
# Reference xgen. Xgen does not like being referenced in under a group.
new_nodes = []
with maintained_selection():
nodes = cmds.file(
maya_filepath,

View file

@ -15,6 +15,16 @@ from openpype.hosts.maya.api import lib
from openpype.hosts.maya.api.pipeline import containerise
# Do not reset these values on update but only apply on first load
# to preserve any potential local overrides
SKIP_UPDATE_ATTRS = {
"displayOutput",
"viewportDensity",
"viewportWidth",
"viewportLength",
}
def set_attribute(node, attr, value):
"""Wrapper of set attribute which ignores None values"""
if value is None:
@ -205,6 +215,8 @@ class YetiCacheLoader(load.LoaderPlugin):
yeti_node = yeti_nodes[0]
for attr, value in node_settings["attrs"].items():
if attr in SKIP_UPDATE_ATTRS:
continue
set_attribute(attr, value, yeti_node)
cmds.setAttr("{}.representation".format(container_node),
@ -311,7 +323,6 @@ class YetiCacheLoader(load.LoaderPlugin):
# Update attributes with defaults
attributes = node_settings["attrs"]
attributes.update({
"viewportDensity": 0.1,
"verbosity": 2,
"fileMode": 1,
@ -321,6 +332,9 @@ class YetiCacheLoader(load.LoaderPlugin):
"visibleInRefractions": True
})
if "viewportDensity" not in attributes:
attributes["viewportDensity"] = 0.1
# Apply attributes to pgYetiMaya node
for attr, value in attributes.items():
set_attribute(attr, value, yeti_node)

View file

@ -281,7 +281,6 @@ class CollectMultiverseLookData(pyblish.api.InstancePlugin):
long=True)
nodes.update(nodes_of_interest)
files = []
sets = {}
instance.data["resources"] = []
publishMipMap = instance.data["publishMipMap"]

View file

@ -4,12 +4,23 @@ import pyblish.api
from openpype.hosts.maya.api import lib
SETTINGS = {"renderDensity",
"renderWidth",
"renderLength",
"increaseRenderBounds",
"imageSearchPath",
"cbId"}
SETTINGS = {
# Preview
"displayOutput",
"colorR", "colorG", "colorB",
"viewportDensity",
"viewportWidth",
"viewportLength",
# Render attributes
"renderDensity",
"renderWidth",
"renderLength",
"increaseRenderBounds",
"imageSearchPath",
# Pipeline specific
"cbId"
}
class CollectYetiCache(pyblish.api.InstancePlugin):
@ -39,10 +50,6 @@ class CollectYetiCache(pyblish.api.InstancePlugin):
# Get yeti nodes and their transforms
yeti_shapes = cmds.ls(instance, type="pgYetiMaya")
for shape in yeti_shapes:
shape_data = {"transform": None,
"name": shape,
"cbId": lib.get_id(shape),
"attrs": None}
# Get specific node attributes
attr_data = {}
@ -58,9 +65,12 @@ class CollectYetiCache(pyblish.api.InstancePlugin):
parent = cmds.listRelatives(shape, parent=True)[0]
transform_data = {"name": parent, "cbId": lib.get_id(parent)}
# Store collected data
shape_data["attrs"] = attr_data
shape_data["transform"] = transform_data
shape_data = {
"transform": transform_data,
"name": shape,
"cbId": lib.get_id(shape),
"attrs": attr_data,
}
settings["nodes"].append(shape_data)

View file

@ -30,8 +30,8 @@ class ExtractImportReference(publish.Extractor,
tmp_format = "_tmp"
@classmethod
def apply_settings(cls, project_setting, system_settings):
cls.active = project_setting["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa
def apply_settings(cls, project_settings):
cls.active = project_settings["deadline"]["publish"]["MayaSubmitDeadline"]["import_reference"] # noqa
def process(self, instance):
if not self.is_active(instance.data):

View file

@ -37,7 +37,7 @@ class ValidateMayaUnits(pyblish.api.ContextPlugin):
)
@classmethod
def apply_settings(cls, project_settings, system_settings):
def apply_settings(cls, project_settings):
"""Apply project settings to creator"""
settings = (
project_settings["maya"]["publish"]["ValidateMayaUnits"]

View file

@ -90,15 +90,13 @@ class AssetOutliner(QtWidgets.QWidget):
def get_all_assets(self):
"""Add all items from the current scene"""
items = []
with preserve_expanded_rows(self.view):
with preserve_selection(self.view):
self.clear()
nodes = commands.get_all_asset_nodes()
items = commands.create_items_from_nodes(nodes)
self.add_items(items)
return len(items) > 0
return len(items) > 0
def get_selected_assets(self):
"""Add all selected items from the current scene"""

View file

@ -112,8 +112,6 @@ class AlembicCameraLoader(load.LoaderPlugin):
version_doc = get_version_by_id(project_name, representation["parent"])
object_name = container['objectName']
# get corresponding node
camera_node = nuke.toNode(object_name)
# get main variables
version_data = version_doc.get("data", {})

View file

@ -20,7 +20,6 @@ class ExtractReviewDataLut(publish.Extractor):
hosts = ["nuke"]
def process(self, instance):
families = instance.data["families"]
self.log.info("Creating staging dir...")
if "representations" in instance.data:
staging_dir = instance.data[

View file

@ -91,8 +91,6 @@ class ExtractThumbnail(publish.Extractor):
if collection:
# get path
fname = os.path.basename(collection.format(
"{head}{padding}{tail}"))
fhead = collection.format("{head}")
thumb_fname = list(collection)[mid_frame]

View file

@ -16,7 +16,6 @@ class CollectAutoImage(pyblish.api.ContextPlugin):
targets = ["automated"]
def process(self, context):
family = "image"
for instance in context:
creator_identifier = instance.data.get("creator_identifier")
if creator_identifier and creator_identifier == "auto_image":

View file

@ -413,8 +413,6 @@ class ClipLoader:
if self.with_handles:
source_in -= handle_start
source_out += handle_end
handle_start = 0
handle_end = 0
# make track item from source in bin as item
timeline_item = lib.create_timeline_item(
@ -433,14 +431,6 @@ class ClipLoader:
self.data["path"], self.active_bin)
_clip_property = media_pool_item.GetClipProperty
# get handles
handle_start = self.data["versionData"].get("handleStart")
handle_end = self.data["versionData"].get("handleEnd")
if handle_start is None:
handle_start = int(self.data["assetData"]["handleStart"])
if handle_end is None:
handle_end = int(self.data["assetData"]["handleEnd"])
source_in = int(_clip_property("Start"))
source_out = int(_clip_property("End"))

View file

@ -49,8 +49,6 @@ class ExtractThumbnailSP(pyblish.api.InstancePlugin):
else:
first_filename = files
staging_dir = None
# Convert to jpeg if not yet
full_input_path = os.path.join(
thumbnail_repre["stagingDir"], first_filename

View file

@ -2,16 +2,18 @@ import pyblish.api
from openpype.pipeline import OptionalPyblishPluginMixin
class CollectFrameDataFromAssetEntity(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Collect Frame Range data From Asset Entity
class CollectMissingFrameDataFromAssetEntity(
pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin
):
"""Collect Missing Frame Range data From Asset Entity
Frame range data will only be collected if the keys
are not yet collected for the instance.
"""
order = pyblish.api.CollectorOrder + 0.491
label = "Collect Frame Data From Asset Entity"
label = "Collect Missing Frame Data From Asset Entity"
families = ["plate", "pointcache",
"vdbcache", "online",
"render"]

View file

@ -15,7 +15,7 @@ class ValidateFrameRange(OptionalPyblishPluginMixin,
label = "Validate Frame Range"
hosts = ["traypublisher"]
families = ["render"]
families = ["render", "plate"]
order = ValidateContentsOrder
optional = True

View file

@ -171,7 +171,7 @@ class LoadImage(plugin.Loader):
george_script = "\n".join(george_script_lines)
execute_george_through_file(george_script)
def _remove_container(self, container, members=None):
def _remove_container(self, container):
if not container:
return
representation = container["representation"]

View file

@ -63,7 +63,6 @@ class ExtractSequence(pyblish.api.Extractor):
"ignoreLayersTransparency", False
)
family_lowered = instance.data["family"].lower()
mark_in = instance.context.data["sceneMarkIn"]
mark_out = instance.context.data["sceneMarkOut"]
@ -76,11 +75,9 @@ class ExtractSequence(pyblish.api.Extractor):
# Frame start/end may be stored as float
frame_start = int(instance.data["frameStart"])
frame_end = int(instance.data["frameEnd"])
# Handles are not stored per instance but on Context
handle_start = instance.context.data["handleStart"]
handle_end = instance.context.data["handleEnd"]
scene_bg_color = instance.context.data["sceneBgColor"]

View file

@ -19,9 +19,8 @@ class ExtractUAsset(publish.Extractor):
"umap" if "umap" in instance.data.get("families") else "uasset")
ar = unreal.AssetRegistryHelpers.get_asset_registry()
self.log.info("Performing extraction..")
self.log.debug("Performing extraction..")
staging_dir = self.staging_dir(instance)
filename = f"{instance.name}.{extension}"
members = instance.data.get("members", [])

View file

@ -724,7 +724,7 @@ def get_ffprobe_data(path_to_file, logger=None):
"""
if not logger:
logger = logging.getLogger(__name__)
logger.info(
logger.debug(
"Getting information about input \"{}\".".format(path_to_file)
)
ffprobe_args = get_ffmpeg_tool_args("ffprobe")

View file

@ -27,8 +27,8 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
def process(self, instance):
component_list = instance.data.get("ftrackComponentsList")
if not component_list:
self.log.info(
"Instance don't have components to integrate to Ftrack."
self.log.debug(
"Instance doesn't have components to integrate to Ftrack."
" Skipping."
)
return
@ -37,7 +37,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
task_entity, parent_entity = self.get_instance_entities(
instance, context)
if parent_entity is None:
self.log.info((
self.log.debug((
"Skipping ftrack integration. Instance \"{}\" does not"
" have specified ftrack entities."
).format(str(instance)))
@ -323,7 +323,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
"type_id": asset_type_id,
"context_id": parent_id
}
self.log.info("Created new Asset with data: {}.".format(asset_data))
self.log.debug("Created new Asset with data: {}.".format(asset_data))
session.create("Asset", asset_data)
session.commit()
return self._query_asset(session, asset_name, asset_type_id, parent_id)
@ -384,7 +384,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
if comment:
new_asset_version_data["comment"] = comment
self.log.info("Created new AssetVersion with data {}".format(
self.log.debug("Created new AssetVersion with data {}".format(
new_asset_version_data
))
session.create("AssetVersion", new_asset_version_data)
@ -555,7 +555,7 @@ class IntegrateFtrackApi(pyblish.api.InstancePlugin):
location=location
)
data["component"] = component_entity
self.log.info(
self.log.debug(
(
"Created new Component with path: {0}, data: {1},"
" metadata: {2}, location: {3}"

View file

@ -40,7 +40,7 @@ class IntegrateFtrackDescription(pyblish.api.InstancePlugin):
comment = instance.data["comment"]
if not comment:
self.log.info("Comment is not set.")
self.log.debug("Comment is not set.")
else:
self.log.debug("Comment is set to `{}`".format(comment))

View file

@ -47,7 +47,7 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin):
app_label = context.data["appLabel"]
comment = instance.data["comment"]
if not comment:
self.log.info("Comment is not set.")
self.log.debug("Comment is not set.")
else:
self.log.debug("Comment is set to `{}`".format(comment))
@ -127,14 +127,14 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin):
note_text = StringTemplate.format_template(template, format_data)
if not note_text.solved:
self.log.warning((
self.log.debug((
"Note template require more keys then can be provided."
"\nTemplate: {}\nMissing values for keys:{}\nData: {}"
).format(template, note_text.missing_keys, format_data))
continue
if not note_text:
self.log.info((
self.log.debug((
"Note for AssetVersion {} would be empty. Skipping."
"\nTemplate: {}\nData: {}"
).format(asset_version["id"], template, format_data))

View file

@ -121,7 +121,7 @@ class IntegrateKitsuNote(pyblish.api.ContextPlugin):
publish_comment = self.format_publish_comment(instance)
if not publish_comment:
self.log.info("Comment is not set.")
self.log.debug("Comment is not set.")
else:
self.log.debug("Comment is `{}`".format(publish_comment))

View file

@ -13,12 +13,17 @@ from openpype.lib import (
Logger
)
from openpype.pipeline import Anatomy
from openpype.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS
log = Logger.get_logger(__name__)
class CashedData:
remapping = None
class CachedData:
remapping = {}
allowed_exts = {
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
}
@contextlib.contextmanager
@ -546,15 +551,15 @@ def get_remapped_colorspace_to_native(
Union[str, None]: native colorspace name defined in remapping or None
"""
CashedData.remapping.setdefault(host_name, {})
if CashedData.remapping[host_name].get("to_native") is None:
CachedData.remapping.setdefault(host_name, {})
if CachedData.remapping[host_name].get("to_native") is None:
remapping_rules = imageio_host_settings["remapping"]["rules"]
CashedData.remapping[host_name]["to_native"] = {
CachedData.remapping[host_name]["to_native"] = {
rule["ocio_name"]: rule["host_native_name"]
for rule in remapping_rules
}
return CashedData.remapping[host_name]["to_native"].get(
return CachedData.remapping[host_name]["to_native"].get(
ocio_colorspace_name)
@ -572,15 +577,15 @@ def get_remapped_colorspace_from_native(
Union[str, None]: Ocio colorspace name defined in remapping or None.
"""
CashedData.remapping.setdefault(host_name, {})
if CashedData.remapping[host_name].get("from_native") is None:
CachedData.remapping.setdefault(host_name, {})
if CachedData.remapping[host_name].get("from_native") is None:
remapping_rules = imageio_host_settings["remapping"]["rules"]
CashedData.remapping[host_name]["from_native"] = {
CachedData.remapping[host_name]["from_native"] = {
rule["host_native_name"]: rule["ocio_name"]
for rule in remapping_rules
}
return CashedData.remapping[host_name]["from_native"].get(
return CachedData.remapping[host_name]["from_native"].get(
host_native_colorspace_name)
@ -601,3 +606,173 @@ def _get_imageio_settings(project_settings, host_name):
imageio_host = project_settings.get(host_name, {}).get("imageio", {})
return imageio_global, imageio_host
def get_colorspace_settings_from_publish_context(context_data):
"""Returns solved settings for the host context.
Args:
context_data (publish.Context.data): publishing context data
Returns:
tuple | bool: config, file rules or None
"""
if "imageioSettings" in context_data and context_data["imageioSettings"]:
return context_data["imageioSettings"]
project_name = context_data["projectName"]
host_name = context_data["hostName"]
anatomy_data = context_data["anatomyData"]
project_settings_ = context_data["project_settings"]
config_data = get_imageio_config(
project_name, host_name,
project_settings=project_settings_,
anatomy_data=anatomy_data
)
# caching invalid state, so it's not recalculated all the time
file_rules = None
if config_data:
file_rules = get_imageio_file_rules(
project_name, host_name,
project_settings=project_settings_
)
# caching settings for future instance processing
context_data["imageioSettings"] = (config_data, file_rules)
return config_data, file_rules
def set_colorspace_data_to_representation(
representation, context_data,
colorspace=None,
log=None
):
"""Sets colorspace data to representation.
Args:
representation (dict): publishing representation
context_data (publish.Context.data): publishing context data
colorspace (str, optional): colorspace name. Defaults to None.
log (logging.Logger, optional): logger instance. Defaults to None.
Example:
```
{
# for other publish plugins and loaders
"colorspace": "linear",
"config": {
# for future references in case need
"path": "/abs/path/to/config.ocio",
# for other plugins within remote publish cases
"template": "{project[root]}/path/to/config.ocio"
}
}
```
"""
log = log or Logger.get_logger(__name__)
file_ext = representation["ext"]
# check if `file_ext` in lower case is in CachedData.allowed_exts
if file_ext.lstrip(".").lower() not in CachedData.allowed_exts:
log.debug(
"Extension '{}' is not in allowed extensions.".format(file_ext)
)
return
# get colorspace settings
config_data, file_rules = get_colorspace_settings_from_publish_context(
context_data)
# in case host color management is not enabled
if not config_data:
log.warning("Host's colorspace management is disabled.")
return
log.debug("Config data is: `{}`".format(config_data))
project_name = context_data["projectName"]
host_name = context_data["hostName"]
project_settings = context_data["project_settings"]
# get one filename
filename = representation["files"]
if isinstance(filename, list):
filename = filename[0]
# get matching colorspace from rules
colorspace = colorspace or get_imageio_colorspace_from_filepath(
filename, host_name, project_name,
config_data=config_data,
file_rules=file_rules,
project_settings=project_settings
)
# infuse data to representation
if colorspace:
colorspace_data = {
"colorspace": colorspace,
"config": config_data
}
# 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

@ -234,6 +234,19 @@ class LoaderPlugin(list):
"""
return cls.options or []
@property
def fname(self):
"""Backwards compatibility with deprecation warning"""
self.log.warning((
"DEPRECATION WARNING: Source - Loader plugin {}."
" The 'fname' property on the Loader plugin will be removed in"
" future versions of OpenPype. Planned version to drop the support"
" is 3.16.6 or 3.17.0."
).format(self.__class__.__name__))
if hasattr(self, "_fname"):
return self._fname
class SubsetLoaderPlugin(LoaderPlugin):
"""Load subset into host application

View file

@ -318,7 +318,8 @@ def load_with_repre_context(
# Backwards compatibility: Originally the loader's __init__ required the
# representation context to set `fname` attribute to the filename to load
loader.fname = get_representation_path_from_context(repre_context)
# Deprecated - to be removed in OpenPype 3.16.6 or 3.17.0.
loader._fname = get_representation_path_from_context(repre_context)
return loader.load(repre_context, name, namespace, options)

View file

@ -1,6 +1,5 @@
import inspect
from abc import ABCMeta
from pprint import pformat
import pyblish.api
from pyblish.plugin import MetaPlugin, ExplicitMetaPlugin
from openpype.lib.transcoding import VIDEO_EXTENSIONS, IMAGE_EXTENSIONS
@ -14,9 +13,8 @@ from .lib import (
)
from openpype.pipeline.colorspace import (
get_imageio_colorspace_from_filepath,
get_imageio_config,
get_imageio_file_rules
get_colorspace_settings_from_publish_context,
set_colorspace_data_to_representation
)
@ -306,12 +304,8 @@ class ColormanagedPyblishPluginMixin(object):
matching colorspace from rules. Finally, it infuses this
data into the representation.
"""
allowed_ext = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
)
@staticmethod
def get_colorspace_settings(context):
def get_colorspace_settings(self, context):
"""Returns solved settings for the host context.
Args:
@ -320,50 +314,18 @@ class ColormanagedPyblishPluginMixin(object):
Returns:
tuple | bool: config, file rules or None
"""
if "imageioSettings" in context.data:
return context.data["imageioSettings"]
project_name = context.data["projectName"]
host_name = context.data["hostName"]
anatomy_data = context.data["anatomyData"]
project_settings_ = context.data["project_settings"]
config_data = get_imageio_config(
project_name, host_name,
project_settings=project_settings_,
anatomy_data=anatomy_data
)
# in case host color management is not enabled
if not config_data:
return None
file_rules = get_imageio_file_rules(
project_name, host_name,
project_settings=project_settings_
)
# caching settings for future instance processing
context.data["imageioSettings"] = (config_data, file_rules)
return config_data, file_rules
return get_colorspace_settings_from_publish_context(context.data)
def set_representation_colorspace(
self, representation, context,
colorspace=None,
colorspace_settings=None
):
"""Sets colorspace data to representation.
Args:
representation (dict): publishing representation
context (publish.Context): publishing context
config_data (dict): host resolved config data
file_rules (dict): host resolved file rules data
colorspace (str, optional): colorspace name. Defaults to None.
colorspace_settings (tuple[dict, dict], optional):
Settings for config_data and file_rules.
Defaults to None.
Example:
```
@ -380,64 +342,10 @@ class ColormanagedPyblishPluginMixin(object):
```
"""
ext = representation["ext"]
# check extension
self.log.debug("__ ext: `{}`".format(ext))
# check if ext in lower case is in self.allowed_ext
if ext.lstrip(".").lower() not in self.allowed_ext:
self.log.debug(
"Extension '{}' is not in allowed extensions.".format(ext)
)
return
if colorspace_settings is None:
colorspace_settings = self.get_colorspace_settings(context)
# in case host color management is not enabled
if not colorspace_settings:
self.log.warning("Host's colorspace management is disabled.")
return
# unpack colorspace settings
config_data, file_rules = colorspace_settings
if not config_data:
# warn in case no colorspace path was defined
self.log.warning("No colorspace management was defined")
return
self.log.debug("Config data is: `{}`".format(config_data))
project_name = context.data["projectName"]
host_name = context.data["hostName"]
project_settings = context.data["project_settings"]
# get one filename
filename = representation["files"]
if isinstance(filename, list):
filename = filename[0]
self.log.debug("__ filename: `{}`".format(filename))
# get matching colorspace from rules
colorspace = colorspace or get_imageio_colorspace_from_filepath(
filename, host_name, project_name,
config_data=config_data,
file_rules=file_rules,
project_settings=project_settings
# using cached settings if available
set_colorspace_data_to_representation(
representation, context.data,
colorspace,
log=self.log
)
self.log.debug("__ colorspace: `{}`".format(colorspace))
# infuse data to representation
if colorspace:
colorspace_data = {
"colorspace": colorspace,
"config": config_data
}
# update data key
representation["colorspaceData"] = colorspace_data
self.log.debug("__ colorspace_data: `{}`".format(
pformat(colorspace_data)))

View file

@ -50,4 +50,7 @@ class CollectSequenceFrameData(pyblish.api.InstancePlugin):
return {
"frameStart": repres_frames[0],
"frameEnd": repres_frames[-1],
"handleStart": 0,
"handleEnd": 0,
"fps": instance.context.data["assetEntity"]["data"]["fps"]
}

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

@ -31,7 +31,6 @@ class ScreenMarquee(QtWidgets.QDialog):
fade_anim.setEndValue(50)
fade_anim.setDuration(200)
fade_anim.setEasingCurve(QtCore.QEasingCurve.OutCubic)
fade_anim.start(QtCore.QAbstractAnimation.DeleteWhenStopped)
fade_anim.valueChanged.connect(self._on_fade_anim)
@ -46,7 +45,7 @@ class ScreenMarquee(QtWidgets.QDialog):
for screen in QtWidgets.QApplication.screens():
screen.geometryChanged.connect(self._fit_screen_geometry)
self._opacity = fade_anim.currentValue()
self._opacity = fade_anim.startValue()
self._click_pos = None
self._capture_rect = None

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"

View file

@ -28,10 +28,9 @@ class TestPipelineColorspace(TestPipeline):
cd to OpenPype repo root dir
poetry run python ./start.py runtests ../tests/unit/openpype/pipeline
"""
TEST_FILES = [
(
"1Lf-mFxev7xiwZCWfImlRcw7Fj8XgNQMh",
"1csqimz8bbNcNgxtEXklLz6GRv91D3KgA",
"test_pipeline_colorspace.zip",
""
)