[Automated] Merged develop into main

This commit is contained in:
ynbot 2023-03-27 17:28:05 +02:00 committed by GitHub
commit 8e2ff09c07
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 468 additions and 179 deletions

View file

@ -1221,7 +1221,7 @@ def set_track_color(track_item, color):
def check_inventory_versions(track_items=None):
"""
Actual version color idetifier of Loaded containers
Actual version color identifier of Loaded containers
Check all track items and filter only
Loader nodes for its version. It will get all versions from database
@ -1249,10 +1249,10 @@ def check_inventory_versions(track_items=None):
project_name = legacy_io.active_project()
filter_result = filter_containers(containers, project_name)
for container in filter_result.latest:
set_track_color(container["_item"], clip_color)
set_track_color(container["_item"], clip_color_last)
for container in filter_result.outdated:
set_track_color(container["_item"], clip_color_last)
set_track_color(container["_item"], clip_color)
def selection_changed_timeline(event):

View file

@ -6,6 +6,11 @@ from pymxs import runtime as rt
from typing import Union
import contextlib
from openpype.pipeline.context_tools import (
get_current_project_asset,
get_current_project
)
JSON_PREFIX = "JSON::"
@ -157,6 +162,112 @@ def get_multipass_setting(project_setting=None):
["multipass"])
def set_scene_resolution(width: int, height: int):
"""Set the render resolution
Args:
width(int): value of the width
height(int): value of the height
Returns:
None
"""
rt.renderWidth = width
rt.renderHeight = height
def reset_scene_resolution():
"""Apply the scene resolution from the project definition
scene resolution can be overwritten by an asset if the asset.data contains
any information regarding scene resolution .
Returns:
None
"""
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))
set_scene_resolution(width, height)
def get_frame_range() -> dict:
"""Get the current assets frame range and handles.
Returns:
dict: with frame start, frame end, handle start, handle end.
"""
# Set frame start/end
asset = get_current_project_asset()
frame_start = asset["data"].get("frameStart")
frame_end = asset["data"].get("frameEnd")
# Backwards compatibility
if frame_start is None or frame_end is None:
frame_start = asset["data"].get("edit_in")
frame_end = asset["data"].get("edit_out")
if frame_start is None or frame_end is None:
return
handles = asset["data"].get("handles") or 0
handle_start = asset["data"].get("handleStart")
if handle_start is None:
handle_start = handles
handle_end = asset["data"].get("handleEnd")
if handle_end is None:
handle_end = handles
return {
"frameStart": frame_start,
"frameEnd": frame_end,
"handleStart": handle_start,
"handleEnd": handle_end
}
def reset_frame_range(fps: bool = True):
"""Set frame range to current asset.
This is part of 3dsmax documentation:
animationRange: A System Global variable which lets you get and
set an Interval value that defines the start and end frames
of the Active Time Segment.
frameRate: A System Global variable which lets you get
and set an Integer value that defines the current
scene frame rate in frames-per-second.
"""
if fps:
data_fps = get_current_project(fields=["data.fps"])
fps_number = float(data_fps["data"]["fps"])
rt.frameRate = fps_number
frame_range = get_frame_range()
frame_start = frame_range["frameStart"] - int(frame_range["handleStart"])
frame_end = frame_range["frameEnd"] + int(frame_range["handleEnd"])
frange_cmd = f"animationRange = interval {frame_start} {frame_end}"
rt.execute(frange_cmd)
def set_context_setting():
"""Apply the project settings from the project definition
Settings can be overwritten by an asset if the asset.data contains
any information regarding those settings.
Examples of settings:
frame range
resolution
Returns:
None
"""
reset_scene_resolution()
def get_max_version():
"""
Args:

View file

@ -4,6 +4,7 @@ from qtpy import QtWidgets, QtCore
from pymxs import runtime as rt
from openpype.tools.utils import host_tools
from openpype.hosts.max.api import lib
class OpenPypeMenu(object):
@ -107,6 +108,17 @@ class OpenPypeMenu(object):
workfiles_action = QtWidgets.QAction("Work Files...", openpype_menu)
workfiles_action.triggered.connect(self.workfiles_callback)
openpype_menu.addAction(workfiles_action)
openpype_menu.addSeparator()
res_action = QtWidgets.QAction("Set Resolution", openpype_menu)
res_action.triggered.connect(self.resolution_callback)
openpype_menu.addAction(res_action)
frame_action = QtWidgets.QAction("Set Frame Range", openpype_menu)
frame_action.triggered.connect(self.frame_range_callback)
openpype_menu.addAction(frame_action)
return openpype_menu
def load_callback(self):
@ -128,3 +140,11 @@ class OpenPypeMenu(object):
def workfiles_callback(self):
"""Callback to show Workfiles tool."""
host_tools.show_workfiles(parent=self.main_widget)
def resolution_callback(self):
"""Callback to reset scene resolution"""
return lib.reset_scene_resolution()
def frame_range_callback(self):
"""Callback to reset frame range"""
return lib.reset_frame_range()

View file

@ -50,6 +50,11 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, INewPublisher):
self._has_been_setup = True
def context_setting():
return lib.set_context_setting()
rt.callbacks.addScript(rt.Name('systemPostNew'),
context_setting)
def has_unsaved_changes(self):
# TODO: how to get it from 3dsmax?
return True

View file

@ -61,7 +61,7 @@ class CollectRender(pyblish.api.InstancePlugin):
"plugin": "3dsmax",
"frameStart": context.data['frameStart'],
"frameEnd": context.data['frameEnd'],
"version": version_int
"version": version_int,
}
self.log.info("data: {0}".format(data))
instance.data.update(data)

View file

@ -26,6 +26,7 @@ class CreateReview(plugin.Creator):
"alpha cut"
]
useMayaTimeline = True
panZoom = False
def __init__(self, *args, **kwargs):
super(CreateReview, self).__init__(*args, **kwargs)
@ -45,5 +46,6 @@ class CreateReview(plugin.Creator):
data["keepImages"] = self.keepImages
data["imagePlane"] = self.imagePlane
data["transparency"] = self.transparency
data["panZoom"] = self.panZoom
self.data = data

View file

@ -79,6 +79,8 @@ class CollectReview(pyblish.api.InstancePlugin):
data['review_width'] = instance.data['review_width']
data['review_height'] = instance.data['review_height']
data["isolate"] = instance.data["isolate"]
data["panZoom"] = instance.data.get("panZoom", False)
data["panel"] = instance.data["panel"]
cmds.setAttr(str(instance) + '.active', 1)
self.log.debug('data {}'.format(instance.context[i].data))
instance.context[i].data.update(data)

View file

@ -1,5 +1,6 @@
import os
import json
import contextlib
import clique
import capture
@ -11,6 +12,16 @@ from maya import cmds
import pymel.core as pm
@contextlib.contextmanager
def panel_camera(panel, camera):
original_camera = cmds.modelPanel(panel, query=True, camera=True)
try:
cmds.modelPanel(panel, edit=True, camera=camera)
yield
finally:
cmds.modelPanel(panel, edit=True, camera=original_camera)
class ExtractPlayblast(publish.Extractor):
"""Extract viewport playblast.
@ -25,6 +36,16 @@ class ExtractPlayblast(publish.Extractor):
optional = True
capture_preset = {}
def _capture(self, preset):
self.log.info(
"Using preset:\n{}".format(
json.dumps(preset, sort_keys=True, indent=4)
)
)
path = capture.capture(log=self.log, **preset)
self.log.debug("playblast path {}".format(path))
def process(self, instance):
self.log.info("Extracting capture..")
@ -43,7 +64,7 @@ class ExtractPlayblast(publish.Extractor):
self.log.info("start: {}, end: {}".format(start, end))
# get cameras
camera = instance.data['review_camera']
camera = instance.data["review_camera"]
preset = lib.load_capture_preset(data=self.capture_preset)
# Grab capture presets from the project settings
@ -57,23 +78,23 @@ class ExtractPlayblast(publish.Extractor):
asset_height = asset_data.get("resolutionHeight")
review_instance_width = instance.data.get("review_width")
review_instance_height = instance.data.get("review_height")
preset['camera'] = camera
preset["camera"] = camera
# Tests if project resolution is set,
# if it is a value other than zero, that value is
# used, if not then the asset resolution is
# used
if review_instance_width and review_instance_height:
preset['width'] = review_instance_width
preset['height'] = review_instance_height
preset["width"] = review_instance_width
preset["height"] = review_instance_height
elif width_preset and height_preset:
preset['width'] = width_preset
preset['height'] = height_preset
preset["width"] = width_preset
preset["height"] = height_preset
elif asset_width and asset_height:
preset['width'] = asset_width
preset['height'] = asset_height
preset['start_frame'] = start
preset['end_frame'] = end
preset["width"] = asset_width
preset["height"] = asset_height
preset["start_frame"] = start
preset["end_frame"] = end
# Enforce persisting camera depth of field
camera_options = preset.setdefault("camera_options", {})
@ -86,8 +107,8 @@ class ExtractPlayblast(publish.Extractor):
self.log.info("Outputting images to %s" % path)
preset['filename'] = path
preset['overwrite'] = True
preset["filename"] = path
preset["overwrite"] = True
pm.refresh(f=True)
@ -114,7 +135,8 @@ class ExtractPlayblast(publish.Extractor):
# Disable Pan/Zoom.
pan_zoom = cmds.getAttr("{}.panZoomEnabled".format(preset["camera"]))
cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), False)
preset.pop("pan_zoom", None)
preset["camera_options"]["panZoomEnabled"] = instance.data["panZoom"]
# Need to explicitly enable some viewport changes so the viewport is
# refreshed ahead of playblasting.
@ -136,30 +158,39 @@ class ExtractPlayblast(publish.Extractor):
)
override_viewport_options = (
capture_presets['Viewport Options']['override_viewport_options']
capture_presets["Viewport Options"]["override_viewport_options"]
)
with lib.maintained_time():
filename = preset.get("filename", "%TEMP%")
# Force viewer to False in call to capture because we have our own
# viewer opening call to allow a signal to trigger between
# playblast and viewer
preset['viewer'] = False
# Force viewer to False in call to capture because we have our own
# viewer opening call to allow a signal to trigger between
# playblast and viewer
preset["viewer"] = False
# Update preset with current panel setting
# if override_viewport_options is turned off
if not override_viewport_options:
panel_preset = capture.parse_view(instance.data["panel"])
panel_preset.pop("camera")
preset.update(panel_preset)
# Update preset with current panel setting
# if override_viewport_options is turned off
if not override_viewport_options:
panel_preset = capture.parse_view(instance.data["panel"])
panel_preset.pop("camera")
preset.update(panel_preset)
self.log.info(
"Using preset:\n{}".format(
json.dumps(preset, sort_keys=True, indent=4)
# Need to ensure Python 2 compatibility.
# TODO: Remove once dropping Python 2.
if getattr(contextlib, "nested", None):
# Python 3 compatibility.
with contextlib.nested(
lib.maintained_time(),
panel_camera(instance.data["panel"], preset["camera"])
):
self._capture(preset)
else:
# Python 2 compatibility.
with contextlib.ExitStack() as stack:
stack.enter_context(lib.maintained_time())
stack.enter_context(
panel_camera(instance.data["panel"], preset["camera"])
)
)
path = capture.capture(log=self.log, **preset)
self._capture(preset)
# Restoring viewport options.
if viewport_defaults:
@ -169,18 +200,17 @@ class ExtractPlayblast(publish.Extractor):
cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), pan_zoom)
self.log.debug("playblast path {}".format(path))
collected_files = os.listdir(stagingdir)
patterns = [clique.PATTERNS["frames"]]
collections, remainder = clique.assemble(collected_files,
minimum_items=1,
patterns=patterns)
filename = preset.get("filename", "%TEMP%")
self.log.debug("filename {}".format(filename))
frame_collection = None
for collection in collections:
filebase = collection.format('{head}').rstrip(".")
filebase = collection.format("{head}").rstrip(".")
self.log.debug("collection head {}".format(filebase))
if filebase in filename:
frame_collection = collection
@ -204,15 +234,15 @@ class ExtractPlayblast(publish.Extractor):
collected_files = collected_files[0]
representation = {
'name': 'png',
'ext': 'png',
'files': collected_files,
"name": self.capture_preset["Codec"]["compression"],
"ext": self.capture_preset["Codec"]["compression"],
"files": collected_files,
"stagingDir": stagingdir,
"frameStart": start,
"frameEnd": end,
'fps': fps,
'preview': True,
'tags': tags,
'camera_name': camera_node_name
"fps": fps,
"preview": True,
"tags": tags,
"camera_name": camera_node_name
}
instance.data["representations"].append(representation)

View file

@ -26,28 +26,28 @@ class ExtractThumbnail(publish.Extractor):
def process(self, instance):
self.log.info("Extracting capture..")
camera = instance.data['review_camera']
camera = instance.data["review_camera"]
capture_preset = (
instance.context.data["project_settings"]['maya']['publish']['ExtractPlayblast']['capture_preset']
)
maya_setting = instance.context.data["project_settings"]["maya"]
plugin_setting = maya_setting["publish"]["ExtractPlayblast"]
capture_preset = plugin_setting["capture_preset"]
override_viewport_options = (
capture_preset['Viewport Options']['override_viewport_options']
capture_preset["Viewport Options"]["override_viewport_options"]
)
try:
preset = lib.load_capture_preset(data=capture_preset)
except KeyError as ke:
self.log.error('Error loading capture presets: {}'.format(str(ke)))
self.log.error("Error loading capture presets: {}".format(str(ke)))
preset = {}
self.log.info('Using viewport preset: {}'.format(preset))
self.log.info("Using viewport preset: {}".format(preset))
# preset["off_screen"] = False
preset['camera'] = camera
preset['start_frame'] = instance.data["frameStart"]
preset['end_frame'] = instance.data["frameStart"]
preset['camera_options'] = {
preset["camera"] = camera
preset["start_frame"] = instance.data["frameStart"]
preset["end_frame"] = instance.data["frameStart"]
preset["camera_options"] = {
"displayGateMask": False,
"displayResolution": False,
"displayFilmGate": False,
@ -74,14 +74,14 @@ class ExtractThumbnail(publish.Extractor):
# used, if not then the asset resolution is
# used
if review_instance_width and review_instance_height:
preset['width'] = review_instance_width
preset['height'] = review_instance_height
preset["width"] = review_instance_width
preset["height"] = review_instance_height
elif width_preset and height_preset:
preset['width'] = width_preset
preset['height'] = height_preset
preset["width"] = width_preset
preset["height"] = height_preset
elif asset_width and asset_height:
preset['width'] = asset_width
preset['height'] = asset_height
preset["width"] = asset_width
preset["height"] = asset_height
# Create temp directory for thumbnail
# - this is to avoid "override" of source file
@ -96,8 +96,8 @@ class ExtractThumbnail(publish.Extractor):
self.log.info("Outputting images to %s" % path)
preset['filename'] = path
preset['overwrite'] = True
preset["filename"] = path
preset["overwrite"] = True
pm.refresh(f=True)
@ -123,14 +123,14 @@ class ExtractThumbnail(publish.Extractor):
preset["viewport_options"] = {"imagePlane": image_plane}
# Disable Pan/Zoom.
pan_zoom = cmds.getAttr("{}.panZoomEnabled".format(preset["camera"]))
cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), False)
preset.pop("pan_zoom", None)
preset["camera_options"]["panZoomEnabled"] = instance.data["panZoom"]
with lib.maintained_time():
# Force viewer to False in call to capture because we have our own
# viewer opening call to allow a signal to trigger between
# playblast and viewer
preset['viewer'] = False
preset["viewer"] = False
# Update preset with current panel setting
# if override_viewport_options is turned off
@ -145,17 +145,15 @@ class ExtractThumbnail(publish.Extractor):
_, thumbnail = os.path.split(playblast)
cmds.setAttr("{}.panZoomEnabled".format(preset["camera"]), pan_zoom)
self.log.info("file list {}".format(thumbnail))
if "representations" not in instance.data:
instance.data["representations"] = []
representation = {
'name': 'thumbnail',
'ext': 'jpg',
'files': thumbnail,
"name": "thumbnail",
"ext": "jpg",
"files": thumbnail,
"stagingDir": dst_staging,
"thumbnail": True
}

View file

@ -54,22 +54,19 @@ class LoadBackdropNodes(load.LoaderPlugin):
version = context['version']
version_data = version.get("data", {})
vname = version.get("name", None)
first = version_data.get("frameStart", None)
last = version_data.get("frameEnd", None)
namespace = namespace or context['asset']['name']
colorspace = version_data.get("colorspace", None)
object_name = "{}_{}".format(name, namespace)
# prepare data for imprinting
# add additional metadata from the version to imprint to Avalon knob
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
"source", "author", "fps"]
add_keys = ["source", "author", "fps"]
data_imprint = {"frameStart": first,
"frameEnd": last,
"version": vname,
"colorspaceInput": colorspace,
"objectName": object_name}
data_imprint = {
"version": vname,
"colorspaceInput": colorspace,
"objectName": object_name
}
for k in add_keys:
data_imprint.update({k: version_data[k]})
@ -204,18 +201,13 @@ class LoadBackdropNodes(load.LoaderPlugin):
name = container['name']
version_data = version_doc.get("data", {})
vname = version_doc.get("name", None)
first = version_data.get("frameStart", None)
last = version_data.get("frameEnd", None)
namespace = container['namespace']
colorspace = version_data.get("colorspace", None)
object_name = "{}_{}".format(name, namespace)
add_keys = ["frameStart", "frameEnd", "handleStart", "handleEnd",
"source", "author", "fps"]
add_keys = ["source", "author", "fps"]
data_imprint = {"representation": str(representation["_id"]),
"frameStart": first,
"frameEnd": last,
"version": vname,
"colorspaceInput": colorspace,
"objectName": object_name}

View file

@ -51,38 +51,10 @@ class CollectBackdrops(pyblish.api.InstancePlugin):
instance.data["label"] = "{0} ({1} nodes)".format(
bckn.name(), len(instance.data["transientData"]["childNodes"]))
instance.data["families"].append(instance.data["family"])
# Get frame range
handle_start = instance.context.data["handleStart"]
handle_end = instance.context.data["handleEnd"]
first_frame = int(nuke.root()["first_frame"].getValue())
last_frame = int(nuke.root()["last_frame"].getValue())
# get version
version = instance.context.data.get('version')
if not version:
raise RuntimeError("Script name has no version in the name.")
if version:
instance.data['version'] = version
instance.data['version'] = version
# Add version data to instance
version_data = {
"handles": handle_start,
"handleStart": handle_start,
"handleEnd": handle_end,
"frameStart": first_frame + handle_start,
"frameEnd": last_frame - handle_end,
"version": int(version),
"families": [instance.data["family"]] + instance.data["families"],
"subset": instance.data["subset"],
"fps": instance.context.data["fps"]
}
instance.data.update({
"versionData": version_data,
"frameStart": first_frame,
"frameEnd": last_frame
})
self.log.info("Backdrop instance collected: `{}`".format(instance))

View file

@ -55,7 +55,7 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin):
self._convert_render_layers(
to_convert["renderLayer"], current_instances)
self._convert_render_passes(
to_convert["renderpass"], current_instances)
to_convert["renderPass"], current_instances)
self._convert_render_scenes(
to_convert["renderScene"], current_instances)
self._convert_workfiles(
@ -116,7 +116,7 @@ class TVPaintLegacyConverted(SubsetConvertorPlugin):
render_layers_by_group_id = {}
for instance in current_instances:
if instance.get("creator_identifier") == "render.layer":
group_id = instance["creator_identifier"]["group_id"]
group_id = instance["creator_attributes"]["group_id"]
render_layers_by_group_id[group_id] = instance
for render_pass in render_passes:

View file

@ -415,11 +415,11 @@ class CreateRenderPass(TVPaintCreator):
.get("creator_attributes", {})
.get("render_layer_instance_id")
)
render_layer_info = render_layers.get(render_layer_instance_id)
render_layer_info = render_layers.get(render_layer_instance_id, {})
self.update_instance_labels(
instance_data,
render_layer_info["variant"],
render_layer_info["template_data"]
render_layer_info.get("variant"),
render_layer_info.get("template_data")
)
instance = CreatedInstance.from_existing(instance_data, self)
self._add_instance_to_context(instance)
@ -607,11 +607,11 @@ class CreateRenderPass(TVPaintCreator):
current_instances = self.host.list_instances()
render_layers = [
{
"value": instance["instance_id"],
"label": instance["subset"]
"value": inst["instance_id"],
"label": inst["subset"]
}
for instance in current_instances
if instance["creator_identifier"] == CreateRenderlayer.identifier
for inst in current_instances
if inst.get("creator_identifier") == CreateRenderlayer.identifier
]
if not render_layers:
render_layers.append({"value": None, "label": "N/A"})
@ -697,6 +697,7 @@ class TVPaintAutoDetectRenderCreator(TVPaintCreator):
["create"]
["auto_detect_render"]
)
self.enabled = plugin_settings.get("enabled", False)
self.allow_group_rename = plugin_settings["allow_group_rename"]
self.group_name_template = plugin_settings["group_name_template"]
self.group_idx_offset = plugin_settings["group_idx_offset"]

View file

@ -22,9 +22,11 @@ class CollectOutputFrameRange(pyblish.api.InstancePlugin):
context = instance.context
frame_start = asset_doc["data"]["frameStart"]
fps = asset_doc["data"]["fps"]
frame_end = frame_start + (
context.data["sceneMarkOut"] - context.data["sceneMarkIn"]
)
instance.data["fps"] = fps
instance.data["frameStart"] = frame_start
instance.data["frameEnd"] = frame_end
self.log.info(

View file

@ -1,5 +1,8 @@
import pyblish.api
from openpype.pipeline import PublishXmlValidationError
from openpype.pipeline import (
PublishXmlValidationError,
OptionalPyblishPluginMixin,
)
from openpype.hosts.tvpaint.api.pipeline import (
list_instances,
write_instances,
@ -31,7 +34,10 @@ class FixAssetNames(pyblish.api.Action):
write_instances(new_instance_items)
class ValidateAssetNames(pyblish.api.ContextPlugin):
class ValidateAssetName(
OptionalPyblishPluginMixin,
pyblish.api.ContextPlugin
):
"""Validate assset name present on instance.
Asset name on instance should be the same as context's.
@ -43,6 +49,8 @@ class ValidateAssetNames(pyblish.api.ContextPlugin):
actions = [FixAssetNames]
def process(self, context):
if not self.is_active(context.data):
return
context_asset_name = context.data["asset"]
for instance in context:
asset_name = instance.data.get("asset")

View file

@ -11,7 +11,7 @@ class ValidateLayersVisiblity(pyblish.api.InstancePlugin):
families = ["review", "render"]
def process(self, instance):
layers = instance.data["layers"]
layers = instance.data.get("layers")
# Instance have empty layers
# - it is not job of this validator to check that
if not layers:

View file

@ -1,7 +1,10 @@
import json
import pyblish.api
from openpype.pipeline import PublishXmlValidationError
from openpype.pipeline import (
PublishXmlValidationError,
OptionalPyblishPluginMixin,
)
from openpype.hosts.tvpaint.api.lib import execute_george
@ -23,7 +26,10 @@ class ValidateMarksRepair(pyblish.api.Action):
)
class ValidateMarks(pyblish.api.ContextPlugin):
class ValidateMarks(
OptionalPyblishPluginMixin,
pyblish.api.ContextPlugin
):
"""Validate mark in and out are enabled and it's duration.
Mark In/Out does not have to match frameStart and frameEnd but duration is
@ -59,6 +65,9 @@ class ValidateMarks(pyblish.api.ContextPlugin):
}
def process(self, context):
if not self.is_active(context.data):
return
current_data = {
"markIn": context.data["sceneMarkIn"],
"markInState": context.data["sceneMarkInState"],

View file

@ -1,11 +1,17 @@
import json
import pyblish.api
from openpype.pipeline import PublishXmlValidationError
from openpype.pipeline import (
PublishXmlValidationError,
OptionalPyblishPluginMixin,
)
# TODO @iLliCiTiT add fix action for fps
class ValidateProjectSettings(pyblish.api.ContextPlugin):
class ValidateProjectSettings(
OptionalPyblishPluginMixin,
pyblish.api.ContextPlugin
):
"""Validate scene settings against database."""
label = "Validate Scene Settings"
@ -13,6 +19,9 @@ class ValidateProjectSettings(pyblish.api.ContextPlugin):
optional = True
def process(self, context):
if not self.is_active(context.data):
return
expected_data = context.data["assetEntity"]["data"]
scene_data = {
"fps": context.data.get("sceneFps"),

View file

@ -1,5 +1,8 @@
import pyblish.api
from openpype.pipeline import PublishXmlValidationError
from openpype.pipeline import (
PublishXmlValidationError,
OptionalPyblishPluginMixin,
)
from openpype.hosts.tvpaint.api.lib import execute_george
@ -14,7 +17,10 @@ class RepairStartFrame(pyblish.api.Action):
execute_george("tv_startframe 0")
class ValidateStartFrame(pyblish.api.ContextPlugin):
class ValidateStartFrame(
OptionalPyblishPluginMixin,
pyblish.api.ContextPlugin
):
"""Validate start frame being at frame 0."""
label = "Validate Start Frame"
@ -24,6 +30,9 @@ class ValidateStartFrame(pyblish.api.ContextPlugin):
optional = True
def process(self, context):
if not self.is_active(context.data):
return
start_frame = execute_george("tv_startframe")
if start_frame == 0:
return

View file

@ -3,21 +3,60 @@
"""
import pyblish.api
from openpype.lib import TextDef
from openpype.pipeline.publish import OpenPypePyblishPluginMixin
class CollectDeadlinePools(pyblish.api.InstancePlugin):
class CollectDeadlinePools(pyblish.api.InstancePlugin,
OpenPypePyblishPluginMixin):
"""Collect pools from instance if present, from Setting otherwise."""
order = pyblish.api.CollectorOrder + 0.420
label = "Collect Deadline Pools"
families = ["rendering", "render.farm", "renderFarm", "renderlayer"]
families = ["rendering",
"render.farm",
"renderFarm",
"renderlayer",
"maxrender"]
primary_pool = None
secondary_pool = None
@classmethod
def apply_settings(cls, project_settings, system_settings):
# deadline.publish.CollectDeadlinePools
settings = project_settings["deadline"]["publish"]["CollectDeadlinePools"] # noqa
cls.primary_pool = settings.get("primary_pool", None)
cls.secondary_pool = settings.get("secondary_pool", None)
def process(self, instance):
attr_values = self.get_attr_values_from_data(instance.data)
if not instance.data.get("primaryPool"):
instance.data["primaryPool"] = self.primary_pool or "none"
instance.data["primaryPool"] = (
attr_values.get("primaryPool") or self.primary_pool or "none"
)
if not instance.data.get("secondaryPool"):
instance.data["secondaryPool"] = self.secondary_pool or "none"
instance.data["secondaryPool"] = (
attr_values.get("secondaryPool") or self.secondary_pool or "none" # noqa
)
@classmethod
def get_attribute_defs(cls):
# TODO: Preferably this would be an enum for the user
# but the Deadline server URL can be dynamic and
# can be set per render instance. Since get_attribute_defs
# can't be dynamic unfortunately EnumDef isn't possible (yet?)
# pool_names = self.deadline_module.get_deadline_pools(deadline_url,
# self.log)
# secondary_pool_names = ["-"] + pool_names
return [
TextDef("primaryPool",
label="Primary Pool",
default=cls.primary_pool),
TextDef("secondaryPool",
label="Secondary Pool",
default=cls.secondary_pool)
]

View file

@ -3,7 +3,15 @@ import getpass
import copy
import attr
from openpype.pipeline import legacy_io
from openpype.lib import (
TextDef,
BoolDef,
NumberDef,
)
from openpype.pipeline import (
legacy_io,
OpenPypePyblishPluginMixin
)
from openpype.settings import get_project_settings
from openpype.hosts.max.api.lib import (
get_current_renderer,
@ -22,7 +30,8 @@ class MaxPluginInfo(object):
IgnoreInputs = attr.ib(default=True)
class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
OpenPypePyblishPluginMixin):
label = "Submit Render to Deadline"
hosts = ["max"]
@ -31,14 +40,22 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
use_published = True
priority = 50
tile_priority = 50
chunk_size = 1
jobInfo = {}
pluginInfo = {}
group = None
deadline_pool = None
deadline_pool_secondary = None
framePerTask = 1
@classmethod
def apply_settings(cls, project_settings, system_settings):
settings = project_settings["deadline"]["publish"]["MaxSubmitDeadline"] # noqa
# Take some defaults from settings
cls.use_published = settings.get("use_published",
cls.use_published)
cls.priority = settings.get("priority",
cls.priority)
cls.chuck_size = settings.get("chunk_size", cls.chunk_size)
cls.group = settings.get("group", cls.group)
def get_job_info(self):
job_info = DeadlineJobInfo(Plugin="3dsmax")
@ -49,11 +66,11 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
instance = self._instance
context = instance.context
# Always use the original work file name for the Job name even when
# rendering is done from the published Work File. The original work
# file name is clearer because it can also have subversion strings,
# etc. which are stripped for the published file.
src_filepath = context.data["currentFile"]
src_filename = os.path.basename(src_filepath)
@ -71,13 +88,13 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
job_info.Pool = instance.data.get("primaryPool")
job_info.SecondaryPool = instance.data.get("secondaryPool")
job_info.ChunkSize = instance.data.get("chunkSize", 1)
job_info.Comment = context.data.get("comment")
job_info.Priority = instance.data.get("priority", self.priority)
job_info.FramesPerTask = instance.data.get("framesPerTask", 1)
if self.group:
job_info.Group = self.group
attr_values = self.get_attr_values_from_data(instance.data)
job_info.ChunkSize = attr_values.get("chunkSize", 1)
job_info.Comment = context.data.get("comment")
job_info.Priority = attr_values.get("priority", self.priority)
job_info.Group = attr_values.get("group", self.group)
# Add options from RenderGlobals
render_globals = instance.data.get("renderGlobals", {})
@ -216,3 +233,32 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
plugin_info.update(plugin_data)
return job_info, plugin_info
@classmethod
def get_attribute_defs(cls):
defs = super(MaxSubmitDeadline, cls).get_attribute_defs()
defs.extend([
BoolDef("use_published",
default=cls.use_published,
label="Use Published Scene"),
NumberDef("priority",
minimum=1,
maximum=250,
decimals=0,
default=cls.priority,
label="Priority"),
NumberDef("chunkSize",
minimum=1,
maximum=50,
decimals=0,
default=cls.chunk_size,
label="Frame Per Task"),
TextDef("group",
default=cls.group,
label="Group Name"),
])
return defs

View file

@ -17,7 +17,11 @@ class ValidateDeadlinePools(OptionalPyblishPluginMixin,
label = "Validate Deadline Pools"
order = pyblish.api.ValidatorOrder
families = ["rendering", "render.farm", "renderFarm", "renderlayer"]
families = ["rendering",
"render.farm",
"renderFarm",
"renderlayer",
"maxrender"]
optional = True
def process(self, instance):

View file

@ -43,10 +43,7 @@
"use_published": true,
"priority": 50,
"chunk_size": 10,
"group": "none",
"deadline_pool": "",
"deadline_pool_secondary": "",
"framePerTask": 1
"group": "none"
},
"NukeSubmitDeadline": {
"enabled": true,

View file

@ -790,7 +790,7 @@
"ExtractPlayblast": {
"capture_preset": {
"Codec": {
"compression": "jpg",
"compression": "png",
"format": "image",
"quality": 95
},
@ -817,7 +817,8 @@
},
"Generic": {
"isolate_view": true,
"off_screen": true
"off_screen": true,
"pan_zoom": false
},
"Renderer": {
"rendererName": "vp2Renderer"

View file

@ -42,6 +42,7 @@
"default_variants": []
},
"auto_detect_render": {
"enabled": false,
"allow_group_rename": true,
"group_name_template": "L{group_index}",
"group_idx_offset": 10,

View file

@ -133,7 +133,7 @@
"linux": []
},
"arguments": {
"windows": [],
"windows": ["-U MAXScript {OPENPYPE_ROOT}\\openpype\\hosts\\max\\startup\\startup.ms"],
"darwin": [],
"linux": []
},

View file

@ -239,27 +239,12 @@
{
"type": "number",
"key": "chunk_size",
"label": "Chunk Size"
"label": "Frame per Task"
},
{
"type": "text",
"key": "group",
"label": "Group Name"
},
{
"type": "text",
"key": "deadline_pool",
"label": "Deadline pool"
},
{
"type": "text",
"key": "deadline_pool_secondary",
"label": "Deadline pool (secondary)"
},
{
"type": "number",
"key": "framePerTask",
"label": "Frame Per Task"
}
]
},

View file

@ -202,7 +202,13 @@
"key": "auto_detect_render",
"label": "Auto-Detect Create Render",
"is_group": true,
"checkbox_key": "enabled",
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"type": "label",
"label": "The creator tries to auto-detect Render Layers and Render Passes in scene. For Render Layers is used group name as a variant and for Render Passes is used TVPaint layer name.<br/><br/>Group names can be renamed by their used order in scene. The renaming template where can be used <b>{group_index}</b> formatting key which is filled by \"used position index of group\".<br/>- Template: <b>L{group_index}</b><br/>- Group offset: <b>10</b><br/>- Group padding: <b>3</b><br/>Would create group names \"<b>L010</b>\", \"<b>L020</b>\", ..."

View file

@ -91,6 +91,11 @@
"type": "boolean",
"key": "off_screen",
"label": " Off Screen"
},
{
"type": "boolean",
"key": "pan_zoom",
"label": " 2D Pan/Zoom"
}
]
},
@ -156,7 +161,7 @@
{
"type": "boolean",
"key": "override_viewport_options",
"label": "override_viewport_options"
"label": "Override Viewport Options"
},
{
"type": "enum",

View file

@ -72,8 +72,8 @@ class HierarchyView(QtWidgets.QTreeView):
column_delegate_defs = {
"name": NameDef(),
"type": TypeDef(),
"frameStart": NumberDef(1),
"frameEnd": NumberDef(1),
"frameStart": NumberDef(0),
"frameEnd": NumberDef(0),
"fps": NumberDef(1, decimals=3, step=1),
"resolutionWidth": NumberDef(0),
"resolutionHeight": NumberDef(0),

View file

@ -110,6 +110,41 @@ or Deadlines **Draft Tile Assembler**.
This is useful to fix some specific renderer glitches and advanced hacking of Maya Scene files. `Patch name` is label for patch for easier orientation.
`Patch regex` is regex used to find line in file, after `Patch line` string is inserted. Note that you need to add line ending.
### Extract Playblast Settings (review)
These settings provide granular control over how the playblasts or reviews are produced in Maya.
Some of these settings are also available on the instance itself, in which case these settings will become the default value when creating the review instance.
![Extract Playblast Settings](assets/maya-admin_extract_playblast_settings.png)
- **Compression type** which file encoding to use.
- **Data format** what format is the file encoding.
- **Quality** lets you control the compression value for the output. Results can vary depending on the compression you selected. Quality values can range from 0 to 100, with a default value of 95.
- **Background Color** the viewports background color.
- **Background Bottom** the viewports background bottom color.
- **Background Top** the viewports background top color.
- **Override display options** override the viewports display options to use what is set in the settings.
- **Isolate view** isolates the view to what is in the review instance. If only a camera is present in the review instance, all nodes are displayed in view.
- **Off Screen** records the playblast hidden from the user.
- **2D Pan/Zoom** enables the 2D Pan/Zoom functionality of the camera.
- **Renderer name** which renderer to use for playblasting.
- **Width** width of the output resolution. If this value is `0`, the asset's width is used.
- **Height** height of the output resolution. If this value is `0`, the asset's height is used.
#### Viewport Options
Most settings to override in the viewport are self explanatory and can be found in Maya.
![Extract Playblast Settings](assets/maya-admin_extract_playblast_settings_viewport_options.png)
- **Override Viewport Options** enable to use the settings below for the viewport when publishing the review.
#### Camera Options
These options are set on the camera shape when publishing the review. They correspond to attributes on the Maya camera shape node.
![Extract Playblast Settings](assets/maya-admin_extract_playblast_settings_camera_options.png)
## Custom Menu
You can add your custom tools menu into Maya by extending definitions in **Maya -> Scripts Menu Definition**.
![Custom menu definition](assets/maya-admin_scriptsmenu.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB