mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge branch 'develop' into enhancement/AY-2130_Maxsceneloader
This commit is contained in:
commit
875e5e1681
12 changed files with 207 additions and 72 deletions
|
|
@ -13,6 +13,9 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
families = ["model", "camera", "rig", "action", "layout", "blendScene"]
|
||||
optional = True
|
||||
|
||||
# From settings
|
||||
compress = False
|
||||
|
||||
def process(self, instance):
|
||||
if not self.is_active(instance.data):
|
||||
return
|
||||
|
|
@ -53,7 +56,7 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
if node.image and node.image.packed_file is None:
|
||||
node.image.pack()
|
||||
|
||||
bpy.data.libraries.write(filepath, data_blocks)
|
||||
bpy.data.libraries.write(filepath, data_blocks, compress=self.compress)
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ class ExtractBlendAnimation(
|
|||
families = ["animation"]
|
||||
optional = True
|
||||
|
||||
# From settings
|
||||
compress = False
|
||||
|
||||
def process(self, instance):
|
||||
if not self.is_active(instance.data):
|
||||
return
|
||||
|
|
@ -46,7 +49,7 @@ class ExtractBlendAnimation(
|
|||
data_blocks.add(child.animation_data.action)
|
||||
data_blocks.add(obj)
|
||||
|
||||
bpy.data.libraries.write(filepath, data_blocks)
|
||||
bpy.data.libraries.write(filepath, data_blocks, compress=self.compress)
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Validate if instance asset is the same as context asset."""
|
||||
|
||||
import pyblish.api
|
||||
from ayon_core.hosts.houdini.api.action import SelectROPAction
|
||||
from ayon_core.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
PublishValidationError,
|
||||
OptionalPyblishPluginMixin
|
||||
)
|
||||
|
||||
|
||||
class ValidateInstanceInContextHoudini(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
"""Validator to check if instance asset match context asset.
|
||||
|
||||
When working in per-shot style you always publish data in context of
|
||||
current asset (shot). This validator checks if this is so. It is optional
|
||||
so it can be disabled when needed.
|
||||
"""
|
||||
# Similar to maya-equivalent `ValidateInstanceInContext`
|
||||
|
||||
order = ValidateContentsOrder
|
||||
label = "Instance in same Context"
|
||||
optional = True
|
||||
hosts = ["houdini"]
|
||||
actions = [SelectROPAction, RepairAction]
|
||||
|
||||
def process(self, instance):
|
||||
if not self.is_active(instance.data):
|
||||
return
|
||||
|
||||
folder_path = instance.data.get("folderPath")
|
||||
task = instance.data.get("task")
|
||||
context = self.get_context(instance)
|
||||
if (folder_path, task) != context:
|
||||
context_label = "{} > {}".format(*context)
|
||||
instance_label = "{} > {}".format(folder_path, task)
|
||||
|
||||
raise PublishValidationError(
|
||||
message=(
|
||||
"Instance '{}' publishes to different asset than current "
|
||||
"context: {}. Current context: {}".format(
|
||||
instance.name, instance_label, context_label
|
||||
)
|
||||
),
|
||||
description=(
|
||||
"## Publishing to a different asset\n"
|
||||
"There are publish instances present which are publishing "
|
||||
"into a different asset than your current context.\n\n"
|
||||
"Usually this is not what you want but there can be cases "
|
||||
"where you might want to publish into another asset or "
|
||||
"shot. If that's the case you can disable the validation "
|
||||
"on the instance to ignore it."
|
||||
)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
context_folder, context_task = cls.get_context(instance)
|
||||
|
||||
create_context = instance.context.data["create_context"]
|
||||
instance_id = instance.data["instance_id"]
|
||||
created_instance = create_context.get_instance_by_id(
|
||||
instance_id
|
||||
)
|
||||
created_instance["folderPath"] = context_folder
|
||||
created_instance["task"] = context_task
|
||||
create_context.save_changes()
|
||||
|
||||
@staticmethod
|
||||
def get_context(instance):
|
||||
"""Return folderPath, task from publishing context data"""
|
||||
context = instance.context
|
||||
return context.data["folderPath"], context.data["task"]
|
||||
|
|
@ -98,21 +98,6 @@ class CreateWorkfile(plugin.MaxCreatorBase, AutoCreator):
|
|||
created_inst.data_to_store()
|
||||
)
|
||||
|
||||
def remove_instances(self, instances):
|
||||
"""Remove specified instance from the scene.
|
||||
|
||||
This is only removing `id` parameter so instance is no longer
|
||||
instance, because it might contain valuable data for artist.
|
||||
|
||||
"""
|
||||
for instance in instances:
|
||||
instance_node = rt.GetNodeByName(
|
||||
instance.data.get("instance_node"))
|
||||
if instance_node:
|
||||
rt.Delete(instance_node)
|
||||
|
||||
self._remove_instance_from_context(instance)
|
||||
|
||||
def create_node(self, product_name):
|
||||
if rt.getNodeByName(product_name):
|
||||
node = rt.getNodeByName(product_name)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import os
|
||||
import re
|
||||
|
||||
import nuke
|
||||
|
||||
from ayon_core import resources
|
||||
|
|
@ -103,9 +105,8 @@ def colorspace_exists_on_node(node, colorspace_name):
|
|||
except ValueError:
|
||||
# knob is not available on input node
|
||||
return False
|
||||
all_clrs = get_colorspace_list(colorspace_knob)
|
||||
|
||||
return colorspace_name in all_clrs
|
||||
return colorspace_name in get_colorspace_list(colorspace_knob)
|
||||
|
||||
|
||||
def get_colorspace_list(colorspace_knob):
|
||||
|
|
@ -117,19 +118,22 @@ def get_colorspace_list(colorspace_knob):
|
|||
Returns:
|
||||
list: list of strings names of profiles
|
||||
"""
|
||||
results = []
|
||||
|
||||
all_clrs = list(colorspace_knob.values())
|
||||
reduced_clrs = []
|
||||
# This pattern is to match with roles which uses an indentation and
|
||||
# parentheses with original colorspace. The value returned from the
|
||||
# colorspace is the string before the indentation, so we'll need to
|
||||
# convert the values to match with value returned from the knob,
|
||||
# ei. knob.value().
|
||||
pattern = r".*\t.* \(.*\)"
|
||||
for colorspace in nuke.getColorspaceList(colorspace_knob):
|
||||
match = re.search(pattern, colorspace)
|
||||
if match:
|
||||
results.append(colorspace.split("\t", 1)[0])
|
||||
else:
|
||||
results.append(colorspace)
|
||||
|
||||
if not colorspace_knob.getFlag(nuke.STRIP_CASCADE_PREFIX):
|
||||
return all_clrs
|
||||
|
||||
# strip colorspace with nested path
|
||||
for clrs in all_clrs:
|
||||
clrs = clrs.split('/')[-1]
|
||||
reduced_clrs.append(clrs)
|
||||
|
||||
return reduced_clrs
|
||||
return results
|
||||
|
||||
|
||||
def is_headless():
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ from ayon_core.pipeline import (
|
|||
get_current_project_name,
|
||||
get_representation_path,
|
||||
)
|
||||
from ayon_core.pipeline.colorspace import (
|
||||
get_imageio_file_rules_colorspace_from_filepath
|
||||
)
|
||||
from ayon_core.hosts.nuke.api.lib import (
|
||||
get_imageio_input_colorspace,
|
||||
maintained_selection
|
||||
|
|
@ -101,7 +104,6 @@ class LoadClip(plugin.NukeLoader):
|
|||
|
||||
filepath = self.filepath_from_context(context)
|
||||
filepath = filepath.replace("\\", "/")
|
||||
self.log.debug("_ filepath: {}".format(filepath))
|
||||
|
||||
start_at_workfile = options.get(
|
||||
"start_at_workfile", self.options_defaults["start_at_workfile"])
|
||||
|
|
@ -154,8 +156,8 @@ class LoadClip(plugin.NukeLoader):
|
|||
with viewer_update_and_undo_stop():
|
||||
read_node["file"].setValue(filepath)
|
||||
|
||||
used_colorspace = self._set_colorspace(
|
||||
read_node, version_data, representation["data"], filepath)
|
||||
self.set_colorspace_to_node(
|
||||
read_node, filepath, version, representation)
|
||||
|
||||
self._set_range_to_node(read_node, first, last, start_at_workfile)
|
||||
|
||||
|
|
@ -180,8 +182,6 @@ class LoadClip(plugin.NukeLoader):
|
|||
colorspace = representation["data"].get(key)
|
||||
colorspace = colorspace or version_data.get(key)
|
||||
data_imprint["db_colorspace"] = colorspace
|
||||
if used_colorspace:
|
||||
data_imprint["used_colorspace"] = used_colorspace
|
||||
else:
|
||||
value_ = context["version"]['data'].get(
|
||||
key, str(None))
|
||||
|
|
@ -304,8 +304,9 @@ class LoadClip(plugin.NukeLoader):
|
|||
# to avoid multiple undo steps for rest of process
|
||||
# we will switch off undo-ing
|
||||
with viewer_update_and_undo_stop():
|
||||
used_colorspace = self._set_colorspace(
|
||||
read_node, version_data, repre_doc["data"], filepath)
|
||||
self.set_colorspace_to_node(
|
||||
read_node, filepath, version_doc, repre_doc
|
||||
)
|
||||
|
||||
self._set_range_to_node(read_node, first, last, start_at_workfile)
|
||||
|
||||
|
|
@ -322,10 +323,6 @@ class LoadClip(plugin.NukeLoader):
|
|||
"author": version_data.get("author")
|
||||
}
|
||||
|
||||
# add used colorspace if found any
|
||||
if used_colorspace:
|
||||
updated_dict["used_colorspace"] = used_colorspace
|
||||
|
||||
last_version_doc = get_last_version_by_subset_id(
|
||||
project_name, version_doc["parent"], fields=["_id"]
|
||||
)
|
||||
|
|
@ -352,6 +349,36 @@ class LoadClip(plugin.NukeLoader):
|
|||
|
||||
self.set_as_member(read_node)
|
||||
|
||||
def set_colorspace_to_node(
|
||||
self,
|
||||
read_node,
|
||||
filepath,
|
||||
version_doc,
|
||||
representation_doc,
|
||||
):
|
||||
"""Set colorspace to read node.
|
||||
|
||||
Sets colorspace with available names validation.
|
||||
|
||||
Args:
|
||||
read_node (nuke.Node): The nuke's read node
|
||||
filepath (str): file path
|
||||
version_doc (dict): version document
|
||||
representation_doc (dict): representation document
|
||||
|
||||
"""
|
||||
used_colorspace = self._get_colorspace_data(
|
||||
version_doc, representation_doc, filepath)
|
||||
|
||||
if (
|
||||
used_colorspace
|
||||
and colorspace_exists_on_node(read_node, used_colorspace)
|
||||
):
|
||||
self.log.info(f"Used colorspace: {used_colorspace}")
|
||||
read_node["colorspace"].setValue(used_colorspace)
|
||||
else:
|
||||
self.log.info("Colorspace not set...")
|
||||
|
||||
def remove(self, container):
|
||||
read_node = container["node"]
|
||||
assert read_node.Class() == "Read", "Must be Read"
|
||||
|
|
@ -452,25 +479,47 @@ class LoadClip(plugin.NukeLoader):
|
|||
|
||||
return self.node_name_template.format(**name_data)
|
||||
|
||||
def _set_colorspace(self, node, version_data, repre_data, path):
|
||||
output_color = None
|
||||
path = path.replace("\\", "/")
|
||||
# get colorspace
|
||||
colorspace = repre_data.get("colorspace")
|
||||
colorspace = colorspace or version_data.get("colorspace")
|
||||
def _get_colorspace_data(self, version_doc, representation_doc, filepath):
|
||||
"""Get colorspace data from version and representation documents
|
||||
|
||||
# colorspace from `project_settings/nuke/imageio/regex_inputs`
|
||||
iio_colorspace = get_imageio_input_colorspace(path)
|
||||
Args:
|
||||
version_doc (dict): version document
|
||||
representation_doc (dict): representation document
|
||||
filepath (str): file path
|
||||
|
||||
# Set colorspace defined in version data
|
||||
if (
|
||||
colorspace is not None
|
||||
and colorspace_exists_on_node(node, str(colorspace))
|
||||
):
|
||||
node["colorspace"].setValue(str(colorspace))
|
||||
output_color = str(colorspace)
|
||||
elif iio_colorspace is not None:
|
||||
node["colorspace"].setValue(iio_colorspace)
|
||||
output_color = iio_colorspace
|
||||
Returns:
|
||||
Any[str,None]: colorspace name or None
|
||||
"""
|
||||
# Get backward compatible colorspace key.
|
||||
colorspace = representation_doc["data"].get("colorspace")
|
||||
self.log.debug(
|
||||
f"Colorspace from representation colorspace: {colorspace}"
|
||||
)
|
||||
|
||||
return output_color
|
||||
# Get backward compatible version data key if colorspace is not found.
|
||||
colorspace = colorspace or version_doc["data"].get("colorspace")
|
||||
self.log.debug(f"Colorspace from version colorspace: {colorspace}")
|
||||
|
||||
# Get colorspace from representation colorspaceData if colorspace is
|
||||
# not found.
|
||||
colorspace_data = representation_doc["data"].get("colorspaceData", {})
|
||||
colorspace = colorspace or colorspace_data.get("colorspace")
|
||||
self.log.debug(
|
||||
f"Colorspace from representation colorspaceData: {colorspace}"
|
||||
)
|
||||
|
||||
# check if any filerules are not applicable
|
||||
new_parsed_colorspace = get_imageio_file_rules_colorspace_from_filepath( # noqa
|
||||
filepath, "nuke", get_current_project_name()
|
||||
)
|
||||
self.log.debug(f"Colorspace new filerules: {new_parsed_colorspace}")
|
||||
|
||||
# colorspace from `project_settings/nuke/imageio/regexInputs`
|
||||
old_parsed_colorspace = get_imageio_input_colorspace(filepath)
|
||||
self.log.debug(f"Colorspace old filerules: {old_parsed_colorspace}")
|
||||
|
||||
return (
|
||||
new_parsed_colorspace
|
||||
or old_parsed_colorspace
|
||||
or colorspace
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ class CollectSlate(pyblish.api.InstancePlugin):
|
|||
(
|
||||
n_ for n_ in nuke.allNodes()
|
||||
if "slate" in n_.name().lower()
|
||||
if not n_["disable"].getValue()
|
||||
if not n_["disable"].getValue() and
|
||||
"publish_instance" not in n_.knobs() # Exclude instance nodes.
|
||||
),
|
||||
None
|
||||
)
|
||||
|
|
|
|||
|
|
@ -194,7 +194,6 @@ class CollectNukeWrites(pyblish.api.InstancePlugin,
|
|||
"frameEndHandle": last_frame,
|
||||
})
|
||||
|
||||
|
||||
# TODO temporarily set stagingDir as persistent for backward
|
||||
# compatibility. This is mainly focused on `renders`folders which
|
||||
# were previously not cleaned up (and could be used in read notes)
|
||||
|
|
@ -269,10 +268,6 @@ class CollectNukeWrites(pyblish.api.InstancePlugin,
|
|||
"tags": []
|
||||
}
|
||||
|
||||
frame_start_str = self._get_frame_start_str(first_frame, last_frame)
|
||||
|
||||
representation['frameStart'] = frame_start_str
|
||||
|
||||
# set slate frame
|
||||
collected_frames = self._add_slate_frame_to_collected_frames(
|
||||
instance,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ class ExtractBlendModel(BaseSettingsModel):
|
|||
default_factory=list,
|
||||
title="Families"
|
||||
)
|
||||
compress: bool = SettingsField(True, title="Compress")
|
||||
|
||||
|
||||
class ExtractBlendAnimationModel(BaseSettingsModel):
|
||||
enabled: bool = SettingsField(True)
|
||||
optional: bool = SettingsField(title="Optional")
|
||||
active: bool = SettingsField(title="Active")
|
||||
compress: bool = SettingsField(False, title="Compress")
|
||||
|
||||
|
||||
class ExtractPlayblastModel(BaseSettingsModel):
|
||||
|
|
@ -51,6 +59,7 @@ class ExtractPlayblastModel(BaseSettingsModel):
|
|||
optional: bool = SettingsField(title="Optional")
|
||||
active: bool = SettingsField(title="Active")
|
||||
presets: str = SettingsField("", title="Presets", widget="textarea")
|
||||
compress: bool = SettingsField(False, title="Compress")
|
||||
|
||||
@validator("presets")
|
||||
def validate_json(cls, value):
|
||||
|
|
@ -110,8 +119,8 @@ class PublishPuginsModel(BaseSettingsModel):
|
|||
default_factory=ValidatePluginModel,
|
||||
title="Extract ABC"
|
||||
)
|
||||
ExtractBlendAnimation: ValidatePluginModel = SettingsField(
|
||||
default_factory=ValidatePluginModel,
|
||||
ExtractBlendAnimation: ExtractBlendAnimationModel = SettingsField(
|
||||
default_factory=ExtractBlendAnimationModel,
|
||||
title="Extract Blend Animation"
|
||||
)
|
||||
ExtractAnimationFBX: ValidatePluginModel = SettingsField(
|
||||
|
|
@ -198,7 +207,8 @@ DEFAULT_BLENDER_PUBLISH_SETTINGS = {
|
|||
"action",
|
||||
"layout",
|
||||
"blendScene"
|
||||
]
|
||||
],
|
||||
"compress": False
|
||||
},
|
||||
"ExtractFBX": {
|
||||
"enabled": False,
|
||||
|
|
@ -213,7 +223,8 @@ DEFAULT_BLENDER_PUBLISH_SETTINGS = {
|
|||
"ExtractBlendAnimation": {
|
||||
"enabled": True,
|
||||
"optional": True,
|
||||
"active": True
|
||||
"active": True,
|
||||
"compress": False
|
||||
},
|
||||
"ExtractAnimationFBX": {
|
||||
"enabled": False,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.6"
|
||||
__version__ = "0.1.7"
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ class PublishPluginsModel(BaseSettingsModel):
|
|||
default_factory=BasicValidateModel,
|
||||
title="Validate Latest Containers.",
|
||||
section="Validators")
|
||||
ValidateInstanceInContextHoudini: BasicValidateModel = SettingsField(
|
||||
default_factory=BasicValidateModel,
|
||||
title="Validate Instance is in same Context.")
|
||||
ValidateMeshIsStatic: BasicValidateModel = SettingsField(
|
||||
default_factory=BasicValidateModel,
|
||||
title="Validate Mesh is Static.")
|
||||
|
|
@ -84,6 +87,11 @@ DEFAULT_HOUDINI_PUBLISH_SETTINGS = {
|
|||
"optional": True,
|
||||
"active": True
|
||||
},
|
||||
"ValidateInstanceInContextHoudini": {
|
||||
"enabled": True,
|
||||
"optional": True,
|
||||
"active": True
|
||||
},
|
||||
"ValidateMeshIsStatic": {
|
||||
"enabled": True,
|
||||
"optional": True,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.2.11"
|
||||
__version__ = "0.2.12"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue