Merge branch '2.x/develop' into feature/tvpaint_creators

This commit is contained in:
iLLiCiTiT 2020-11-05 19:02:44 +01:00
commit c2a340fab3
5 changed files with 172 additions and 46 deletions

View file

@ -864,10 +864,10 @@ class BuildWorkfile:
current_task_name = io.Session["AVALON_TASK"]
# Load workfile presets for task
build_presets = self.get_build_presets(current_task_name)
self.build_presets = self.get_build_presets(current_task_name)
# Skip if there are any presets for task
if not build_presets:
if not self.build_presets:
log.warning(
"Current task `{}` does not have any loading preset.".format(
current_task_name
@ -876,9 +876,9 @@ class BuildWorkfile:
return
# Get presets for loading current asset
current_context_profiles = build_presets.get("current_context")
current_context_profiles = self.build_presets.get("current_context")
# Get presets for loading linked assets
link_context_profiles = build_presets.get("linked_assets")
link_context_profiles = self.build_presets.get("linked_assets")
# Skip if both are missing
if not current_context_profiles and not link_context_profiles:
log.warning("Current task `{}` has empty loading preset.".format(
@ -1231,7 +1231,36 @@ class BuildWorkfile:
:rtype: list
"""
loaded_containers = []
for subset_id, repres in repres_by_subset_id.items():
# Get subset id order from build presets.
build_presets = self.build_presets.get("current_context", [])
build_presets += self.build_presets.get("linked_assets", [])
subset_ids_ordered = []
for preset in build_presets:
for preset_family in preset["families"]:
for id, subset in subsets_by_id.items():
if preset_family not in subset["data"].get("families", []):
continue
subset_ids_ordered.append(id)
# Order representations from subsets.
print("repres_by_subset_id", repres_by_subset_id)
representations_ordered = []
representations = []
for id in subset_ids_ordered:
for subset_id, repres in repres_by_subset_id.items():
if repres in representations:
continue
if id == subset_id:
representations_ordered.append((subset_id, repres))
representations.append(repres)
print("representations", representations)
# Load ordered reprensentations.
for subset_id, repres in representations_ordered:
subset_name = subsets_by_id[subset_id]["name"]
profile = profiles_per_subset_id[subset_id]

View file

@ -157,6 +157,11 @@ class ExtractBurnin(pype.api.Extractor):
filled_anatomy = anatomy.format_all(burnin_data)
burnin_data["anatomy"] = filled_anatomy.get_solved()
# Add source camera name to burnin data
camera_name = repre.get("camera_name")
if camera_name:
burnin_data["camera_name"] = camera_name
first_output = True
files_to_delete = []

View file

@ -4,14 +4,70 @@ import maya.cmds as cmds
from avalon import api, io
from avalon.maya.pipeline import containerise
from avalon.maya import lib
from Qt import QtWidgets
from Qt import QtWidgets, QtCore
class CameraWindow(QtWidgets.QDialog):
def __init__(self, cameras):
super(CameraWindow, self).__init__()
self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)
self.camera = None
self.widgets = {
"label": QtWidgets.QLabel("Select camera for image plane."),
"list": QtWidgets.QListWidget(),
"warning": QtWidgets.QLabel("No cameras selected!"),
"buttons": QtWidgets.QWidget(),
"okButton": QtWidgets.QPushButton("Ok"),
"cancelButton": QtWidgets.QPushButton("Cancel")
}
# Build warning.
self.widgets["warning"].setVisible(False)
self.widgets["warning"].setStyleSheet("color: red")
# Build list.
for camera in cameras:
self.widgets["list"].addItem(camera)
# Build buttons.
layout = QtWidgets.QHBoxLayout(self.widgets["buttons"])
layout.addWidget(self.widgets["okButton"])
layout.addWidget(self.widgets["cancelButton"])
# Build layout.
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.widgets["label"])
layout.addWidget(self.widgets["list"])
layout.addWidget(self.widgets["buttons"])
layout.addWidget(self.widgets["warning"])
self.widgets["okButton"].pressed.connect(self.on_ok_pressed)
self.widgets["cancelButton"].pressed.connect(self.on_cancel_pressed)
self.widgets["list"].itemPressed.connect(self.on_list_itemPressed)
def on_list_itemPressed(self, item):
self.camera = item.text()
def on_ok_pressed(self):
if self.camera is None:
self.widgets["warning"].setVisible(True)
return
self.close()
def on_cancel_pressed(self):
self.camera = None
self.close()
class ImagePlaneLoader(api.Loader):
"""Specific loader of plate for image planes on selected camera."""
families = ["plate", "render"]
label = "Create imagePlane on selected camera."
label = "Load imagePlane."
representations = ["mov", "exr", "preview", "png"]
icon = "image"
color = "orange"
@ -26,43 +82,24 @@ class ImagePlaneLoader(api.Loader):
suffix="_",
)
# Getting camera from selection.
selection = pc.ls(selection=True)
# Get camera from user selection.
camera = None
default_cameras = [
"frontShape", "perspShape", "sideShape", "topShape"
]
cameras = [
x for x in pc.ls(type="camera") if x.name() not in default_cameras
]
camera_names = {x.getParent().name(): x for x in cameras}
camera_names["Create new camera."] = "create_camera"
window = CameraWindow(camera_names.keys())
window.exec_()
camera = camera_names[window.camera]
if len(selection) > 1:
QtWidgets.QMessageBox.critical(
None,
"Error!",
"Multiple nodes selected. Please select only one.",
QtWidgets.QMessageBox.Ok
)
return
if camera == "create_camera":
camera = pc.createNode("camera")
if len(selection) < 1:
result = QtWidgets.QMessageBox.critical(
None,
"Error!",
"No camera selected. Do you want to create a camera?",
QtWidgets.QMessageBox.Ok,
QtWidgets.QMessageBox.Cancel
)
if result == QtWidgets.QMessageBox.Ok:
camera = pc.createNode("camera")
else:
return
else:
relatives = pc.listRelatives(selection[0], shapes=True)
if pc.ls(relatives, type="camera"):
camera = selection[0]
else:
QtWidgets.QMessageBox.critical(
None,
"Error!",
"Selected node is not a camera.",
QtWidgets.QMessageBox.Ok
)
if camera is None:
return
try:
@ -100,10 +137,16 @@ class ImagePlaneLoader(api.Loader):
# Ensure OpenEXRLoader plugin is loaded.
pc.loadPlugin("OpenEXRLoader.mll", quiet=True)
message = (
"Hold image sequence on first frame?"
"\n{} files available.".format(
len(context["representation"]["files"])
)
)
reply = QtWidgets.QMessageBox.information(
None,
"Frame Hold.",
"Hold image sequence on first frame?",
message,
QtWidgets.QMessageBox.Ok,
QtWidgets.QMessageBox.Cancel
)

View file

@ -110,6 +110,9 @@ class ExtractPlayblast(pype.api.Extractor):
if not instance.data.get("keepImages"):
tags.append("delete")
# Add camera node name to representation data
camera_node_name = pm.ls(camera)[0].getTransform().getName()
representation = {
'name': 'png',
'ext': 'png',
@ -119,7 +122,8 @@ class ExtractPlayblast(pype.api.Extractor):
"frameEnd": end,
'fps': fps,
'preview': True,
'tags': tags
'tags': tags,
'camera_name': camera_node_name
}
instance.data["representations"].append(representation)

View file

@ -1,5 +1,6 @@
from avalon import api
from avalon.tvpaint import lib
from avalon.vendor import qargparse
from avalon.tvpaint import CommunicatorWrapper
class ImportImage(api.Loader):
@ -16,11 +17,54 @@ class ImportImage(api.Loader):
import_script = (
"filepath = \"{}\"\n"
"layer_name = \"{}\"\n"
"tv_loadsequence filepath \"preload\" PARSE layer_id\n"
"tv_loadsequence filepath {}PARSE layer_id\n"
"tv_layerrename layer_id layer_name"
)
defaults = {
"stretch": True,
"timestretch": True,
"preload": True
}
options = [
qargparse.Boolean(
"stretch",
label="Stretch to project size",
default=True,
help="Stretch loaded image/s to project resolution?"
),
qargparse.Boolean(
"timestretch",
label="Stretch to timeline length",
default=True,
help="Clip loaded image/s to timeline length?"
),
qargparse.Boolean(
"preload",
label="Preload loaded image/s",
default=True,
help="Preload image/s?"
)
]
def load(self, context, name, namespace, options):
stretch = options.get("stretch", self.defaults["stretch"])
timestretch = options.get("timestretch", self.defaults["timestretch"])
preload = options.get("preload", self.defaults["preload"])
load_options = []
if stretch:
load_options.append("\"STRETCH\"")
if timestretch:
load_options.append("\"TIMESTRETCH\"")
if preload:
load_options.append("\"PRELOAD\"")
load_options_str = ""
for load_option in load_options:
load_options_str += (load_option + " ")
# Prepare layer name
asset_name = context["asset"]["name"]
version_name = context["version"]["name"]
@ -33,6 +77,7 @@ class ImportImage(api.Loader):
# - filename mus not contain backwards slashes
george_script = self.import_script.format(
self.fname.replace("\\", "/"),
layer_name
layer_name,
load_options_str
)
return lib.execute_george_through_file(george_script)