diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a0c058f73..cc5bf39a29 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,8 @@
# Changelog
-## [3.12.1-nightly.4](https://github.com/pypeclub/OpenPype/tree/HEAD)
+## [3.12.1](https://github.com/pypeclub/OpenPype/tree/3.12.1) (2022-07-13)
-[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.0...HEAD)
+[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.12.0...3.12.1)
### 📖 Documentation
@@ -14,6 +14,9 @@
**🚀 Enhancements**
+- TrayPublisher: Added more options for grouping of instances [\#3494](https://github.com/pypeclub/OpenPype/pull/3494)
+- NewPublisher: Align creator attributes from top to bottom [\#3487](https://github.com/pypeclub/OpenPype/pull/3487)
+- NewPublisher: Added ability to use label of instance [\#3484](https://github.com/pypeclub/OpenPype/pull/3484)
- General: Creator Plugins have access to project [\#3476](https://github.com/pypeclub/OpenPype/pull/3476)
- General: Better arguments order in creator init [\#3475](https://github.com/pypeclub/OpenPype/pull/3475)
- Ftrack: Trigger custom ftrack events on project creation and preparation [\#3465](https://github.com/pypeclub/OpenPype/pull/3465)
@@ -21,10 +24,15 @@
- Blender: Bugfix - Set fps properly on open [\#3426](https://github.com/pypeclub/OpenPype/pull/3426)
- Hiero: Add custom scripts menu [\#3425](https://github.com/pypeclub/OpenPype/pull/3425)
- Blender: pre pyside install for all platforms [\#3400](https://github.com/pypeclub/OpenPype/pull/3400)
-- Maya: Ability to set resolution for playblasts from asset, and override through review instance. [\#3360](https://github.com/pypeclub/OpenPype/pull/3360)
+- Maya: Add additional playblast options to review Extractor. [\#3384](https://github.com/pypeclub/OpenPype/pull/3384)
**🐛 Bug fixes**
+- TrayPublisher: Keep use instance label in list view [\#3493](https://github.com/pypeclub/OpenPype/pull/3493)
+- General: Extract review use first frame of input sequence [\#3491](https://github.com/pypeclub/OpenPype/pull/3491)
+- General: Fix Plist loading for application launch [\#3485](https://github.com/pypeclub/OpenPype/pull/3485)
+- Nuke: Workfile tools open on start [\#3479](https://github.com/pypeclub/OpenPype/pull/3479)
+- New Publisher: Disabled context change allows creation [\#3478](https://github.com/pypeclub/OpenPype/pull/3478)
- General: thumbnail extractor fix [\#3474](https://github.com/pypeclub/OpenPype/pull/3474)
- Kitsu: bugfix with sync-service ans publish plugins [\#3473](https://github.com/pypeclub/OpenPype/pull/3473)
- Flame: solved problem with multi-selected loading [\#3470](https://github.com/pypeclub/OpenPype/pull/3470)
@@ -38,6 +46,7 @@
- Nuke: Slate frame is integrated [\#3427](https://github.com/pypeclub/OpenPype/pull/3427)
- Maya: Camera extra data - additional fix for \#3304 [\#3386](https://github.com/pypeclub/OpenPype/pull/3386)
- Maya: Handle excluding `model` family from frame range validator. [\#3370](https://github.com/pypeclub/OpenPype/pull/3370)
+- Harmony: audio validator has wrong logic [\#3364](https://github.com/pypeclub/OpenPype/pull/3364)
**🔀 Refactored code**
@@ -46,7 +55,9 @@
- General: Use query functions in global plugins [\#3459](https://github.com/pypeclub/OpenPype/pull/3459)
- Clockify: Use query functions in clockify actions [\#3458](https://github.com/pypeclub/OpenPype/pull/3458)
- General: Use query functions in rest api calls [\#3457](https://github.com/pypeclub/OpenPype/pull/3457)
+- General: Use query functions in openpype lib functions [\#3454](https://github.com/pypeclub/OpenPype/pull/3454)
- General: Use query functions in load utils [\#3446](https://github.com/pypeclub/OpenPype/pull/3446)
+- General: Move publish plugin and publish render abstractions [\#3442](https://github.com/pypeclub/OpenPype/pull/3442)
- General: Use Anatomy after move to pipeline [\#3436](https://github.com/pypeclub/OpenPype/pull/3436)
- General: Anatomy moved to pipeline [\#3435](https://github.com/pypeclub/OpenPype/pull/3435)
- Fusion: Use client query functions [\#3380](https://github.com/pypeclub/OpenPype/pull/3380)
@@ -66,8 +77,6 @@
- Webserver: Added CORS middleware [\#3422](https://github.com/pypeclub/OpenPype/pull/3422)
- Attribute Defs UI: Files widget show what is allowed to drop in [\#3411](https://github.com/pypeclub/OpenPype/pull/3411)
- General: Add ability to change user value for templates [\#3366](https://github.com/pypeclub/OpenPype/pull/3366)
-- Hosts: More options for in-host callbacks [\#3357](https://github.com/pypeclub/OpenPype/pull/3357)
-- Multiverse: expose some settings to GUI [\#3350](https://github.com/pypeclub/OpenPype/pull/3350)
**🐛 Bug fixes**
@@ -82,7 +91,6 @@
- Maya: vray device aspect ratio fix [\#3381](https://github.com/pypeclub/OpenPype/pull/3381)
- Flame: bunch of publishing issues [\#3377](https://github.com/pypeclub/OpenPype/pull/3377)
- Harmony: added unc path to zifile command in Harmony [\#3372](https://github.com/pypeclub/OpenPype/pull/3372)
-- Standalone: settings improvements [\#3355](https://github.com/pypeclub/OpenPype/pull/3355)
**🔀 Refactored code**
@@ -107,37 +115,20 @@
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.11.1-nightly.1...3.11.1)
-**🆕 New features**
-
-- Flame: custom export temp folder [\#3346](https://github.com/pypeclub/OpenPype/pull/3346)
-- Nuke: removing third-party plugins [\#3344](https://github.com/pypeclub/OpenPype/pull/3344)
-
**🚀 Enhancements**
- Pyblish Pype: Hiding/Close issues [\#3367](https://github.com/pypeclub/OpenPype/pull/3367)
-- Ftrack: Removed requirement of pypeclub role from default settings [\#3354](https://github.com/pypeclub/OpenPype/pull/3354)
-- Kitsu: Prevent crash on missing frames information [\#3352](https://github.com/pypeclub/OpenPype/pull/3352)
**🐛 Bug fixes**
- Nuke: bake streams with slate on farm [\#3368](https://github.com/pypeclub/OpenPype/pull/3368)
-- Harmony: audio validator has wrong logic [\#3364](https://github.com/pypeclub/OpenPype/pull/3364)
- Nuke: Fix missing variable in extract thumbnail [\#3363](https://github.com/pypeclub/OpenPype/pull/3363)
- Nuke: Fix precollect writes [\#3361](https://github.com/pypeclub/OpenPype/pull/3361)
-- AE- fix validate\_scene\_settings and renderLocal [\#3358](https://github.com/pypeclub/OpenPype/pull/3358)
-- deadline: fixing misidentification of revieables [\#3356](https://github.com/pypeclub/OpenPype/pull/3356)
-- General: Create only one thumbnail per instance [\#3351](https://github.com/pypeclub/OpenPype/pull/3351)
-- nuke: adding extract thumbnail settings 3.10 [\#3347](https://github.com/pypeclub/OpenPype/pull/3347)
-- General: Fix last version function [\#3345](https://github.com/pypeclub/OpenPype/pull/3345)
## [3.11.0](https://github.com/pypeclub/OpenPype/tree/3.11.0) (2022-06-17)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.11.0-nightly.4...3.11.0)
-**🐛 Bug fixes**
-
-- General: Handle empty source key on instance [\#3342](https://github.com/pypeclub/OpenPype/pull/3342)
-
## [3.10.0](https://github.com/pypeclub/OpenPype/tree/3.10.0) (2022-05-26)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.10.0-nightly.6...3.10.0)
diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py
index 34340a13a5..e4221978c0 100644
--- a/openpype/hosts/maya/api/lib.py
+++ b/openpype/hosts/maya/api/lib.py
@@ -2522,12 +2522,30 @@ def load_capture_preset(data=None):
temp_options2['multiSampleEnable'] = False
temp_options2['multiSampleCount'] = preset[id][key]
+ if key == 'renderDepthOfField':
+ temp_options2['renderDepthOfField'] = preset[id][key]
+
if key == 'ssaoEnable':
if preset[id][key] is True:
temp_options2['ssaoEnable'] = True
else:
temp_options2['ssaoEnable'] = False
+ if key == 'ssaoSamples':
+ temp_options2['ssaoSamples'] = preset[id][key]
+
+ if key == 'ssaoAmount':
+ temp_options2['ssaoAmount'] = preset[id][key]
+
+ if key == 'ssaoRadius':
+ temp_options2['ssaoRadius'] = preset[id][key]
+
+ if key == 'hwFogDensity':
+ temp_options2['hwFogDensity'] = preset[id][key]
+
+ if key == 'ssaoFilterRadius':
+ temp_options2['ssaoFilterRadius'] = preset[id][key]
+
if key == 'alphaCut':
temp_options2['transparencyAlgorithm'] = 5
temp_options2['transparencyQuality'] = 1
@@ -2535,6 +2553,48 @@ def load_capture_preset(data=None):
if key == 'headsUpDisplay':
temp_options['headsUpDisplay'] = True
+ if key == 'fogging':
+ temp_options['fogging'] = preset[id][key] or False
+
+ if key == 'hwFogStart':
+ temp_options2['hwFogStart'] = preset[id][key]
+
+ if key == 'hwFogEnd':
+ temp_options2['hwFogEnd'] = preset[id][key]
+
+ if key == 'hwFogAlpha':
+ temp_options2['hwFogAlpha'] = preset[id][key]
+
+ if key == 'hwFogFalloff':
+ temp_options2['hwFogFalloff'] = int(preset[id][key])
+
+ if key == 'hwFogColorR':
+ temp_options2['hwFogColorR'] = preset[id][key]
+
+ if key == 'hwFogColorG':
+ temp_options2['hwFogColorG'] = preset[id][key]
+
+ if key == 'hwFogColorB':
+ temp_options2['hwFogColorB'] = preset[id][key]
+
+ if key == 'motionBlurEnable':
+ if preset[id][key] is True:
+ temp_options2['motionBlurEnable'] = True
+ else:
+ temp_options2['motionBlurEnable'] = False
+
+ if key == 'motionBlurSampleCount':
+ temp_options2['motionBlurSampleCount'] = preset[id][key]
+
+ if key == 'motionBlurShutterOpenFraction':
+ temp_options2['motionBlurShutterOpenFraction'] = preset[id][key]
+
+ if key == 'lineAAEnable':
+ if preset[id][key] is True:
+ temp_options2['lineAAEnable'] = True
+ else:
+ temp_options2['lineAAEnable'] = False
+
else:
temp_options[str(key)] = preset[id][key]
@@ -2544,7 +2604,24 @@ def load_capture_preset(data=None):
'gpuCacheDisplayFilter',
'multiSample',
'ssaoEnable',
- 'textureMaxResolution'
+ 'ssaoSamples',
+ 'ssaoAmount',
+ 'ssaoFilterRadius',
+ 'ssaoRadius',
+ 'hwFogStart',
+ 'hwFogEnd',
+ 'hwFogAlpha',
+ 'hwFogFalloff',
+ 'hwFogColorR',
+ 'hwFogColorG',
+ 'hwFogColorB',
+ 'hwFogDensity',
+ 'textureMaxResolution',
+ 'motionBlurEnable',
+ 'motionBlurSampleCount',
+ 'motionBlurShutterOpenFraction',
+ 'lineAAEnable',
+ 'renderDepthOfField'
]:
temp_options.pop(key, None)
diff --git a/openpype/hosts/nuke/plugins/load/load_clip.py b/openpype/hosts/nuke/plugins/load/load_clip.py
index d177e6ba76..b2dc4a52d7 100644
--- a/openpype/hosts/nuke/plugins/load/load_clip.py
+++ b/openpype/hosts/nuke/plugins/load/load_clip.py
@@ -54,20 +54,28 @@ class LoadClip(plugin.NukeLoader):
script_start = int(nuke.root()["first_frame"].value())
# option gui
- defaults = {
- "start_at_workfile": True
+ options_defaults = {
+ "start_at_workfile": True,
+ "add_retime": True
}
- options = [
- qargparse.Boolean(
- "start_at_workfile",
- help="Load at workfile start frame",
- default=True
- )
- ]
-
node_name_template = "{class_name}_{ext}"
+ @classmethod
+ def get_options(cls, *args):
+ return [
+ qargparse.Boolean(
+ "start_at_workfile",
+ help="Load at workfile start frame",
+ default=cls.options_defaults["start_at_workfile"]
+ ),
+ qargparse.Boolean(
+ "add_retime",
+ help="Load with retime",
+ default=cls.options_defaults["add_retime"]
+ )
+ ]
+
@classmethod
def get_representations(cls):
return (
@@ -86,7 +94,10 @@ class LoadClip(plugin.NukeLoader):
file = self.fname.replace("\\", "/")
start_at_workfile = options.get(
- "start_at_workfile", self.defaults["start_at_workfile"])
+ "start_at_workfile", self.options_defaults["start_at_workfile"])
+
+ add_retime = options.get(
+ "add_retime", self.options_defaults["add_retime"])
version = context['version']
version_data = version.get("data", {})
@@ -151,7 +162,7 @@ class LoadClip(plugin.NukeLoader):
data_imprint = {}
for k in add_keys:
if k == 'version':
- data_imprint.update({k: context["version"]['name']})
+ data_imprint[k] = context["version"]['name']
elif k == 'colorspace':
colorspace = repre["data"].get(k)
colorspace = colorspace or version_data.get(k)
@@ -159,10 +170,13 @@ class LoadClip(plugin.NukeLoader):
if used_colorspace:
data_imprint["used_colorspace"] = used_colorspace
else:
- data_imprint.update(
- {k: context["version"]['data'].get(k, str(None))})
+ data_imprint[k] = context["version"]['data'].get(
+ k, str(None))
- data_imprint.update({"objectName": read_name})
+ data_imprint["objectName"] = read_name
+
+ if add_retime and version_data.get("retime", None):
+ data_imprint["addRetime"] = True
read_node["tile_color"].setValue(int("0x4ecd25ff", 16))
@@ -174,7 +188,7 @@ class LoadClip(plugin.NukeLoader):
loader=self.__class__.__name__,
data=data_imprint)
- if version_data.get("retime", None):
+ if add_retime and version_data.get("retime", None):
self._make_retimes(read_node, version_data)
self.set_as_member(read_node)
@@ -198,7 +212,12 @@ class LoadClip(plugin.NukeLoader):
read_node = nuke.toNode(container['objectName'])
file = get_representation_path(representation).replace("\\", "/")
- start_at_workfile = bool("start at" in read_node['frame_mode'].value())
+ start_at_workfile = "start at" in read_node['frame_mode'].value()
+
+ add_retime = [
+ key for key in read_node.knobs().keys()
+ if "addRetime" in key
+ ]
project_name = legacy_io.active_project()
version_doc = get_version_by_id(project_name, representation["parent"])
@@ -286,7 +305,7 @@ class LoadClip(plugin.NukeLoader):
"updated to version: {}".format(version_doc.get("name"))
)
- if version_data.get("retime", None):
+ if add_retime and version_data.get("retime", None):
self._make_retimes(read_node, version_data)
else:
self.clear_members(read_node)
diff --git a/openpype/hosts/traypublisher/api/__init__.py b/openpype/hosts/traypublisher/api/__init__.py
index c461c0c526..4e7284b09a 100644
--- a/openpype/hosts/traypublisher/api/__init__.py
+++ b/openpype/hosts/traypublisher/api/__init__.py
@@ -1,20 +1,8 @@
from .pipeline import (
- install,
- ls,
-
- set_project_name,
- get_context_title,
- get_context_data,
- update_context_data,
+ TrayPublisherHost,
)
__all__ = (
- "install",
- "ls",
-
- "set_project_name",
- "get_context_title",
- "get_context_data",
- "update_context_data",
+ "TrayPublisherHost",
)
diff --git a/openpype/hosts/traypublisher/api/pipeline.py b/openpype/hosts/traypublisher/api/pipeline.py
index 954a0bae47..2d9db7801e 100644
--- a/openpype/hosts/traypublisher/api/pipeline.py
+++ b/openpype/hosts/traypublisher/api/pipeline.py
@@ -9,6 +9,8 @@ from openpype.pipeline import (
register_creator_plugin_path,
legacy_io,
)
+from openpype.host import HostBase, INewPublisher
+
ROOT_DIR = os.path.dirname(os.path.dirname(
os.path.abspath(__file__)
@@ -17,6 +19,35 @@ PUBLISH_PATH = os.path.join(ROOT_DIR, "plugins", "publish")
CREATE_PATH = os.path.join(ROOT_DIR, "plugins", "create")
+class TrayPublisherHost(HostBase, INewPublisher):
+ name = "traypublisher"
+
+ def install(self):
+ os.environ["AVALON_APP"] = self.name
+ legacy_io.Session["AVALON_APP"] = self.name
+
+ pyblish.api.register_host("traypublisher")
+ pyblish.api.register_plugin_path(PUBLISH_PATH)
+ register_creator_plugin_path(CREATE_PATH)
+
+ def get_context_title(self):
+ return HostContext.get_project_name()
+
+ def get_context_data(self):
+ return HostContext.get_context_data()
+
+ def update_context_data(self, data, changes):
+ HostContext.save_context_data(data, changes)
+
+ def set_project_name(self, project_name):
+ # TODO Deregister project specific plugins and register new project
+ # plugins
+ os.environ["AVALON_PROJECT"] = project_name
+ legacy_io.Session["AVALON_PROJECT"] = project_name
+ legacy_io.install()
+ HostContext.set_project_name(project_name)
+
+
class HostContext:
_context_json_path = None
@@ -150,32 +181,3 @@ def get_context_data():
def update_context_data(data, changes):
HostContext.save_context_data(data)
-
-
-def get_context_title():
- return HostContext.get_project_name()
-
-
-def ls():
- """Probably will never return loaded containers."""
- return []
-
-
-def install():
- """This is called before a project is known.
-
- Project is defined with 'set_project_name'.
- """
- os.environ["AVALON_APP"] = "traypublisher"
-
- pyblish.api.register_host("traypublisher")
- pyblish.api.register_plugin_path(PUBLISH_PATH)
- register_creator_plugin_path(CREATE_PATH)
-
-
-def set_project_name(project_name):
- # TODO Deregister project specific plugins and register new project plugins
- os.environ["AVALON_PROJECT"] = project_name
- legacy_io.Session["AVALON_PROJECT"] = project_name
- legacy_io.install()
- HostContext.set_project_name(project_name)
diff --git a/openpype/hosts/traypublisher/api/plugin.py b/openpype/hosts/traypublisher/api/plugin.py
index 202664cfc6..cc93d7c157 100644
--- a/openpype/hosts/traypublisher/api/plugin.py
+++ b/openpype/hosts/traypublisher/api/plugin.py
@@ -37,6 +37,21 @@ class TrayPublishCreator(Creator):
# Use same attributes as for instance attrobites
return self.get_instance_attr_defs()
+ def _store_new_instance(self, new_instance):
+ """Tray publisher specific method to store instance.
+
+ Instance is stored into "workfile" of traypublisher and also add it
+ to CreateContext.
+
+ Args:
+ new_instance (CreatedInstance): Instance that should be stored.
+ """
+
+ # Host implementation of storing metadata about instance
+ HostContext.add_instance(new_instance.data_to_store())
+ # Add instance to current context
+ self._add_instance_to_context(new_instance)
+
class SettingsCreator(TrayPublishCreator):
create_allow_context_change = True
@@ -58,10 +73,8 @@ class SettingsCreator(TrayPublishCreator):
data["settings_creator"] = True
# Create new instance
new_instance = CreatedInstance(self.family, subset_name, data, self)
- # Host implementation of storing metadata about instance
- HostContext.add_instance(new_instance.data_to_store())
- # Add instance to current context
- self._add_instance_to_context(new_instance)
+
+ self._store_new_instance(new_instance)
def get_instance_attr_defs(self):
return [
diff --git a/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py b/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py
index b2be43c701..f76306cf05 100644
--- a/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py
+++ b/openpype/hosts/traypublisher/plugins/publish/collect_simple_instances.py
@@ -1,4 +1,6 @@
import os
+
+import clique
import pyblish.api
@@ -29,6 +31,14 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin):
for filename in filepath_item["filenames"]
]
+ cols, rems = clique.assemble(filepaths)
+ source = None
+ if cols:
+ source = cols[0].format("{head}{padding}{tail}")
+ elif rems:
+ source = rems[0]
+
+ instance.data["source"] = source
instance.data["sourceFilepaths"] = filepaths
instance.data["stagingDir"] = filepath_item["directory"]
@@ -45,6 +55,8 @@ class CollectSettingsSimpleInstances(pyblish.api.InstancePlugin):
"files": filenames
})
+ instance.data["source"] = "\n".join(filepaths)
+
self.log.debug("Created Simple Settings instance {}".format(
instance.data
))
diff --git a/openpype/lib/events.py b/openpype/lib/events.py
index 7bec6ee30d..301d62e2a6 100644
--- a/openpype/lib/events.py
+++ b/openpype/lib/events.py
@@ -11,6 +11,10 @@ except Exception:
from openpype.lib.python_2_comp import WeakMethod
+class MissingEventSystem(Exception):
+ pass
+
+
class EventCallback(object):
"""Callback registered to a topic.
@@ -176,16 +180,20 @@ class Event(object):
topic (str): Identifier of event.
data (Any): Data specific for event. Dictionary is recommended.
source (str): Identifier of source.
+ event_system (EventSystem): Event system in which can be event
+ triggered.
"""
+
_data = {}
- def __init__(self, topic, data=None, source=None):
+ def __init__(self, topic, data=None, source=None, event_system=None):
self._id = str(uuid4())
self._topic = topic
if data is None:
data = {}
self._data = data
self._source = source
+ self._event_system = event_system
def __getitem__(self, key):
return self._data[key]
@@ -211,28 +219,118 @@ class Event(object):
def emit(self):
"""Emit event and trigger callbacks."""
- StoredCallbacks.emit_event(self)
+ if self._event_system is None:
+ raise MissingEventSystem(
+ "Can't emit event {}. Does not have set event system.".format(
+ str(repr(self))
+ )
+ )
+ self._event_system.emit_event(self)
-class StoredCallbacks:
- _registered_callbacks = []
+class EventSystem(object):
+ """Encapsulate event handling into an object.
+
+ System wraps registered callbacks and triggered events into single object
+ so it is possible to create mutltiple independent systems that have their
+ topics and callbacks.
+
+
+ """
+
+ def __init__(self):
+ self._registered_callbacks = []
+
+ def add_callback(self, topic, callback):
+ """Register callback in event system.
+
+ Args:
+ topic (str): Topic for EventCallback.
+ callback (Callable): Function or method that will be called
+ when topic is triggered.
+
+ Returns:
+ EventCallback: Created callback object which can be used to
+ stop listening.
+ """
- @classmethod
- def add_callback(cls, topic, callback):
callback = EventCallback(topic, callback)
- cls._registered_callbacks.append(callback)
+ self._registered_callbacks.append(callback)
return callback
- @classmethod
- def emit_event(cls, event):
+ def create_event(self, topic, data, source):
+ """Create new event which is bound to event system.
+
+ Args:
+ topic (str): Event topic.
+ data (dict): Data related to event.
+ source (str): Source of event.
+
+ Returns:
+ Event: Object of event.
+ """
+
+ return Event(topic, data, source, self)
+
+ def emit(self, topic, data, source):
+ """Create event based on passed data and emit it.
+
+ This is easiest way how to trigger event in an event system.
+
+ Args:
+ topic (str): Event topic.
+ data (dict): Data related to event.
+ source (str): Source of event.
+
+ Returns:
+ Event: Created and emitted event.
+ """
+
+ event = self.create_event(topic, data, source)
+ event.emit()
+ return event
+
+ def emit_event(self, event):
+ """Emit event object.
+
+ Args:
+ event (Event): Prepared event with topic and data.
+ """
+
invalid_callbacks = []
- for callback in cls._registered_callbacks:
+ for callback in self._registered_callbacks:
callback.process_event(event)
if not callback.is_ref_valid:
invalid_callbacks.append(callback)
for callback in invalid_callbacks:
- cls._registered_callbacks.remove(callback)
+ self._registered_callbacks.remove(callback)
+
+
+class GlobalEventSystem:
+ """Event system living in global scope of process.
+
+ This is primarily used in host implementation to trigger events
+ related to DCC changes or changes of context in the host implementation.
+ """
+
+ _global_event_system = None
+
+ @classmethod
+ def get_global_event_system(cls):
+ if cls._global_event_system is None:
+ cls._global_event_system = EventSystem()
+ return cls._global_event_system
+
+ @classmethod
+ def add_callback(cls, topic, callback):
+ event_system = cls.get_global_event_system()
+ return event_system.add_callback(topic, callback)
+
+ @classmethod
+ def emit(cls, topic, data, source):
+ event_system = cls.get_global_event_system()
+ return event_system.emit(topic, data, source)
def register_event_callback(topic, callback):
@@ -249,7 +347,8 @@ def register_event_callback(topic, callback):
enable/disable listening to a topic or remove the callback from
the topic completely.
"""
- return StoredCallbacks.add_callback(topic, callback)
+
+ return GlobalEventSystem.add_callback(topic, callback)
def emit_event(topic, data=None, source=None):
@@ -263,6 +362,5 @@ def emit_event(topic, data=None, source=None):
Returns:
Event: Object of event that was emitted.
"""
- event = Event(topic, data, source)
- event.emit()
- return event
+
+ return GlobalEventSystem.emit(topic, data, source)
diff --git a/openpype/modules/ftrack/event_handlers_user/action_create_project_structure.py b/openpype/modules/ftrack/event_handlers_user/action_create_project_structure.py
index ebea8872f9..df914de854 100644
--- a/openpype/modules/ftrack/event_handlers_user/action_create_project_structure.py
+++ b/openpype/modules/ftrack/event_handlers_user/action_create_project_structure.py
@@ -84,6 +84,11 @@ class CreateProjectFolders(BaseAction):
create_project_folders(basic_paths, project_name)
self.create_ftrack_entities(basic_paths, project_entity)
+ self.trigger_event(
+ "openpype.project.structure.created",
+ {"project_name": project_name}
+ )
+
except Exception as exc:
self.log.warning("Creating of structure crashed.", exc_info=True)
session.rollback()
diff --git a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py
index 952b21546d..77a7ebdfcf 100644
--- a/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py
+++ b/openpype/modules/ftrack/plugins/publish/integrate_ftrack_note.py
@@ -116,6 +116,7 @@ class IntegrateFtrackNote(pyblish.api.InstancePlugin):
"app_name": app_name,
"app_label": app_label,
"published_paths": "
".join(sorted(published_paths)),
+ "source": instance.data.get("source", '')
}
comment = template.format(**format_data)
if not comment:
diff --git a/openpype/modules/timers_manager/timers_manager.py b/openpype/modules/timers_manager/timers_manager.py
index 3cf1614316..3453e4bc4c 100644
--- a/openpype/modules/timers_manager/timers_manager.py
+++ b/openpype/modules/timers_manager/timers_manager.py
@@ -2,13 +2,13 @@ import os
import platform
+from openpype.client import get_asset_by_name
from openpype.modules import OpenPypeModule
from openpype_interfaces import (
ITrayService,
ILaunchHookPaths
)
from openpype.lib.events import register_event_callback
-from openpype.pipeline import AvalonMongoDB
from .exceptions import InvalidContextError
@@ -197,22 +197,13 @@ class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths):
" Project: \"{}\" Asset: \"{}\" Task: \"{}\""
).format(str(project_name), str(asset_name), str(task_name)))
- dbconn = AvalonMongoDB()
- dbconn.install()
- dbconn.Session["AVALON_PROJECT"] = project_name
-
- asset_doc = dbconn.find_one(
- {
- "type": "asset",
- "name": asset_name
- },
- {
- "data.tasks": True,
- "data.parents": True
- }
+ asset_doc = get_asset_by_name(
+ project_name,
+ asset_name,
+ fields=["_id", "name", "data.tasks", "data.parents"]
)
+
if not asset_doc:
- dbconn.uninstall()
raise InvalidContextError((
"Asset \"{}\" not found in project \"{}\""
).format(asset_name, project_name))
@@ -220,7 +211,6 @@ class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths):
asset_data = asset_doc.get("data") or {}
asset_tasks = asset_data.get("tasks") or {}
if task_name not in asset_tasks:
- dbconn.uninstall()
raise InvalidContextError((
"Task \"{}\" not found on asset \"{}\" in project \"{}\""
).format(task_name, asset_name, project_name))
@@ -238,9 +228,10 @@ class TimersManager(OpenPypeModule, ITrayService, ILaunchHookPaths):
hierarchy_items = asset_data.get("parents") or []
hierarchy_items.append(asset_name)
- dbconn.uninstall()
return {
"project_name": project_name,
+ "asset_id": str(asset_doc["_id"]),
+ "asset_name": asset_doc["name"],
"task_name": task_name,
"task_type": task_type,
"hierarchy": hierarchy_items
diff --git a/openpype/pipeline/create/context.py b/openpype/pipeline/create/context.py
index aecdb04635..9b55c3b21e 100644
--- a/openpype/pipeline/create/context.py
+++ b/openpype/pipeline/create/context.py
@@ -29,6 +29,7 @@ UpdateData = collections.namedtuple("UpdateData", ["instance", "changes"])
class ImmutableKeyError(TypeError):
"""Accessed key is immutable so does not allow changes or removements."""
+
def __init__(self, key, msg=None):
self.immutable_key = key
if not msg:
@@ -40,6 +41,7 @@ class ImmutableKeyError(TypeError):
class HostMissRequiredMethod(Exception):
"""Host does not have implemented required functions for creation."""
+
def __init__(self, host, missing_methods):
self.missing_methods = missing_methods
self.host = host
@@ -66,6 +68,7 @@ class InstanceMember:
TODO:
Implement and use!
"""
+
def __init__(self, instance, name):
self.instance = instance
@@ -94,6 +97,7 @@ class AttributeValues:
values(dict): Values after possible conversion.
origin_data(dict): Values loaded from host before conversion.
"""
+
def __init__(self, attr_defs, values, origin_data=None):
from openpype.lib.attribute_definitions import UnknownDef
@@ -174,6 +178,10 @@ class AttributeValues:
output = {}
for key in self._data:
output[key] = self[key]
+
+ for key, attr_def in self._attr_defs_by_key.items():
+ if key not in output:
+ output[key] = attr_def.default
return output
@staticmethod
@@ -196,6 +204,7 @@ class CreatorAttributeValues(AttributeValues):
Args:
instance (CreatedInstance): Instance for which are values hold.
"""
+
def __init__(self, instance, *args, **kwargs):
self.instance = instance
super(CreatorAttributeValues, self).__init__(*args, **kwargs)
@@ -211,6 +220,7 @@ class PublishAttributeValues(AttributeValues):
publish_attributes(PublishAttributes): Wrapper for multiple publish
attributes is used as parent object.
"""
+
def __init__(self, publish_attributes, *args, **kwargs):
self.publish_attributes = publish_attributes
super(PublishAttributeValues, self).__init__(*args, **kwargs)
@@ -232,6 +242,7 @@ class PublishAttributes:
attr_plugins(list): List of publish plugins that may have defined
attribute definitions.
"""
+
def __init__(self, parent, origin_data, attr_plugins=None):
self.parent = parent
self._origin_data = copy.deepcopy(origin_data)
@@ -270,6 +281,7 @@ class PublishAttributes:
key(str): Plugin name.
default: Default value if plugin was not found.
"""
+
if key not in self._data:
return default
@@ -287,11 +299,13 @@ class PublishAttributes:
def plugin_names_order(self):
"""Plugin names order by their 'order' attribute."""
+
for name in self._plugin_names_order:
yield name
def data_to_store(self):
"""Convert attribute values to "data to store"."""
+
output = {}
for key, attr_value in self._data.items():
output[key] = attr_value.data_to_store()
@@ -299,6 +313,7 @@ class PublishAttributes:
def changes(self):
"""Return changes per each key."""
+
changes = {}
for key, attr_val in self._data.items():
attr_changes = attr_val.changes()
@@ -314,6 +329,7 @@ class PublishAttributes:
def set_publish_plugins(self, attr_plugins):
"""Set publish plugins attribute definitions."""
+
self._plugin_names_order = []
self._missing_plugins = []
self.attr_plugins = attr_plugins or []
@@ -365,6 +381,7 @@ class CreatedInstance:
`openpype.pipeline.registered_host`.
new(bool): Is instance new.
"""
+
# Keys that can't be changed or removed from data after loading using
# creator.
# - 'creator_attributes' and 'publish_attributes' can change values of
@@ -566,6 +583,7 @@ class CreatedInstance:
@property
def id(self):
"""Instance identifier."""
+
return self._data["instance_id"]
@property
@@ -574,10 +592,12 @@ class CreatedInstance:
Access to data is needed to modify values.
"""
+
return self
def changes(self):
"""Calculate and return changes."""
+
changes = {}
new_keys = set()
for key, new_value in self._data.items():
@@ -716,6 +736,7 @@ class CreateContext:
self.manual_creators = {}
self.publish_discover_result = None
+ self.publish_plugins_mismatch_targets = []
self.publish_plugins = []
self.plugins_with_defs = []
self._attr_plugins_by_family = {}
@@ -838,6 +859,7 @@ class CreateContext:
discover_result = DiscoverResult()
plugins_with_defs = []
plugins_by_targets = []
+ plugins_mismatch_targets = []
if discover_publish_plugins:
discover_result = publish_plugins_discover()
publish_plugins = discover_result.plugins
@@ -847,11 +869,19 @@ class CreateContext:
plugins_by_targets = pyblish.logic.plugins_by_targets(
publish_plugins, list(targets)
)
+
# Collect plugins that can have attribute definitions
for plugin in publish_plugins:
if OpenPypePyblishPluginMixin in inspect.getmro(plugin):
plugins_with_defs.append(plugin)
+ plugins_mismatch_targets = [
+ plugin
+ for plugin in publish_plugins
+ if plugin not in plugins_by_targets
+ ]
+
+ self.publish_plugins_mismatch_targets = plugins_mismatch_targets
self.publish_discover_result = discover_result
self.publish_plugins = plugins_by_targets
self.plugins_with_defs = plugins_with_defs
diff --git a/openpype/pipeline/create/creator_plugins.py b/openpype/pipeline/create/creator_plugins.py
index 91b9d80234..52c76db5ef 100644
--- a/openpype/pipeline/create/creator_plugins.py
+++ b/openpype/pipeline/create/creator_plugins.py
@@ -102,6 +102,10 @@ class BaseCreator:
return self.create_context.project_name
+ @property
+ def host(self):
+ return self.create_context.host
+
def get_group_label(self):
"""Group label under which are instances grouped in UI.
diff --git a/openpype/plugins/publish/collect_from_create_context.py b/openpype/plugins/publish/collect_from_create_context.py
index f6ead98809..d2be633cbe 100644
--- a/openpype/plugins/publish/collect_from_create_context.py
+++ b/openpype/plugins/publish/collect_from_create_context.py
@@ -47,12 +47,11 @@ class CollectFromCreateContext(pyblish.api.ContextPlugin):
"label": subset,
"name": subset,
"family": in_data["family"],
- "families": instance_families
+ "families": instance_families,
+ "representations": []
})
for key, value in in_data.items():
if key not in instance.data:
instance.data[key] = value
self.log.info("collected instance: {}".format(instance.data))
self.log.info("parsing data: {}".format(in_data))
-
- instance.data["representations"] = list()
diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json
index 9bebd92cb9..5976c6a823 100644
--- a/openpype/settings/defaults/project_settings/maya.json
+++ b/openpype/settings/defaults/project_settings/maya.json
@@ -497,11 +497,29 @@
"override_viewport_options": true,
"displayLights": "default",
"textureMaxResolution": 1024,
- "multiSample": 4,
+ "renderDepthOfField": true,
"shadows": true,
"textures": true,
"twoSidedLighting": true,
- "ssaoEnable": true,
+ "lineAAEnable": true,
+ "multiSample": 8,
+ "ssaoEnable": false,
+ "ssaoAmount": 1,
+ "ssaoRadius": 16,
+ "ssaoFilterRadius": 16,
+ "ssaoSamples": 16,
+ "fogging": false,
+ "hwFogFalloff": "0",
+ "hwFogDensity": 0.0,
+ "hwFogStart": 0,
+ "hwFogEnd": 100,
+ "hwFogAlpha": 0,
+ "hwFogColorR": 1.0,
+ "hwFogColorG": 1.0,
+ "hwFogColorB": 1.0,
+ "motionBlurEnable": false,
+ "motionBlurSampleCount": 8,
+ "motionBlurShutterOpenFraction": 0.2,
"cameras": false,
"clipGhosts": false,
"controlVertices": false,
diff --git a/openpype/settings/defaults/project_settings/nuke.json b/openpype/settings/defaults/project_settings/nuke.json
index 6c45e2a9c1..3e29122074 100644
--- a/openpype/settings/defaults/project_settings/nuke.json
+++ b/openpype/settings/defaults/project_settings/nuke.json
@@ -287,7 +287,11 @@
"LoadClip": {
"enabled": true,
"_representations": [],
- "node_name_template": "{class_name}_{ext}"
+ "node_name_template": "{class_name}_{ext}",
+ "options_defaults": {
+ "start_at_workfile": true,
+ "add_retime": true
+ }
}
},
"workfile_builder": {
diff --git a/openpype/settings/defaults/project_settings/traypublisher.json b/openpype/settings/defaults/project_settings/traypublisher.json
index 0b54cfd39e..d3e8028cdb 100644
--- a/openpype/settings/defaults/project_settings/traypublisher.json
+++ b/openpype/settings/defaults/project_settings/traypublisher.json
@@ -8,8 +8,8 @@
"default_variants": [
"Main"
],
- "description": "Publish workfile backup",
- "detailed_description": "",
+ "description": "Backup of a working scene",
+ "detailed_description": "Workfiles are full scenes from any application that are directly edited by artists. They represent a state of work on a task at a given point and are usually not directly referenced into other scenes.",
"allow_sequences": true,
"extensions": [
".ma",
@@ -30,6 +30,201 @@
".psb",
".aep"
]
+ },
+ {
+ "family": "model",
+ "identifier": "",
+ "label": "Model",
+ "icon": "fa.cubes",
+ "default_variants": [
+ "Main",
+ "Proxy",
+ "Sculpt"
+ ],
+ "description": "Clean models",
+ "detailed_description": "Models should only contain geometry data, without any extras like cameras, locators or bones.\n\nKeep in mind that models published from tray publisher are not validated for correctness. ",
+ "allow_sequences": false,
+ "extensions": [
+ ".ma",
+ ".mb",
+ ".obj",
+ ".abc",
+ ".fbx",
+ ".bgeo",
+ ".bgeogz",
+ ".bgeosc",
+ ".usd",
+ ".blend"
+ ]
+ },
+ {
+ "family": "pointcache",
+ "identifier": "",
+ "label": "Pointcache",
+ "icon": "fa.gears",
+ "default_variants": [
+ "Main"
+ ],
+ "description": "Geometry Caches",
+ "detailed_description": "Alembic or bgeo cache of animated data",
+ "allow_sequences": true,
+ "extensions": [
+ ".abc",
+ ".bgeo",
+ ".bgeogz",
+ ".bgeosc"
+ ]
+ },
+ {
+ "family": "plate",
+ "identifier": "",
+ "label": "Plate",
+ "icon": "mdi.camera-image",
+ "default_variants": [
+ "Main",
+ "BG",
+ "Animatic",
+ "Reference",
+ "Offline"
+ ],
+ "description": "Footage Plates",
+ "detailed_description": "Any type of image seqeuence coming from outside of the studio. Usually camera footage, but could also be animatics used for reference.",
+ "allow_sequences": true,
+ "extensions": [
+ ".exr",
+ ".png",
+ ".dpx",
+ ".jpg",
+ ".tiff",
+ ".tif",
+ ".mov",
+ ".mp4",
+ ".avi"
+ ]
+ },
+ {
+ "family": "render",
+ "identifier": "",
+ "label": "Render",
+ "icon": "mdi.folder-multiple-image",
+ "default_variants": [],
+ "description": "Rendered images or video",
+ "detailed_description": "Sequence or single file renders",
+ "allow_sequences": true,
+ "extensions": [
+ ".exr",
+ ".png",
+ ".dpx",
+ ".jpg",
+ ".jpeg",
+ ".tiff",
+ ".tif",
+ ".mov",
+ ".mp4",
+ ".avi"
+ ]
+ },
+ {
+ "family": "camera",
+ "identifier": "",
+ "label": "Camera",
+ "icon": "fa.video-camera",
+ "default_variants": [],
+ "description": "3d Camera",
+ "detailed_description": "Ideally this should be only camera itself with baked animation, however, it can technically also include helper geometry.",
+ "allow_sequences": false,
+ "extensions": [
+ ".abc",
+ ".ma",
+ ".hip",
+ ".blend",
+ ".fbx",
+ ".usd"
+ ]
+ },
+ {
+ "family": "image",
+ "identifier": "",
+ "label": "Image",
+ "icon": "fa.image",
+ "default_variants": [
+ "Reference",
+ "Texture",
+ "Concept",
+ "Background"
+ ],
+ "description": "Single image",
+ "detailed_description": "Any image data can be published as image family. References, textures, concept art, matte paints. This is a fallback 2d family for everything that doesn't fit more specific family.",
+ "allow_sequences": false,
+ "extensions": [
+ ".exr",
+ ".jpg",
+ ".jpeg",
+ ".dpx",
+ ".bmp",
+ ".tif",
+ ".tiff",
+ ".png",
+ ".psb",
+ ".psd"
+ ]
+ },
+ {
+ "family": "vdb",
+ "identifier": "",
+ "label": "VDB Volumes",
+ "icon": "fa.cloud",
+ "default_variants": [],
+ "description": "Sparse volumetric data",
+ "detailed_description": "Hierarchical data structure for the efficient storage and manipulation of sparse volumetric data discretized on three-dimensional grids",
+ "allow_sequences": true,
+ "extensions": [
+ ".vdb"
+ ]
+ },
+ {
+ "family": "matchmove",
+ "identifier": "",
+ "label": "Matchmove",
+ "icon": "fa.empire",
+ "default_variants": [
+ "Camera",
+ "Object",
+ "Mocap"
+ ],
+ "description": "Matchmoving script",
+ "detailed_description": "Script exported from matchmoving application to be later processed into a tracked camera with additional data",
+ "allow_sequences": false,
+ "extensions": []
+ },
+ {
+ "family": "rig",
+ "identifier": "",
+ "label": "Rig",
+ "icon": "fa.wheelchair",
+ "default_variants": [],
+ "description": "CG rig file",
+ "detailed_description": "CG rigged character or prop. Rig should be clean of any extra data and directly loadable into it's respective application\t",
+ "allow_sequences": false,
+ "extensions": [
+ ".ma",
+ ".blend",
+ ".hip",
+ ".hda"
+ ]
+ },
+ {
+ "family": "simpleUnrealTexture",
+ "identifier": "",
+ "label": "Simple UE texture",
+ "icon": "fa.image",
+ "default_variants": [
+ ""
+ ],
+ "description": "Simple Unreal Engine texture",
+ "detailed_description": "Texture files with Unreal Engine naming conventions",
+ "allow_sequences": false,
+ "extensions": []
}
]
}
\ No newline at end of file
diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json
index f8f9d5093d..c0069dcdab 100644
--- a/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json
+++ b/openpype/settings/entities/schemas/projects_schema/schema_project_ftrack.json
@@ -822,7 +822,7 @@
},
{
"type": "label",
- "label": "Template may contain formatting keys intent, comment, host_name, app_name, app_label and published_paths."
+ "label": "Template may contain formatting keys intent, comment, host_name, app_name, app_label, published_paths and source."
},
{
"type": "text",
diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json
index d6b81c8687..7a40f349cc 100644
--- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json
+++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_maya_capture.json
@@ -202,12 +202,15 @@
"decimal": 0
},
{
- "type": "number",
- "key": "multiSample",
- "label": "Anti Aliasing Samples",
- "decimal": 0,
- "minimum": 0,
- "maximum": 32
+ "type": "splitter"
+ },
+ {
+ "type":"boolean",
+ "key": "renderDepthOfField",
+ "label": "Depth of Field"
+ },
+ {
+ "type": "splitter"
},
{
"type": "boolean",
@@ -224,11 +227,145 @@
"key": "twoSidedLighting",
"label": "Two Sided Lighting"
},
+ {
+ "type": "splitter"
+ },
+ {
+ "type": "boolean",
+ "key": "lineAAEnable",
+ "label": "Enable Anti-Aliasing"
+ },
+ {
+ "type": "number",
+ "key": "multiSample",
+ "label": "Anti Aliasing Samples",
+ "decimal": 0,
+ "minimum": 0,
+ "maximum": 32
+ },
+ {
+ "type": "splitter"
+ },
{
"type": "boolean",
"key": "ssaoEnable",
"label": "Screen Space Ambient Occlusion"
},
+ {
+ "type": "number",
+ "key": "ssaoAmount",
+ "label": "SSAO Amount"
+ },
+ {
+ "type": "number",
+ "key": "ssaoRadius",
+ "label": "SSAO Radius"
+ },
+ {
+ "type": "number",
+ "key": "ssaoFilterRadius",
+ "label": "SSAO Filter Radius",
+ "decimal": 0,
+ "minimum": 1,
+ "maximum": 32
+ },
+ {
+ "type": "number",
+ "key": "ssaoSamples",
+ "label": "SSAO Samples",
+ "decimal": 0,
+ "minimum": 8,
+ "maximum": 32
+ },
+ {
+ "type": "splitter"
+ },
+ {
+ "type": "boolean",
+ "key": "fogging",
+ "label": "Enable Hardware Fog"
+ },
+ {
+ "type": "enum",
+ "key": "hwFogFalloff",
+ "label": "Hardware Falloff",
+ "enum_items": [
+ { "0": "Linear"},
+ { "1": "Exponential"},
+ { "2": "Exponential Squared"}
+ ]
+ },
+ {
+ "type": "number",
+ "key": "hwFogDensity",
+ "label": "Fog Density",
+ "decimal": 2,
+ "minimum": 0,
+ "maximum": 1
+ },
+ {
+ "type": "number",
+ "key": "hwFogStart",
+ "label": "Fog Start"
+ },
+ {
+ "type": "number",
+ "key": "hwFogEnd",
+ "label": "Fog End"
+ },
+ {
+ "type": "number",
+ "key": "hwFogAlpha",
+ "label": "Fog Alpha"
+ },
+ {
+ "type": "number",
+ "key": "hwFogColorR",
+ "label": "Fog Color R",
+ "decimal": 2,
+ "minimum": 0,
+ "maximum": 1
+ },
+ {
+ "type": "number",
+ "key": "hwFogColorG",
+ "label": "Fog Color G",
+ "decimal": 2,
+ "minimum": 0,
+ "maximum": 1
+ },
+ {
+ "type": "number",
+ "key": "hwFogColorB",
+ "label": "Fog Color B",
+ "decimal": 2,
+ "minimum": 0,
+ "maximum": 1
+ },
+ {
+ "type": "splitter"
+ },
+ {
+ "type": "boolean",
+ "key": "motionBlurEnable",
+ "label": "Enable Motion Blur"
+ },
+ {
+ "type": "number",
+ "key": "motionBlurSampleCount",
+ "label": "Motion Blur Sample Count",
+ "decimal": 0,
+ "minimum": 8,
+ "maximum": 32
+ },
+ {
+ "type": "number",
+ "key": "motionBlurShutterOpenFraction",
+ "label": "Shutter Open Fraction",
+ "decimal": 3,
+ "minimum": 0.01,
+ "maximum": 32
+ },
{
"type": "splitter"
},
diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_load.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_load.json
index 5bd8337e4c..805424c632 100644
--- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_load.json
+++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_load.json
@@ -11,10 +11,52 @@
{
"key": "LoadImage",
"label": "Image Loader"
+ }
+ ]
+ },
+ {
+ "type": "dict",
+ "collapsible": true,
+ "key": "LoadClip",
+ "label": "Clip Loader",
+ "checkbox_key": "enabled",
+ "children": [
+ {
+ "type": "boolean",
+ "key": "enabled",
+ "label": "Enabled"
},
{
- "key": "LoadClip",
- "label": "Clip Loader"
+ "type": "list",
+ "key": "_representations",
+ "label": "Representations",
+ "object_type": "text"
+ },
+ {
+ "type": "text",
+ "key": "node_name_template",
+ "label": "Node name template"
+ },
+ {
+ "type": "splitter"
+ },
+ {
+ "type": "dict",
+ "collapsible": false,
+ "key": "options_defaults",
+ "label": "Loader option defaults",
+ "children": [
+ {
+ "type": "boolean",
+ "key": "start_at_workfile",
+ "label": "Start at worfile beggining"
+ },
+ {
+ "type": "boolean",
+ "key": "add_retime",
+ "label": "Add retime"
+ }
+ ]
}
]
}
diff --git a/openpype/tools/publisher/control.py b/openpype/tools/publisher/control.py
index 915fb7f32e..f692bb4000 100644
--- a/openpype/tools/publisher/control.py
+++ b/openpype/tools/publisher/control.py
@@ -154,15 +154,20 @@ class PublishReport:
self._all_instances_by_id = {}
self._current_context = None
- def reset(self, context, publish_discover_result=None):
+ def reset(self, context, create_context):
"""Reset report and clear all data."""
- self._publish_discover_result = publish_discover_result
+
+ self._publish_discover_result = create_context.publish_discover_result
self._plugin_data = []
self._plugin_data_with_plugin = []
self._current_plugin_data = {}
self._all_instances_by_id = {}
self._current_context = context
+ for plugin in create_context.publish_plugins_mismatch_targets:
+ plugin_data = self._add_plugin_data_item(plugin)
+ plugin_data["skipped"] = True
+
def add_plugin_iter(self, plugin, context):
"""Add report about single iteration of plugin."""
for instance in context:
@@ -205,6 +210,7 @@ class PublishReport:
"name": plugin.__name__,
"label": label,
"order": plugin.order,
+ "targets": list(plugin.targets),
"instances_data": [],
"actions_data": [],
"skipped": False,
@@ -777,10 +783,7 @@ class PublisherController:
# - pop the key after first collector using it would be safest option?
self._publish_context.data["create_context"] = self.create_context
- self._publish_report.reset(
- self._publish_context,
- self.create_context.publish_discover_result
- )
+ self._publish_report.reset(self._publish_context, self.create_context)
self._publish_validation_errors = []
self._publish_current_plugin_validation_errors = None
self._publish_error = None
diff --git a/openpype/tools/publisher/publish_report_viewer/report_items.py b/openpype/tools/publisher/publish_report_viewer/report_items.py
index b47d14da25..8a01569723 100644
--- a/openpype/tools/publisher/publish_report_viewer/report_items.py
+++ b/openpype/tools/publisher/publish_report_viewer/report_items.py
@@ -83,10 +83,8 @@ class PublishReport:
logs = []
plugins_items_by_id = {}
- plugins_id_order = []
for plugin_data in data["plugins_data"]:
item = PluginItem(plugin_data)
- plugins_id_order.append(item.id)
plugins_items_by_id[item.id] = item
for instance_data_item in plugin_data["instances_data"]:
instance_id = instance_data_item["id"]
@@ -95,6 +93,14 @@ class PublishReport:
copy.deepcopy(log_item_data), item.id, instance_id
)
logs.append(log_item)
+ sorted_plugins = sorted(
+ plugins_items_by_id.values(),
+ key=lambda item: item.order
+ )
+ plugins_id_order = [
+ plugin_item.id
+ for plugin_item in sorted_plugins
+ ]
logs_by_instance_id = collections.defaultdict(list)
for log_item in logs:
diff --git a/openpype/tools/settings/settings/categories.py b/openpype/tools/settings/settings/categories.py
index 764f42f1a3..f42027d9e2 100644
--- a/openpype/tools/settings/settings/categories.py
+++ b/openpype/tools/settings/settings/categories.py
@@ -854,6 +854,9 @@ class ProjectWidget(SettingsCategoryWidget):
project_list_widget.version_change_requested.connect(
self._on_source_version_change
)
+ project_list_widget.extract_to_file_requested.connect(
+ self._on_extract_to_file
+ )
self.project_list_widget = project_list_widget
diff --git a/openpype/tools/settings/settings/widgets.py b/openpype/tools/settings/settings/widgets.py
index 45c21d5685..88d923c16a 100644
--- a/openpype/tools/settings/settings/widgets.py
+++ b/openpype/tools/settings/settings/widgets.py
@@ -1008,6 +1008,7 @@ class ProjectSortFilterProxy(QtCore.QSortFilterProxyModel):
class ProjectListWidget(QtWidgets.QWidget):
project_changed = QtCore.Signal()
version_change_requested = QtCore.Signal(str)
+ extract_to_file_requested = QtCore.Signal()
def __init__(self, parent, only_active=False):
self._parent = parent
@@ -1099,7 +1100,12 @@ class ProjectListWidget(QtWidgets.QWidget):
self.version_change_requested
)
submenu.addAction(action)
+
+ extract_action = QtWidgets.QAction("Extract to file", menu)
+ extract_action.triggered.connect(self.extract_to_file_requested)
+
menu.addMenu(submenu)
+ menu.addAction(extract_action)
menu.exec_(QtGui.QCursor.pos())
def on_item_clicked(self, new_index):
diff --git a/openpype/tools/traypublisher/window.py b/openpype/tools/traypublisher/window.py
index 5934c4aa8a..cc33287091 100644
--- a/openpype/tools/traypublisher/window.py
+++ b/openpype/tools/traypublisher/window.py
@@ -12,9 +12,7 @@ from openpype.pipeline import (
install_host,
AvalonMongoDB,
)
-from openpype.hosts.traypublisher import (
- api as traypublisher
-)
+from openpype.hosts.traypublisher.api import TrayPublisherHost
from openpype.tools.publisher import PublisherWindow
from openpype.tools.utils.constants import PROJECT_NAME_ROLE
from openpype.tools.utils.models import (
@@ -111,9 +109,13 @@ class StandaloneOverlayWidget(QtWidgets.QFrame):
if project_name:
self._set_project(project_name)
+ @property
+ def host(self):
+ return self._publisher_window.controller.host
+
def _set_project(self, project_name):
self._project_name = project_name
- traypublisher.set_project_name(project_name)
+ self.host.set_project_name(project_name)
self.setVisible(False)
self.project_selected.emit(project_name)
@@ -190,7 +192,8 @@ class TrayPublishWindow(PublisherWindow):
def main():
- install_host(traypublisher)
+ host = TrayPublisherHost()
+ install_host(host)
app = QtWidgets.QApplication([])
window = TrayPublishWindow()
window.show()
diff --git a/openpype/vendor/python/common/capture.py b/openpype/vendor/python/common/capture.py
index 6b4c40a6e8..4d9e1da3e4 100644
--- a/openpype/vendor/python/common/capture.py
+++ b/openpype/vendor/python/common/capture.py
@@ -380,7 +380,8 @@ Viewport2Options = {
"transparencyAlgorithm": 1,
"transparencyQuality": 0.33,
"useMaximumHardwareLights": True,
- "vertexAnimationCache": 0
+ "vertexAnimationCache": 0,
+ "renderDepthOfField": 0
}
diff --git a/openpype/version.py b/openpype/version.py
index 3239b0e2a2..c7b0de0381 100644
--- a/openpype/version.py
+++ b/openpype/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
-__version__ = "3.12.1-nightly.4"
+__version__ = "3.12.1"
diff --git a/pyproject.toml b/pyproject.toml
index f5bd7cc946..4bdaaab4ed 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
-version = "3.12.1-nightly.4" # OpenPype
+version = "3.12.1" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team "]
license = "MIT License"