Merge branch 'develop' of https://github.com/pypeclub/OpenPype into feature/multiverse

This commit is contained in:
Bo Zhou 2022-03-23 14:09:40 +09:00
commit 0bf37ddffc
259 changed files with 4653 additions and 1939 deletions

View file

@ -1,13 +1,65 @@
# Changelog
## [3.9.0-nightly.9](https://github.com/pypeclub/OpenPype/tree/HEAD)
## [3.9.2-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.8.2...HEAD)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.9.1...HEAD)
**🚀 Enhancements**
- CI: change the version bump logic [\#2919](https://github.com/pypeclub/OpenPype/pull/2919)
- Deadline: Add headless argument [\#2916](https://github.com/pypeclub/OpenPype/pull/2916)
- Ftrack: Fill workfile in custom attribute [\#2906](https://github.com/pypeclub/OpenPype/pull/2906)
- Settings UI: Add simple tooltips for settings entities [\#2901](https://github.com/pypeclub/OpenPype/pull/2901)
**🐛 Bug fixes**
- Ftrack: Missing Ftrack id after editorial publish [\#2905](https://github.com/pypeclub/OpenPype/pull/2905)
- AfterEffects: Fix rendering for single frame in DL [\#2875](https://github.com/pypeclub/OpenPype/pull/2875)
**🔀 Refactored code**
- General: Move formatting and workfile functions [\#2914](https://github.com/pypeclub/OpenPype/pull/2914)
## [3.9.1](https://github.com/pypeclub/OpenPype/tree/3.9.1) (2022-03-18)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.1-nightly.3...3.9.1)
**🚀 Enhancements**
- General: Change how OPENPYPE\_DEBUG value is handled [\#2907](https://github.com/pypeclub/OpenPype/pull/2907)
- nuke: imageio adding ocio config version 1.2 [\#2897](https://github.com/pypeclub/OpenPype/pull/2897)
- Flame: support for comment with xml attribute overrides [\#2892](https://github.com/pypeclub/OpenPype/pull/2892)
- Nuke: ExtractReviewSlate can handle more codes and profiles [\#2879](https://github.com/pypeclub/OpenPype/pull/2879)
- Flame: sequence used for reference video [\#2869](https://github.com/pypeclub/OpenPype/pull/2869)
**🐛 Bug fixes**
- General: Fix use of Anatomy roots [\#2904](https://github.com/pypeclub/OpenPype/pull/2904)
- Fixing gap detection in extract review [\#2902](https://github.com/pypeclub/OpenPype/pull/2902)
- Pyblish Pype - ensure current state is correct when entering new group order [\#2899](https://github.com/pypeclub/OpenPype/pull/2899)
- SceneInventory: Fix import of load function [\#2894](https://github.com/pypeclub/OpenPype/pull/2894)
- Harmony - fixed creator issue [\#2891](https://github.com/pypeclub/OpenPype/pull/2891)
- General: Remove forgotten use of avalon Creator [\#2885](https://github.com/pypeclub/OpenPype/pull/2885)
- General: Avoid circular import [\#2884](https://github.com/pypeclub/OpenPype/pull/2884)
- Fixes for attaching loaded containers \(\#2837\) [\#2874](https://github.com/pypeclub/OpenPype/pull/2874)
**🔀 Refactored code**
- General: Reduce style usage to OpenPype repository [\#2889](https://github.com/pypeclub/OpenPype/pull/2889)
- General: Move loader logic from avalon to openpype [\#2886](https://github.com/pypeclub/OpenPype/pull/2886)
## [3.9.0](https://github.com/pypeclub/OpenPype/tree/3.9.0) (2022-03-14)
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.9.0-nightly.9...3.9.0)
**Deprecated:**
- AssetCreator: Remove the tool [\#2845](https://github.com/pypeclub/OpenPype/pull/2845)
### 📖 Documentation
- Documentation: Change Photoshop & AfterEffects plugin path [\#2878](https://github.com/pypeclub/OpenPype/pull/2878)
**🚀 Enhancements**
- General: Subset name filtering in ExtractReview outpus [\#2872](https://github.com/pypeclub/OpenPype/pull/2872)
@ -27,6 +79,7 @@
- General: Missing time function [\#2877](https://github.com/pypeclub/OpenPype/pull/2877)
- Deadline: Fix plugin name for tile assemble [\#2868](https://github.com/pypeclub/OpenPype/pull/2868)
- Nuke: gizmo precollect fix [\#2866](https://github.com/pypeclub/OpenPype/pull/2866)
- General: Fix hardlink for windows [\#2864](https://github.com/pypeclub/OpenPype/pull/2864)
- General: ffmpeg was crashing on slate merge [\#2860](https://github.com/pypeclub/OpenPype/pull/2860)
- WebPublisher: Video file was published with one too many frame [\#2858](https://github.com/pypeclub/OpenPype/pull/2858)
@ -35,11 +88,13 @@
- Nuke: slate resolution to input video resolution [\#2853](https://github.com/pypeclub/OpenPype/pull/2853)
- WebPublisher: Fix username stored in DB [\#2852](https://github.com/pypeclub/OpenPype/pull/2852)
- WebPublisher: Fix wrong number of frames for video file [\#2851](https://github.com/pypeclub/OpenPype/pull/2851)
- Nuke: Fix family test in validate\_write\_legacy to work with stillImage [\#2847](https://github.com/pypeclub/OpenPype/pull/2847)
- Nuke: fix multiple baking profile farm publishing [\#2842](https://github.com/pypeclub/OpenPype/pull/2842)
- Blender: Fixed parameters for FBX export of the camera [\#2840](https://github.com/pypeclub/OpenPype/pull/2840)
- Maya: Stop creation of reviews for Cryptomattes [\#2832](https://github.com/pypeclub/OpenPype/pull/2832)
- Deadline: Remove recreated event [\#2828](https://github.com/pypeclub/OpenPype/pull/2828)
- Deadline: Added missing events folder [\#2827](https://github.com/pypeclub/OpenPype/pull/2827)
- Maya: Deformer node ids validation plugin [\#2826](https://github.com/pypeclub/OpenPype/pull/2826)
- Settings: Missing document with OP versions may break start of OpenPype [\#2825](https://github.com/pypeclub/OpenPype/pull/2825)
- Deadline: more detailed temp file name for environment json [\#2824](https://github.com/pypeclub/OpenPype/pull/2824)
- General: Host name was formed from obsolete code [\#2821](https://github.com/pypeclub/OpenPype/pull/2821)
@ -57,12 +112,6 @@
- General: Move change context functions [\#2839](https://github.com/pypeclub/OpenPype/pull/2839)
- Tools: Don't use avalon tools code [\#2829](https://github.com/pypeclub/OpenPype/pull/2829)
- Move Unreal Implementation to OpenPype [\#2823](https://github.com/pypeclub/OpenPype/pull/2823)
- General: Extract template formatting from anatomy [\#2766](https://github.com/pypeclub/OpenPype/pull/2766)
**Merged pull requests:**
- Nuke: gizmo precollect fix [\#2866](https://github.com/pypeclub/OpenPype/pull/2866)
- Nuke: Fix family test in validate\_write\_legacy to work with stillImage [\#2847](https://github.com/pypeclub/OpenPype/pull/2847)
## [3.8.2](https://github.com/pypeclub/OpenPype/tree/3.8.2) (2022-02-07)

View file

@ -5,7 +5,6 @@ import platform
import functools
import logging
from openpype.pipeline import LegacyCreator
from .settings import get_project_settings
from .lib import (
Anatomy,
@ -76,6 +75,11 @@ def install():
"""Install Pype to Avalon."""
from pyblish.lib import MessageHandler
from openpype.modules import load_modules
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
register_inventory_action,
)
from avalon import pipeline
# Make sure modules are loaded
@ -91,7 +95,7 @@ def install():
log.info("Registering global plug-ins..")
pyblish.register_plugin_path(PUBLISH_PATH)
pyblish.register_discovery_filter(filter_pyblish_plugins)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
project_name = os.environ.get("AVALON_PROJECT")
@ -119,9 +123,9 @@ def install():
continue
pyblish.register_plugin_path(path)
avalon.register_plugin_path(avalon.Loader, path)
register_loader_plugin_path(path)
avalon.register_plugin_path(LegacyCreator, path)
avalon.register_plugin_path(avalon.InventoryAction, path)
register_inventory_action(path)
# apply monkey patched discover to original one
log.info("Patching discovery")
@ -139,10 +143,12 @@ def _on_task_change():
@import_wrapper
def uninstall():
"""Uninstall Pype from Avalon."""
from openpype.pipeline import deregister_loader_plugin_path
log.info("Deregistering global plug-ins..")
pyblish.deregister_plugin_path(PUBLISH_PATH)
pyblish.deregister_discovery_filter(filter_pyblish_plugins)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
log.info("Global plug-ins unregistred")
# restore original discover

View file

@ -101,7 +101,7 @@ def eventserver(debug,
on linux and window service).
"""
if debug:
os.environ['OPENPYPE_DEBUG'] = "3"
os.environ["OPENPYPE_DEBUG"] = "1"
PypeCommands().launch_eventservercli(
ftrack_url,
@ -128,7 +128,7 @@ def webpublisherwebserver(debug, executable, upload_dir, host=None, port=None):
Expect "pype.club" user created on Ftrack.
"""
if debug:
os.environ['OPENPYPE_DEBUG'] = "3"
os.environ["OPENPYPE_DEBUG"] = "1"
PypeCommands().launch_webpublisher_webservercli(
upload_dir=upload_dir,
@ -176,7 +176,7 @@ def publish(debug, paths, targets, gui):
More than one path is allowed.
"""
if debug:
os.environ['OPENPYPE_DEBUG'] = '3'
os.environ["OPENPYPE_DEBUG"] = "1"
PypeCommands.publish(list(paths), targets, gui)
@ -195,7 +195,7 @@ def remotepublishfromapp(debug, project, path, host, user=None, targets=None):
More than one path is allowed.
"""
if debug:
os.environ['OPENPYPE_DEBUG'] = '3'
os.environ["OPENPYPE_DEBUG"] = "1"
PypeCommands.remotepublishfromapp(
project, path, host, user, targets=targets
)
@ -215,7 +215,7 @@ def remotepublish(debug, project, path, user=None, targets=None):
More than one path is allowed.
"""
if debug:
os.environ['OPENPYPE_DEBUG'] = '3'
os.environ["OPENPYPE_DEBUG"] = "1"
PypeCommands.remotepublish(project, path, user, targets=targets)
@ -240,7 +240,7 @@ def texturecopy(debug, project, asset, path):
Nothing is written to database.
"""
if debug:
os.environ['OPENPYPE_DEBUG'] = '3'
os.environ["OPENPYPE_DEBUG"] = "1"
PypeCommands().texture_copy(project, asset, path)
@ -409,7 +409,7 @@ def syncserver(debug, active_site):
var OPENPYPE_LOCAL_ID set to 'active_site'.
"""
if debug:
os.environ['OPENPYPE_DEBUG'] = '3'
os.environ["OPENPYPE_DEBUG"] = "1"
PypeCommands().syncserver(active_site)

View file

@ -2,14 +2,20 @@ import os
import sys
from Qt import QtWidgets
from bson.objectid import ObjectId
import pyblish.api
import avalon.api
from avalon import io, pipeline
from avalon import io
from openpype import lib
from openpype.api import Logger
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
deregister_loader_plugin_path,
AVALON_CONTAINER_ID,
)
import openpype.hosts.aftereffects
from openpype.lib import register_event_callback
@ -25,7 +31,6 @@ PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
def check_inventory():
@ -38,7 +43,7 @@ def check_inventory():
representation = container['representation']
representation_doc = io.find_one(
{
"_id": io.ObjectId(representation),
"_id": ObjectId(representation),
"type": "representation"
},
projection={"parent": True}
@ -67,7 +72,7 @@ def install():
pyblish.api.register_host("aftereffects")
pyblish.api.register_plugin_path(PUBLISH_PATH)
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
log.info(PUBLISH_PATH)
@ -80,7 +85,7 @@ def install():
def uninstall():
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)
@ -145,7 +150,7 @@ def containerise(name,
"""
data = {
"schema": "openpype:container-2.0",
"id": pipeline.AVALON_CONTAINER_ID,
"id": AVALON_CONTAINER_ID,
"name": name,
"namespace": namespace,
"loader": str(loader),

View file

@ -1,9 +1,8 @@
import avalon.api
from openpype.pipeline import LoaderPlugin
from .launch_logic import get_stub
class AfterEffectsLoader(avalon.api.Loader):
class AfterEffectsLoader(LoaderPlugin):
@staticmethod
def get_stub():
return get_stub()

View file

@ -1,8 +1,8 @@
"""Host API required Work Files tool"""
import os
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
from .launch_logic import get_stub
from avalon import api
def _active_document():
@ -14,7 +14,7 @@ def _active_document():
def file_extensions():
return api.HOST_WORKFILE_EXTENSIONS["aftereffects"]
return HOST_WORKFILE_EXTENSIONS["aftereffects"]
def has_unsaved_changes():

View file

@ -1,11 +1,10 @@
import re
import avalon.api
from openpype.lib import (
get_background_layers,
get_unique_layer_name
)
from openpype.pipeline import get_representation_path
from openpype.hosts.aftereffects.api import (
AfterEffectsLoader,
containerise
@ -78,7 +77,7 @@ class BackgroundLoader(AfterEffectsLoader):
else: # switching version - keep same name
comp_name = container["namespace"]
path = avalon.api.get_representation_path(representation)
path = get_representation_path(representation)
layers = get_background_layers(path)
comp = stub.reload_background(container["members"][1],

View file

@ -1,8 +1,8 @@
import re
import avalon.api
from openpype import lib
from openpype.pipeline import get_representation_path
from openpype.hosts.aftereffects.api import (
AfterEffectsLoader,
containerise
@ -92,7 +92,7 @@ class FileLoader(AfterEffectsLoader):
"{}_{}".format(context["asset"], context["subset"]))
else: # switching version - keep same name
layer_name = container["namespace"]
path = avalon.api.get_representation_path(representation)
path = get_representation_path(representation)
# with aftereffects.maintained_selection(): # TODO
stub.replace_item(layer.id, path, stub.LOADED_ICON + layer_name)
stub.imprint(

View file

@ -328,7 +328,6 @@ class LaunchWorkFiles(LaunchQtApp):
result = super().execute(context)
self._window.set_context({
"asset": avalon.api.Session["AVALON_ASSET"],
"silo": avalon.api.Session["AVALON_SILO"],
"task": avalon.api.Session["AVALON_TASK"]
})
return result

View file

@ -12,9 +12,13 @@ from . import ops
import pyblish.api
import avalon.api
from avalon import io, schema
from avalon.pipeline import AVALON_CONTAINER_ID
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
deregister_loader_plugin_path,
AVALON_CONTAINER_ID,
)
from openpype.api import Logger
from openpype.lib import (
register_event_callback,
@ -27,7 +31,6 @@ PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
ORIGINAL_EXCEPTHOOK = sys.excepthook
@ -50,7 +53,7 @@ def install():
pyblish.api.register_host("blender")
pyblish.api.register_plugin_path(str(PUBLISH_PATH))
avalon.api.register_plugin_path(avalon.api.Loader, str(LOAD_PATH))
register_loader_plugin_path(str(LOAD_PATH))
avalon.api.register_plugin_path(LegacyCreator, str(CREATE_PATH))
lib.append_user_scripts()
@ -72,7 +75,7 @@ def uninstall():
pyblish.api.deregister_host("blender")
pyblish.api.deregister_plugin_path(str(PUBLISH_PATH))
avalon.api.deregister_plugin_path(avalon.api.Loader, str(LOAD_PATH))
deregister_loader_plugin_path(str(LOAD_PATH))
avalon.api.deregister_plugin_path(LegacyCreator, str(CREATE_PATH))
if not IS_HEADLESS:

View file

@ -5,8 +5,10 @@ from typing import Dict, List, Optional
import bpy
import avalon.api
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
LoaderPlugin,
)
from .pipeline import AVALON_CONTAINERS
from .ops import (
MainThreadItem,
@ -145,13 +147,13 @@ class Creator(LegacyCreator):
return collection
class Loader(avalon.api.Loader):
class Loader(LoaderPlugin):
"""Base class for Loader plug-ins."""
hosts = ["blender"]
class AssetLoader(avalon.api.Loader):
class AssetLoader(LoaderPlugin):
"""A basic AssetLoader for Blender
This will implement the basic logic for linking/appending assets

View file

@ -4,7 +4,8 @@ from pathlib import Path
from typing import List, Optional
import bpy
from avalon import api
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
class OpenFileCacher:
@ -77,7 +78,7 @@ def has_unsaved_changes() -> bool:
def file_extensions() -> List[str]:
"""Return the supported file extensions for Blender scene files."""
return api.HOST_WORKFILE_EXTENSIONS["blender"]
return HOST_WORKFILE_EXTENSIONS["blender"]
def work_root(session: dict) -> str:

View file

@ -6,11 +6,14 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from openpype.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
from openpype.hosts.blender.api import plugin, lib
@ -178,7 +181,7 @@ class CacheModelLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -5,9 +5,13 @@ from pathlib import Path
from pprint import pformat
from typing import Dict, List, Optional
from avalon import api, blender
import bpy
from openpype.pipeline import get_representation_path
import openpype.hosts.blender.api.plugin
from openpype.hosts.blender.api.pipeline import (
containerise_existing,
AVALON_PROPERTY,
)
logger = logging.getLogger("openpype").getChild("blender").getChild("load_action")
@ -49,7 +53,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
container = bpy.data.collections.new(lib_container)
container.name = container_name
blender.pipeline.containerise_existing(
containerise_existing(
container,
name,
namespace,
@ -57,8 +61,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
self.__class__.__name__,
)
container_metadata = container.get(
blender.pipeline.AVALON_PROPERTY)
container_metadata = container.get(AVALON_PROPERTY)
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
@ -90,16 +93,16 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
anim_data.action.make_local()
if not obj.get(blender.pipeline.AVALON_PROPERTY):
if not obj.get(AVALON_PROPERTY):
obj[blender.pipeline.AVALON_PROPERTY] = dict()
obj[AVALON_PROPERTY] = dict()
avalon_info = obj[blender.pipeline.AVALON_PROPERTY]
avalon_info = obj[AVALON_PROPERTY]
avalon_info.update({"container_name": container_name})
objects_list.append(obj)
animation_container.pop(blender.pipeline.AVALON_PROPERTY)
animation_container.pop(AVALON_PROPERTY)
# Save the list of objects in the metadata container
container_metadata["objects"] = objects_list
@ -128,7 +131,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
container["objectName"]
)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
logger.info(
@ -153,8 +156,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
f"Unsupported file: {libpath}"
)
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
collection_metadata = collection.get(AVALON_PROPERTY)
collection_libpath = collection_metadata["libpath"]
normalized_collection_libpath = (
@ -225,16 +227,16 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
strip.action = anim_data.action
strip.action_frame_end = anim_data.action.frame_range[1]
if not obj.get(blender.pipeline.AVALON_PROPERTY):
if not obj.get(AVALON_PROPERTY):
obj[blender.pipeline.AVALON_PROPERTY] = dict()
obj[AVALON_PROPERTY] = dict()
avalon_info = obj[blender.pipeline.AVALON_PROPERTY]
avalon_info = obj[AVALON_PROPERTY]
avalon_info.update({"container_name": collection.name})
objects_list.append(obj)
anim_container.pop(blender.pipeline.AVALON_PROPERTY)
anim_container.pop(AVALON_PROPERTY)
# Save the list of objects in the metadata container
collection_metadata["objects"] = objects_list
@ -266,8 +268,7 @@ class BlendActionLoader(openpype.hosts.blender.api.plugin.AssetLoader):
"Nested collections are not supported."
)
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
collection_metadata = collection.get(AVALON_PROPERTY)
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]

View file

@ -6,12 +6,14 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from openpype.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api import plugin
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
@ -102,7 +104,7 @@ class AudioLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
self.log.info(
"Container: %s\nRepresentation: %s",

View file

@ -7,12 +7,14 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from openpype.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api import plugin
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
logger = logging.getLogger("openpype").getChild(
@ -155,7 +157,7 @@ class BlendCameraLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -6,12 +6,14 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from openpype.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api import plugin, lib
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
@ -143,7 +145,7 @@ class FbxCameraLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -6,12 +6,14 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from openpype.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api import plugin, lib
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
@ -187,7 +189,7 @@ class FbxModelLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -6,14 +6,16 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from openpype import lib
from openpype.pipeline import legacy_create
from openpype.pipeline import (
legacy_create,
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api import plugin
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
@ -309,7 +311,7 @@ class BlendLayoutLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -7,12 +7,18 @@ from typing import Dict, Optional
import bpy
from avalon import api
from openpype.pipeline import (
discover_loader_plugins,
remove_container,
load_container,
get_representation_path,
loaders_from_representation,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api.pipeline import (
AVALON_INSTANCES,
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
from openpype.hosts.blender.api import plugin
@ -33,7 +39,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
objects = list(asset_group.children)
for obj in objects:
api.remove(obj.get(AVALON_PROPERTY))
remove_container(obj.get(AVALON_PROPERTY))
def _remove_animation_instances(self, asset_group):
instances = bpy.data.collections.get(AVALON_INSTANCES)
@ -66,13 +72,13 @@ class JsonLayoutLoader(plugin.AssetLoader):
with open(libpath, "r") as fp:
data = json.load(fp)
all_loaders = api.discover(api.Loader)
all_loaders = discover_loader_plugins()
for element in data:
reference = element.get('reference')
family = element.get('family')
loaders = api.loaders_from_representation(all_loaders, reference)
loaders = loaders_from_representation(all_loaders, reference)
loader = self._get_loader(loaders, family)
if not loader:
@ -102,7 +108,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
# at this time it will not return anything. The assets will be
# loaded in the next Blender cycle, so we use the options to
# set the transform, parent and assign the action, if there is one.
api.load(
load_container(
loader,
reference,
namespace=instance_name,
@ -188,7 +194,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -8,7 +8,7 @@ import os
import json
import bpy
from avalon import api
from openpype.pipeline import get_representation_path
from openpype.hosts.blender.api import plugin
from openpype.hosts.blender.api.pipeline import (
containerise_existing,
@ -140,7 +140,7 @@ class BlendLookLoader(plugin.AssetLoader):
def update(self, container: Dict, representation: Dict):
collection = bpy.data.collections.get(container["objectName"])
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -6,12 +6,14 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from openpype.pipeline import (
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api import plugin
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
@ -195,7 +197,7 @@ class BlendModelLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -6,15 +6,19 @@ from typing import Dict, List, Optional
import bpy
from avalon import api
from avalon.blender import lib as avalon_lib
from openpype import lib
from openpype.pipeline import legacy_create
from openpype.hosts.blender.api import plugin
from openpype.pipeline import (
legacy_create,
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.blender.api import (
plugin,
get_selection,
)
from openpype.hosts.blender.api.pipeline import (
AVALON_CONTAINERS,
AVALON_PROPERTY,
AVALON_CONTAINER_ID
)
@ -263,7 +267,7 @@ class BlendRigLoader(plugin.AssetLoader):
if anim_file:
bpy.ops.import_scene.fbx(filepath=anim_file, anim_offset=0.0)
imported = avalon_lib.get_selection()
imported = get_selection()
armature = [
o for o in asset_group.children if o.type == 'ARMATURE'][0]
@ -307,7 +311,7 @@ class BlendRigLoader(plugin.AssetLoader):
"""
object_name = container["objectName"]
asset_group = bpy.data.objects.get(object_name)
libpath = Path(api.get_representation_path(representation))
libpath = Path(get_representation_path(representation))
extension = libpath.suffix.lower()
self.log.info(

View file

@ -1,6 +1,8 @@
import os
import json
from bson.objectid import ObjectId
import bpy
import bpy_extras
import bpy_extras.anim_utils
@ -140,7 +142,7 @@ class ExtractLayout(openpype.api.Extractor):
blend = io.find_one(
{
"type": "representation",
"parent": io.ObjectId(parent),
"parent": ObjectId(parent),
"name": "blend"
},
projection={"_id": True})
@ -151,7 +153,7 @@ class ExtractLayout(openpype.api.Extractor):
fbx = io.find_one(
{
"type": "representation",
"parent": io.ObjectId(parent),
"parent": ObjectId(parent),
"name": "fbx"
},
projection={"_id": True})
@ -162,7 +164,7 @@ class ExtractLayout(openpype.api.Extractor):
abc = io.find_one(
{
"type": "representation",
"parent": io.ObjectId(parent),
"parent": ObjectId(parent),
"name": "abc"
},
projection={"_id": True})

View file

@ -68,7 +68,8 @@ from .workio import (
)
from .render_utils import (
export_clip,
get_preset_path_by_xml_name
get_preset_path_by_xml_name,
modify_preset_file
)
__all__ = [
@ -140,5 +141,6 @@ __all__ = [
# render utils
"export_clip",
"get_preset_path_by_xml_name"
"get_preset_path_by_xml_name",
"modify_preset_file"
]

View file

@ -4,10 +4,15 @@ Basic avalon integration
import os
import contextlib
from avalon import api as avalon
from avalon.pipeline import AVALON_CONTAINER_ID
from pyblish import api as pyblish
from openpype.api import Logger
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
deregister_loader_plugin_path,
AVALON_CONTAINER_ID,
)
from .lib import (
set_segment_data_marker,
set_publish_attribute,
@ -22,7 +27,6 @@ PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
AVALON_CONTAINERS = "AVALON_CONTAINERS"
@ -30,12 +34,10 @@ log = Logger.get_logger(__name__)
def install():
pyblish.register_host("flame")
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.register_plugin_path(LegacyCreator, CREATE_PATH)
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
log.info("OpenPype Flame plug-ins registred ...")
# register callback for switching publishable
@ -43,14 +45,14 @@ def install():
log.info("OpenPype Flame host installed ...")
def uninstall():
pyblish.deregister_host("flame")
log.info("Deregistering Flame plug-ins..")
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH)
avalon.deregister_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
# register callback for switching publishable
pyblish.deregister_callback("instanceToggled", on_pyblish_instance_toggled)

View file

@ -7,9 +7,11 @@ import six
import qargparse
from Qt import QtWidgets, QtCore
import openpype.api as openpype
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
LoaderPlugin,
)
from openpype import style
import avalon.api as avalon
from . import (
lib as flib,
pipeline as fpipeline,
@ -660,7 +662,7 @@ class PublishableClip:
# Publishing plugin functions
# Loader plugin functions
class ClipLoader(avalon.Loader):
class ClipLoader(LoaderPlugin):
"""A basic clip loader for Flame
This will implement the basic behavior for a loader to inherit from that

View file

@ -1,4 +1,5 @@
import os
from xml.etree import ElementTree as ET
def export_clip(export_path, clip, preset_path, **kwargs):
@ -123,3 +124,29 @@ def get_preset_path_by_xml_name(xml_preset_name):
# if nothing found then return False
return False
def modify_preset_file(xml_path, staging_dir, data):
"""Modify xml preset with input data
Args:
xml_path (str ): path for input xml preset
staging_dir (str): staging dir path
data (dict): data where key is xmlTag and value as string
Returns:
str: _description_
"""
# create temp path
dirname, basename = os.path.split(xml_path)
temp_path = os.path.join(staging_dir, basename)
# change xml following data keys
with open(xml_path, "r") as datafile:
tree = ET.parse(datafile)
for key, value in data.items():
for element in tree.findall(".//{}".format(key)):
element.text = str(value)
tree.write(temp_path)
return temp_path

View file

@ -420,13 +420,20 @@ class WireTapCom(object):
RuntimeError: Not able to set colorspace policy
"""
color_policy = color_policy or "Legacy"
# check if the colour policy in custom dir
if not os.path.exists(color_policy):
color_policy = "/syncolor/policies/Autodesk/{}".format(
color_policy)
# create arguments
project_colorspace_cmd = [
os.path.join(
self.wiretap_tools_dir,
"wiretap_duplicate_node"
),
"-s",
"/syncolor/policies/Autodesk/{}".format(color_policy),
color_policy,
"-n",
"/projects/{}/syncolor".format(project_name)
]

View file

@ -73,7 +73,7 @@ class FlamePrelaunch(PreLaunchHook):
"FrameWidth": int(width),
"FrameHeight": int(height),
"AspectRatio": float((width / height) * _db_p_data["pixelAspect"]),
"FrameRate": "{} fps".format(fps),
"FrameRate": self._get_flame_fps(fps),
"FrameDepth": str(imageio_flame["project"]["frameDepth"]),
"FieldDominance": str(imageio_flame["project"]["fieldDominance"])
}
@ -101,6 +101,28 @@ class FlamePrelaunch(PreLaunchHook):
self.launch_context.launch_args.extend(app_arguments)
def _get_flame_fps(self, fps_num):
fps_table = {
float(23.976): "23.976 fps",
int(25): "25 fps",
int(24): "24 fps",
float(29.97): "29.97 fps DF",
int(30): "30 fps",
int(50): "50 fps",
float(59.94): "59.94 fps DF",
int(60): "60 fps"
}
match_key = min(fps_table.keys(), key=lambda x: abs(x - fps_num))
try:
return fps_table[match_key]
except KeyError as msg:
raise KeyError((
"Missing FPS key in conversion table. "
"Following keys are available: {}".format(fps_table.keys())
)) from msg
def _add_pythonpath(self):
pythonpath = self.launch_context.env.get("PYTHONPATH")

View file

@ -172,7 +172,7 @@ class LoadClip(opfapi.ClipLoader):
# version_name = version.get("name", None)
# colorspace = version_data.get("colorspace", None)
# object_name = "{}_{}".format(name, namespace)
# file = api.get_representation_path(representation).replace("\\", "/")
# file = get_representation_path(representation).replace("\\", "/")
# clip = track_item.source()
# # reconnect media to new path

View file

@ -1,3 +1,4 @@
import re
import pyblish
import openpype
import openpype.hosts.flame.api as opfapi
@ -6,6 +7,10 @@ from openpype.hosts.flame.otio import flame_export
# # developer reload modules
from pprint import pformat
# constatns
NUM_PATERN = re.compile(r"([0-9\.]+)")
TXT_PATERN = re.compile(r"([a-zA-Z]+)")
class CollectTimelineInstances(pyblish.api.ContextPlugin):
"""Collect all Timeline segment selection."""
@ -16,6 +21,16 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
audio_track_items = []
# TODO: add to settings
# settings
xml_preset_attrs_from_comments = {
"width": "number",
"height": "number",
"pixelRatio": "float",
"resizeType": "string",
"resizeFilter": "string"
}
def process(self, context):
project = context.data["flameProject"]
sequence = context.data["flameSequence"]
@ -26,6 +41,10 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
# process all sellected
with opfapi.maintained_segment_selection(sequence) as segments:
for segment in segments:
comment_attributes = self._get_comment_attributes(segment)
self.log.debug("_ comment_attributes: {}".format(
pformat(comment_attributes)))
clip_data = opfapi.get_segment_attributes(segment)
clip_name = clip_data["segment_name"]
self.log.debug("clip_name: {}".format(clip_name))
@ -101,6 +120,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
# add resolution
self._get_resolution_to_data(inst_data, context)
# add comment attributes if any
inst_data.update(comment_attributes)
# create instance
instance = context.create_instance(**inst_data)
@ -126,6 +148,94 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
if marker_data.get("reviewTrack") is not None:
instance.data["reviewAudio"] = True
def _get_comment_attributes(self, segment):
comment = segment.comment.get_value()
# try to find attributes
attributes = {
"xml_overrides": {
"pixelRatio": 1.00}
}
# search for `:`
for split in self._split_comments(comment):
# make sure we ignore if not `:` in key
if ":" not in split:
continue
self._get_xml_preset_attrs(
attributes, split)
# add xml overides resolution to instance data
xml_overrides = attributes["xml_overrides"]
if xml_overrides.get("width"):
attributes.update({
"resolutionWidth": xml_overrides["width"],
"resolutionHeight": xml_overrides["height"],
"pixelAspect": xml_overrides["pixelRatio"]
})
return attributes
def _get_xml_preset_attrs(self, attributes, split):
# split to key and value
key, value = split.split(":")
for a_name, a_type in self.xml_preset_attrs_from_comments.items():
# exclude all not related attributes
if a_name.lower() not in key.lower():
continue
# get pattern defined by type
pattern = TXT_PATERN
if a_type in ("number" , "float"):
pattern = NUM_PATERN
res_goup = pattern.findall(value)
# raise if nothing is found as it is not correctly defined
if not res_goup:
raise ValueError((
"Value for `{}` attribute is not "
"set correctly: `{}`").format(a_name, split))
if "string" in a_type:
_value = res_goup[0]
if "float" in a_type:
_value = float(res_goup[0])
if "number" in a_type:
_value = int(res_goup[0])
attributes["xml_overrides"][a_name] = _value
# condition for resolution in key
if "resolution" in key.lower():
res_goup = NUM_PATERN.findall(value)
# check if axpect was also defined
# 1920x1080x1.5
aspect = res_goup[2] if len(res_goup) > 2 else 1
width = int(res_goup[0])
height = int(res_goup[1])
pixel_ratio = float(aspect)
attributes["xml_overrides"].update({
"width": width,
"height": height,
"pixelRatio": pixel_ratio
})
def _split_comments(self, comment_string):
# first split comment by comma
split_comments = []
if "," in comment_string:
split_comments.extend(comment_string.split(","))
elif ";" in comment_string:
split_comments.extend(comment_string.split(";"))
else:
split_comments.append(comment_string)
return split_comments
def _get_head_tail(self, clip_data, first_frame):
# calculate head and tail with forward compatibility
head = clip_data.get("segment_head")

View file

@ -1,6 +1,7 @@
import os
from pprint import pformat
from copy import deepcopy
import pyblish.api
import openpype.api
from openpype.hosts.flame import api as opfapi
@ -22,6 +23,8 @@ class ExtractSubsetResources(openpype.api.Extractor):
"ext": "jpg",
"xml_preset_file": "Jpeg (8-bit).xml",
"xml_preset_dir": "",
"export_type": "File Sequence",
"ignore_comment_attrs": True,
"colorspace_out": "Output - sRGB",
"representation_add_range": False,
"representation_tags": ["thumbnail"]
@ -30,6 +33,8 @@ class ExtractSubsetResources(openpype.api.Extractor):
"ext": "mov",
"xml_preset_file": "Apple iPad (1920x1080).xml",
"xml_preset_dir": "",
"export_type": "Movie",
"ignore_comment_attrs": True,
"colorspace_out": "Output - Rec.709",
"representation_add_range": True,
"representation_tags": [
@ -54,21 +59,35 @@ class ExtractSubsetResources(openpype.api.Extractor):
):
instance.data["representations"] = []
frame_start = instance.data["frameStart"]
handle_start = instance.data["handleStart"]
frame_start_handle = frame_start - handle_start
source_first_frame = instance.data["sourceFirstFrame"]
source_start_handles = instance.data["sourceStartH"]
source_end_handles = instance.data["sourceEndH"]
source_duration_handles = (
source_end_handles - source_start_handles) + 1
# flame objects
segment = instance.data["item"]
sequence_clip = instance.context.data["flameSequence"]
clip_data = instance.data["flameSourceClip"]
clip = clip_data["PyClip"]
in_mark = (source_start_handles - source_first_frame) + 1
out_mark = in_mark + source_duration_handles
# segment's parent track name
s_track_name = segment.parent.name.get_value()
# get configured workfile frame start/end (handles excluded)
frame_start = instance.data["frameStart"]
# get media source first frame
source_first_frame = instance.data["sourceFirstFrame"]
# get timeline in/out of segment
clip_in = instance.data["clipIn"]
clip_out = instance.data["clipOut"]
# get handles value - take only the max from both
handle_start = instance.data["handleStart"]
handle_end = instance.data["handleStart"]
handles = max(handle_start, handle_end)
# get media source range with handles
source_end_handles = instance.data["sourceEndH"]
source_start_handles = instance.data["sourceStartH"]
source_end_handles = instance.data["sourceEndH"]
# create staging dir path
staging_dir = self.staging_dir(instance)
# add default preset type for thumbnail and reviewable video
@ -77,15 +96,61 @@ class ExtractSubsetResources(openpype.api.Extractor):
export_presets = deepcopy(self.default_presets)
export_presets.update(self.export_presets_mapping)
# with maintained duplication loop all presets
with opfapi.maintained_object_duplication(clip) as duplclip:
# loop all preset names and
for unique_name, preset_config in export_presets.items():
# loop all preset names and
for unique_name, preset_config in export_presets.items():
modify_xml_data = {}
# get all presets attributes
preset_file = preset_config["xml_preset_file"]
preset_dir = preset_config["xml_preset_dir"]
export_type = preset_config["export_type"]
repre_tags = preset_config["representation_tags"]
ignore_comment_attrs = preset_config["ignore_comment_attrs"]
color_out = preset_config["colorspace_out"]
# get frame range with handles for representation range
frame_start_handle = frame_start - handle_start
source_duration_handles = (
source_end_handles - source_start_handles) + 1
# define in/out marks
in_mark = (source_start_handles - source_first_frame) + 1
out_mark = in_mark + source_duration_handles
# by default export source clips
exporting_clip = clip
if export_type == "Sequence Publish":
# change export clip to sequence
exporting_clip = sequence_clip
# change in/out marks to timeline in/out
in_mark = clip_in
out_mark = clip_out
# add xml tags modifications
modify_xml_data.update({
"exportHandles": True,
"nbHandles": handles,
"startFrame": frame_start
})
if not ignore_comment_attrs:
# add any xml overrides collected form segment.comment
modify_xml_data.update(instance.data["xml_overrides"])
self.log.debug("__ modify_xml_data: {}".format(pformat(
modify_xml_data
)))
# with maintained duplication loop all presets
with opfapi.maintained_object_duplication(
exporting_clip) as duplclip:
kwargs = {}
preset_file = preset_config["xml_preset_file"]
preset_dir = preset_config["xml_preset_dir"]
repre_tags = preset_config["representation_tags"]
color_out = preset_config["colorspace_out"]
if export_type == "Sequence Publish":
# only keep visible layer where instance segment is child
self.hide_other_tracks(duplclip, s_track_name)
# validate xml preset file is filled
if preset_file == "":
@ -108,10 +173,13 @@ class ExtractSubsetResources(openpype.api.Extractor):
)
# create preset path
preset_path = str(os.path.join(
preset_orig_xml_path = str(os.path.join(
preset_dir, preset_file
))
preset_path = opfapi.modify_preset_file(
preset_orig_xml_path, staging_dir, modify_xml_data)
# define kwargs based on preset type
if "thumbnail" in unique_name:
kwargs["thumb_frame_number"] = in_mark + (
@ -122,6 +190,7 @@ class ExtractSubsetResources(openpype.api.Extractor):
"out_mark": out_mark
})
# get and make export dir paths
export_dir_path = str(os.path.join(
staging_dir, unique_name
))
@ -132,6 +201,7 @@ class ExtractSubsetResources(openpype.api.Extractor):
export_dir_path, duplclip, preset_path, **kwargs)
extension = preset_config["ext"]
# create representation data
representation_data = {
"name": unique_name,
@ -159,7 +229,12 @@ class ExtractSubsetResources(openpype.api.Extractor):
# add files to represetation but add
# imagesequence as list
if (
"movie_file" in preset_path
# first check if path in files is not mov extension
[
f for f in files
if os.path.splitext(f)[-1] == ".mov"
]
# then try if thumbnail is not in unique name
or unique_name == "thumbnail"
):
representation_data["files"] = files.pop()
@ -246,3 +321,19 @@ class ExtractSubsetResources(openpype.api.Extractor):
)
return new_stage_dir, new_files_list
def hide_other_tracks(self, sequence_clip, track_name):
"""Helper method used only if sequence clip is used
Args:
sequence_clip (flame.Clip): sequence clip
track_name (str): track name
"""
# create otio tracks and clips
for ver in sequence_clip.versions:
for track in ver.tracks:
if len(track.segments) == 0 and track.hidden:
continue
if track.name.get_value() != track_name:
track.hidden = True

View file

@ -3,10 +3,11 @@ import sys
import re
import contextlib
from bson.objectid import ObjectId
from Qt import QtGui
import avalon.api
from avalon import io
from openpype.pipeline import switch_container
from .pipeline import get_current_comp, comp_lock_and_undo_chunk
self = sys.modules[__name__]
@ -92,7 +93,7 @@ def switch_item(container,
# Collect any of current asset, subset and representation if not provided
# so we can use the original name from those.
if any(not x for x in [asset_name, subset_name, representation_name]):
_id = io.ObjectId(container["representation"])
_id = ObjectId(container["representation"])
representation = io.find_one({"type": "representation", "_id": _id})
version, subset, asset, project = io.parenthood(representation)
@ -142,7 +143,7 @@ def switch_item(container,
assert representation, ("Could not find representation in the database "
"with the name '%s'" % representation_name)
avalon.api.switch(container, representation)
switch_container(container, representation)
return representation

View file

@ -8,10 +8,16 @@ import contextlib
import pyblish.api
import avalon.api
from avalon.pipeline import AVALON_CONTAINER_ID
from openpype.api import Logger
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
deregister_loader_plugin_path,
register_inventory_action_path,
deregister_inventory_action_path,
AVALON_CONTAINER_ID,
)
import openpype.hosts.fusion
log = Logger().get_logger(__name__)
@ -63,9 +69,9 @@ def install():
pyblish.api.register_plugin_path(PUBLISH_PATH)
log.info("Registering Fusion plug-ins..")
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
register_inventory_action_path(INVENTORY_PATH)
pyblish.api.register_callback(
"instanceToggled", on_pyblish_instance_toggled
@ -87,11 +93,9 @@ def uninstall():
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
log.info("Deregistering Fusion plug-ins..")
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)
avalon.api.deregister_plugin_path(
avalon.api.InventoryAction, INVENTORY_PATH
)
deregister_inventory_action_path(INVENTORY_PATH)
pyblish.api.deregister_callback(
"instanceToggled", on_pyblish_instance_toggled

View file

@ -1,12 +1,14 @@
"""Host API required Work Files tool"""
import sys
import os
from avalon import api
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
from .pipeline import get_current_comp
def file_extensions():
return api.HOST_WORKFILE_EXTENSIONS["fusion"]
return HOST_WORKFILE_EXTENSIONS["fusion"]
def has_unsaved_changes():

View file

@ -1,7 +1,7 @@
from avalon import api
from openpype.pipeline import InventoryAction
class FusionSelectContainers(api.InventoryAction):
class FusionSelectContainers(InventoryAction):
label = "Select Containers"
icon = "mouse-pointer"

View file

@ -1,6 +1,6 @@
from avalon import api
from Qt import QtGui, QtWidgets
from openpype.pipeline import InventoryAction
from openpype import style
from openpype.hosts.fusion.api import (
get_current_comp,
@ -8,7 +8,7 @@ from openpype.hosts.fusion.api import (
)
class FusionSetToolColor(api.InventoryAction):
class FusionSetToolColor(InventoryAction):
"""Update the color of the selected tools"""
label = "Set Tool Color"

View file

@ -2,10 +2,10 @@
"""
from avalon import api
from openpype.pipeline import load
class FusionSetFrameRangeLoader(api.Loader):
class FusionSetFrameRangeLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["animation",
@ -39,7 +39,7 @@ class FusionSetFrameRangeLoader(api.Loader):
lib.update_frame_range(start, end)
class FusionSetFrameRangeWithHandlesLoader(api.Loader):
class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["animation",

View file

@ -1,8 +1,12 @@
import os
import contextlib
from avalon import api, io
from avalon import io
from openpype.pipeline import (
load,
get_representation_path,
)
from openpype.hosts.fusion.api import (
imprint_container,
get_current_comp,
@ -117,7 +121,7 @@ def loader_shift(loader, frame, relative=True):
return int(shift)
class FusionLoadSequence(api.Loader):
class FusionLoadSequence(load.LoaderPlugin):
"""Load image sequence into Fusion"""
families = ["imagesequence", "review", "render"]
@ -204,7 +208,7 @@ class FusionLoadSequence(api.Loader):
assert tool.ID == "Loader", "Must be Loader"
comp = tool.Comp()
root = os.path.dirname(api.get_representation_path(representation))
root = os.path.dirname(get_representation_path(representation))
path = self._get_first_image(root)
# Get start frame from version data

View file

@ -575,7 +575,7 @@ replace_files = """function %s_replace_files(args)
""" % (signature, signature)
class ImageSequenceLoader(api.Loader):
class ImageSequenceLoader(load.LoaderPlugin):
"""Load images
Stores the imported asset in a container named after the asset.
"""

View file

@ -272,8 +272,8 @@ function Client() {
app.avalonClient.send(
{
'module': 'avalon.api',
'method': 'emit',
'module': 'openpype.lib',
'method': 'emit_event',
'args': ['application.launched']
}, false);
};

View file

@ -2,15 +2,20 @@ import os
from pathlib import Path
import logging
from bson.objectid import ObjectId
import pyblish.api
from avalon import io
import avalon.api
from avalon.pipeline import AVALON_CONTAINER_ID
from openpype import lib
from openpype.lib import register_event_callback
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
deregister_loader_plugin_path,
AVALON_CONTAINER_ID,
)
import openpype.hosts.harmony
import openpype.hosts.harmony.api as harmony
@ -109,7 +114,7 @@ def check_inventory():
representation = container['representation']
representation_doc = io.find_one(
{
"_id": io.ObjectId(representation),
"_id": ObjectId(representation),
"type": "representation"
},
projection={"parent": True}
@ -180,7 +185,7 @@ def install():
pyblish.api.register_host("harmony")
pyblish.api.register_plugin_path(PUBLISH_PATH)
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
log.info(PUBLISH_PATH)
@ -194,7 +199,7 @@ def install():
def uninstall():
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)

View file

@ -2,20 +2,21 @@
import os
import shutil
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
from .lib import (
ProcessContext,
get_local_harmony_path,
zip_and_move,
launch_zip_file
)
from avalon import api
# used to lock saving until previous save is done.
save_disabled = False
def file_extensions():
return api.HOST_WORKFILE_EXTENSIONS["harmony"]
return HOST_WORKFILE_EXTENSIONS["harmony"]
def has_unsaved_changes():

View file

@ -1,4 +1,7 @@
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
import openpype.hosts.harmony.api as harmony
sig = harmony.signature()
@ -29,7 +32,7 @@ function %s(args)
""" % (sig, sig)
class ImportAudioLoader(api.Loader):
class ImportAudioLoader(load.LoaderPlugin):
"""Import audio."""
families = ["shot", "audio"]
@ -37,7 +40,7 @@ class ImportAudioLoader(api.Loader):
label = "Import Audio"
def load(self, context, name=None, namespace=None, data=None):
wav_file = api.get_representation_path(context["representation"])
wav_file = get_representation_path(context["representation"])
harmony.send(
{"function": func, "args": [context["subset"]["name"], wav_file]}
)

View file

@ -1,7 +1,10 @@
import os
import json
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
import openpype.hosts.harmony.api as harmony
import openpype.lib
@ -226,7 +229,7 @@ replace_files
"""
class BackgroundLoader(api.Loader):
class BackgroundLoader(load.LoaderPlugin):
"""Load images
Stores the imported asset in a container named after the asset.
"""
@ -278,7 +281,7 @@ class BackgroundLoader(api.Loader):
def update(self, container, representation):
path = api.get_representation_path(representation)
path = get_representation_path(representation)
with open(path) as json_file:
data = json.load(json_file)
@ -297,7 +300,7 @@ class BackgroundLoader(api.Loader):
bg_folder = os.path.dirname(path)
path = api.get_representation_path(representation)
path = get_representation_path(representation)
print(container)

View file

@ -6,12 +6,15 @@ from pathlib import Path
import clique
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
import openpype.hosts.harmony.api as harmony
import openpype.lib
class ImageSequenceLoader(api.Loader):
class ImageSequenceLoader(load.LoaderPlugin):
"""Load image sequences.
Stores the imported asset in a container named after the asset.
@ -79,7 +82,7 @@ class ImageSequenceLoader(api.Loader):
self_name = self.__class__.__name__
node = container.get("nodes").pop()
path = api.get_representation_path(representation)
path = get_representation_path(representation)
collections, remainder = clique.assemble(
os.listdir(os.path.dirname(path))
)

View file

@ -1,11 +1,14 @@
import os
import shutil
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
import openpype.hosts.harmony.api as harmony
class ImportPaletteLoader(api.Loader):
class ImportPaletteLoader(load.LoaderPlugin):
"""Import palettes."""
families = ["palette", "harmony.palette"]
@ -31,7 +34,7 @@ class ImportPaletteLoader(api.Loader):
scene_path = harmony.send(
{"function": "scene.currentProjectPath"}
)["result"]
src = api.get_representation_path(representation)
src = get_representation_path(representation)
dst = os.path.join(
scene_path,
"palette-library",

View file

@ -6,12 +6,15 @@ import os
import shutil
import uuid
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
import openpype.hosts.harmony.api as harmony
import openpype.lib
class TemplateLoader(api.Loader):
class TemplateLoader(load.LoaderPlugin):
"""Load Harmony template as container.
.. todo::
@ -38,7 +41,7 @@ class TemplateLoader(api.Loader):
# Load template.
self_name = self.__class__.__name__
temp_dir = tempfile.mkdtemp()
zip_file = api.get_representation_path(context["representation"])
zip_file = get_representation_path(context["representation"])
template_path = os.path.join(temp_dir, "temp.tpl")
with zipfile.ZipFile(zip_file, "r") as zip_ref:
zip_ref.extractall(template_path)

View file

@ -3,11 +3,14 @@ import zipfile
import os
import shutil
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
import openpype.hosts.harmony.api as harmony
class ImportTemplateLoader(api.Loader):
class ImportTemplateLoader(load.LoaderPlugin):
"""Import templates."""
families = ["harmony.template", "workfile"]
@ -17,7 +20,7 @@ class ImportTemplateLoader(api.Loader):
def load(self, context, name=None, namespace=None, data=None):
# Import template.
temp_dir = tempfile.mkdtemp()
zip_file = api.get_representation_path(context["representation"])
zip_file = get_representation_path(context["representation"])
template_path = os.path.join(temp_dir, "temp.tpl")
with zipfile.ZipFile(zip_file, "r") as zip_ref:
zip_ref.extractall(template_path)

View file

@ -1,12 +1,12 @@
import os
import hiero.core.events
from openpype.api import Logger
from openpype.lib import register_event_callback
from .lib import (
sync_avalon_data_to_workfile,
launch_workfiles_app,
selection_changed_timeline,
before_project_save,
register_event_callback
)
from .tags import add_tags_to_workfile
from .menu import update_menu_task_label

View file

@ -8,7 +8,10 @@ import platform
import ast
import shutil
import hiero
from Qt import QtWidgets
from bson.objectid import ObjectId
import avalon.api as avalon
import avalon.io
from openpype.api import (Logger, Anatomy, get_anatomy_settings)
@ -1006,7 +1009,7 @@ def check_inventory_versions():
# get representation from io
representation = io.find_one({
"type": "representation",
"_id": io.ObjectId(container["representation"])
"_id": ObjectId(container["representation"])
})
# Get start frame from version data

View file

@ -4,12 +4,17 @@ Basic avalon integration
import os
import contextlib
from collections import OrderedDict
from avalon.pipeline import AVALON_CONTAINER_ID
from avalon import api as avalon
from avalon import schema
from pyblish import api as pyblish
from openpype.api import Logger
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
deregister_loader_plugin_path,
AVALON_CONTAINER_ID,
)
from openpype.tools.utils import host_tools
from . import lib, menu, events
@ -24,7 +29,6 @@ PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish").replace("\\", "/")
LOAD_PATH = os.path.join(PLUGINS_DIR, "load").replace("\\", "/")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create").replace("\\", "/")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory").replace("\\", "/")
AVALON_CONTAINERS = ":AVALON_CONTAINERS"
@ -45,9 +49,8 @@ def install():
log.info("Registering Hiero plug-ins..")
pyblish.register_host("hiero")
pyblish.register_plugin_path(PUBLISH_PATH)
avalon.register_plugin_path(avalon.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.register_plugin_path(LegacyCreator, CREATE_PATH)
avalon.register_plugin_path(avalon.InventoryAction, INVENTORY_PATH)
# register callback for switching publishable
pyblish.register_callback("instanceToggled", on_pyblish_instance_toggled)
@ -67,7 +70,7 @@ def uninstall():
log.info("Deregistering Hiero plug-ins..")
pyblish.deregister_host("hiero")
pyblish.deregister_plugin_path(PUBLISH_PATH)
avalon.deregister_plugin_path(avalon.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
avalon.deregister_plugin_path(LegacyCreator, CREATE_PATH)
# register callback for switching publishable

View file

@ -6,9 +6,9 @@ import hiero
from Qt import QtWidgets, QtCore
import qargparse
import avalon.api as avalon
import openpype.api as openpype
from openpype.pipeline import LegacyCreator
from openpype.pipeline import LoaderPlugin, LegacyCreator
from . import lib
log = openpype.Logger().get_logger(__name__)
@ -306,7 +306,7 @@ def get_reference_node_parents(ref):
return parents
class SequenceLoader(avalon.Loader):
class SequenceLoader(LoaderPlugin):
"""A basic SequenceLoader for Resolve
This will implement the basic behavior for a loader to inherit from that

View file

@ -1,14 +1,14 @@
import os
import hiero
from avalon import api
from openpype.api import Logger
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
log = Logger().get_logger(__name__)
log = Logger.get_logger(__name__)
def file_extensions():
return api.HOST_WORKFILE_EXTENSIONS["hiero"]
return HOST_WORKFILE_EXTENSIONS["hiero"]
def has_unsaved_changes():

View file

@ -1,4 +1,5 @@
from avalon import io, api
from avalon import io
from openpype.pipeline import get_representation_path
import openpype.hosts.hiero.api as phiero
# from openpype.hosts.hiero.api import plugin, lib
# reload(lib)
@ -112,7 +113,7 @@ class LoadClip(phiero.SequenceLoader):
version_name = version.get("name", None)
colorspace = version_data.get("colorspace", None)
object_name = "{}_{}".format(name, namespace)
file = api.get_representation_path(representation).replace("\\", "/")
file = get_representation_path(representation).replace("\\", "/")
clip = track_item.source()
# reconnect media to new path

View file

@ -8,10 +8,13 @@ import hdefereval
import pyblish.api
import avalon.api
from avalon.pipeline import AVALON_CONTAINER_ID
from avalon.lib import find_submodule
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
AVALON_CONTAINER_ID,
)
import openpype.hosts.houdini
from openpype.hosts.houdini.api import lib
@ -50,7 +53,7 @@ def install():
pyblish.api.register_host("hpython")
pyblish.api.register_plugin_path(PUBLISH_PATH)
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
log.info("Installing callbacks ... ")

View file

@ -2,11 +2,11 @@
import os
import hou
from avalon import api
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
def file_extensions():
return api.HOST_WORKFILE_EXTENSIONS["houdini"]
return HOST_WORKFILE_EXTENSIONS["houdini"]
def has_unsaved_changes():

View file

@ -2,10 +2,10 @@
"""
from avalon import api
from openpype.pipeline import load
class SetFrameRangeLoader(api.Loader):
class SetFrameRangeLoader(load.LoaderPlugin):
"""Set Houdini frame range"""
families = [
@ -43,7 +43,7 @@ class SetFrameRangeLoader(api.Loader):
hou.playbar.setPlaybackRange(start, end)
class SetFrameRangeWithHandlesLoader(api.Loader):
class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"""Set Maya frame range including pre- and post-handles"""
families = [

View file

@ -1,10 +1,12 @@
import os
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
from openpype.hosts.houdini.api import pipeline
class AbcLoader(api.Loader):
class AbcLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["model", "animation", "pointcache", "gpuCache"]
@ -90,7 +92,7 @@ class AbcLoader(api.Loader):
return
# Update the file path
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = file_path.replace("\\", "/")
alembic_node.setParms({"fileName": file_path})

View file

@ -1,11 +1,15 @@
import os
from avalon import api
from avalon.houdini import pipeline
import clique
from openpype.pipeline import (
load,
get_representation_path,
)
from openpype.hosts.houdini.api import pipeline
class AssLoader(api.Loader):
class AssLoader(load.LoaderPlugin):
"""Load .ass with Arnold Procedural"""
families = ["ass"]
@ -88,7 +92,7 @@ class AssLoader(api.Loader):
def update(self, container, representation):
# Update the file path
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = file_path.replace("\\", "/")
procedural = container["node"]

View file

@ -1,4 +1,7 @@
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
from openpype.hosts.houdini.api import pipeline
@ -74,7 +77,7 @@ def transfer_non_default_values(src, dest, ignore=None):
dest_parm.setFromParm(parm)
class CameraLoader(api.Loader):
class CameraLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["camera"]
@ -129,7 +132,7 @@ class CameraLoader(api.Loader):
node = container["node"]
# Update the file path
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = file_path.replace("\\", "/")
# Update attributes

View file

@ -1,10 +1,13 @@
# -*- coding: utf-8 -*-
from avalon import api
import os
from openpype.pipeline import (
load,
get_representation_path,
)
from openpype.hosts.houdini.api import pipeline
class HdaLoader(api.Loader):
class HdaLoader(load.LoaderPlugin):
"""Load Houdini Digital Asset file."""
families = ["hda"]
@ -15,7 +18,6 @@ class HdaLoader(api.Loader):
color = "orange"
def load(self, context, name=None, namespace=None, data=None):
import os
import hou
# Format file name, Houdini only wants forward slashes
@ -49,7 +51,7 @@ class HdaLoader(api.Loader):
import hou
hda_node = container["node"]
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = file_path.replace("\\", "/")
hou.hda.installFile(file_path)
defs = hda_node.type().allInstalledDefinitions()

View file

@ -1,6 +1,10 @@
import os
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.houdini.api import lib, pipeline
import hou
@ -37,7 +41,7 @@ def get_image_avalon_container():
return image_container
class ImageLoader(api.Loader):
class ImageLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["colorbleed.imagesequence"]
@ -70,7 +74,7 @@ class ImageLoader(api.Loader):
# Imprint it manually
data = {
"schema": "avalon-core:container-2.0",
"id": pipeline.AVALON_CONTAINER_ID,
"id": AVALON_CONTAINER_ID,
"name": node_name,
"namespace": namespace,
"loader": str(self.__class__.__name__),
@ -87,7 +91,7 @@ class ImageLoader(api.Loader):
node = container["node"]
# Update the file path
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = file_path.replace("\\", "/")
file_path = self._get_file_sequence(file_path)

View file

@ -1,8 +1,12 @@
from avalon import api
from openpype.hosts.houdini.api import lib, pipeline
from openpype.pipeline import (
load,
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.houdini.api import lib
class USDSublayerLoader(api.Loader):
class USDSublayerLoader(load.LoaderPlugin):
"""Sublayer USD file in Solaris"""
families = [
@ -40,7 +44,7 @@ class USDSublayerLoader(api.Loader):
# Imprint it manually
data = {
"schema": "avalon-core:container-2.0",
"id": pipeline.AVALON_CONTAINER_ID,
"id": AVALON_CONTAINER_ID,
"name": node_name,
"namespace": namespace,
"loader": str(self.__class__.__name__),
@ -57,7 +61,7 @@ class USDSublayerLoader(api.Loader):
node = container["node"]
# Update the file path
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = file_path.replace("\\", "/")
# Update attributes

View file

@ -1,8 +1,12 @@
from avalon import api
from openpype.hosts.houdini.api import lib, pipeline
from openpype.pipeline import (
load,
get_representation_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.houdini.api import lib
class USDReferenceLoader(api.Loader):
class USDReferenceLoader(load.LoaderPlugin):
"""Reference USD file in Solaris"""
families = [
@ -40,7 +44,7 @@ class USDReferenceLoader(api.Loader):
# Imprint it manually
data = {
"schema": "avalon-core:container-2.0",
"id": pipeline.AVALON_CONTAINER_ID,
"id": AVALON_CONTAINER_ID,
"name": node_name,
"namespace": namespace,
"loader": str(self.__class__.__name__),
@ -57,7 +61,7 @@ class USDReferenceLoader(api.Loader):
node = container["node"]
# Update the file path
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = file_path.replace("\\", "/")
# Update attributes

View file

@ -1,11 +1,14 @@
import os
import re
from avalon import api
from openpype.pipeline import (
load,
get_representation_path,
)
from openpype.hosts.houdini.api import pipeline
class VdbLoader(api.Loader):
class VdbLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["vdbcache"]
@ -96,7 +99,7 @@ class VdbLoader(api.Loader):
return
# Update the file path
file_path = api.get_representation_path(representation)
file_path = get_representation_path(representation)
file_path = self.format_path(file_path)
file_node.setParms({"fileName": file_path})

View file

@ -1,7 +1,7 @@
from avalon import api
from openpype.pipeline import load
class ShowInUsdview(api.Loader):
class ShowInUsdview(load.LoaderPlugin):
"""Open USD file in usdview"""
families = ["colorbleed.usd"]

View file

@ -7,6 +7,7 @@ from collections import deque
import pyblish.api
import openpype.api
from openpype.pipeline import get_representation_path
import openpype.hosts.houdini.api.usd as hou_usdlib
from openpype.hosts.houdini.api.lib import render_rop
@ -308,7 +309,7 @@ class ExtractUSDLayered(openpype.api.Extractor):
self.log.debug("No existing representation..")
return False
old_file = api.get_representation_path(representation)
old_file = get_representation_path(representation)
if not os.path.exists(old_file):
return False

View file

@ -145,7 +145,6 @@ class AvalonURIOutputProcessor(base.OutputProcessorBase):
path = self._template.format(**{
"root": root,
"project": PROJECT,
"silo": asset_doc["silo"],
"asset": asset_doc["name"],
"subset": subset,
"representation": ext,
@ -165,4 +164,3 @@ output_processor = AvalonURIOutputProcessor()
def usdOutputProcessor():
return output_processor

View file

@ -17,10 +17,16 @@ import bson
from maya import cmds, mel
import maya.api.OpenMaya as om
from avalon import api, io, pipeline
from avalon import api, io
from openpype import lib
from openpype.api import get_anatomy_settings
from openpype.pipeline import (
discover_loader_plugins,
loaders_from_representation,
get_representation_path,
load_container,
)
from .commands import reset_frame_range
@ -1580,21 +1586,21 @@ def assign_look_by_version(nodes, version_id):
log.info("Using look for the first time ..")
# Load file
loaders = api.loaders_from_representation(api.discover(api.Loader),
representation_id)
_loaders = discover_loader_plugins()
loaders = loaders_from_representation(_loaders, representation_id)
Loader = next((i for i in loaders if i.__name__ == "LookLoader"), None)
if Loader is None:
raise RuntimeError("Could not find LookLoader, this is a bug")
# Reference the look file
with maintained_selection():
container_node = pipeline.load(Loader, look_representation)
container_node = load_container(Loader, look_representation)
# Get container members
shader_nodes = get_container_members(container_node)
# Load relationships
shader_relation = api.get_representation_path(json_representation)
shader_relation = get_representation_path(json_representation)
with open(shader_relation, "r") as f:
relationships = json.load(f)
@ -1931,18 +1937,26 @@ def remove_other_uv_sets(mesh):
cmds.removeMultiInstance(attr, b=True)
def get_id_from_history(node):
def get_id_from_sibling(node, history_only=True):
"""Return first node id in the history chain that matches this node.
The nodes in history must be of the exact same node type and must be
parented under the same parent.
Optionally, if no matching node is found from the history, all the
siblings of the node that are of the same type are checked.
Additionally to having the same parent, the sibling must be marked as
'intermediate object'.
Args:
node (str): node to retrieve the
node (str): node to retrieve the history from
history_only (bool): if True and if nothing found in history,
look for an 'intermediate object' in all the node's siblings
of same type
Returns:
str or None: The id from the node in history or None when no id found
on any valid nodes in the history.
str or None: The id from the sibling node or None when no id found
on any valid nodes in the history or siblings.
"""
@ -1971,6 +1985,45 @@ def get_id_from_history(node):
if _id:
return _id
if not history_only:
# Get siblings of same type
similar_nodes = cmds.listRelatives(parent,
type=node_type,
fullPath=True)
similar_nodes = cmds.ls(similar_nodes, exactType=node_type, long=True)
# Exclude itself
similar_nodes = [x for x in similar_nodes if x != node]
# Get all unique ids from siblings in order since
# we consistently take the first one found
sibling_ids = OrderedDict()
for similar_node in similar_nodes:
# Check if "intermediate object"
if not cmds.getAttr(similar_node + ".intermediateObject"):
continue
_id = get_id(similar_node)
if not _id:
continue
if _id in sibling_ids:
sibling_ids[_id].append(similar_node)
else:
sibling_ids[_id] = [similar_node]
if sibling_ids:
first_id, found_nodes = next(iter(sibling_ids.items()))
# Log a warning if we've found multiple unique ids
if len(sibling_ids) > 1:
log.warning(("Found more than 1 intermediate shape with"
" unique id for '{}'. Using id of first"
" found: '{}'".format(node, found_nodes[0])))
return first_id
# Project settings
def set_scene_fps(fps, update=True):

View file

@ -10,7 +10,6 @@ import pyblish.api
import avalon.api
from avalon.lib import find_submodule
from avalon.pipeline import AVALON_CONTAINER_ID
import openpype.hosts.maya
from openpype.tools.utils import host_tools
@ -20,7 +19,14 @@ from openpype.lib import (
emit_event
)
from openpype.lib.path_tools import HostDirmap
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
register_inventory_action_path,
deregister_loader_plugin_path,
deregister_inventory_action_path,
AVALON_CONTAINER_ID,
)
from openpype.hosts.maya.lib import copy_workspace_mel
from . import menu, lib
@ -53,9 +59,9 @@ def install():
pyblish.api.register_host("mayapy")
pyblish.api.register_host("maya")
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
register_inventory_action_path(INVENTORY_PATH)
log.info(PUBLISH_PATH)
log.info("Installing callbacks ... ")
@ -182,11 +188,9 @@ def uninstall():
pyblish.api.deregister_host("mayapy")
pyblish.api.deregister_host("maya")
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)
avalon.api.deregister_plugin_path(
avalon.api.InventoryAction, INVENTORY_PATH
)
deregister_inventory_action_path(INVENTORY_PATH)
menu.uninstall()

View file

@ -4,9 +4,12 @@ from maya import cmds
import qargparse
from avalon import api
from avalon.pipeline import AVALON_CONTAINER_ID
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
LoaderPlugin,
get_representation_path,
AVALON_CONTAINER_ID,
)
from .pipeline import containerise
from . import lib
@ -95,7 +98,7 @@ class Creator(LegacyCreator):
return instance
class Loader(api.Loader):
class Loader(LoaderPlugin):
hosts = ["maya"]
@ -178,7 +181,7 @@ class ReferenceLoader(Loader):
loader=self.__class__.__name__
)
loaded_containers.append(container)
self._organize_containers([ref_node], container)
self._organize_containers(nodes, container)
c += 1
namespace = None
@ -194,7 +197,7 @@ class ReferenceLoader(Loader):
node = container["objectName"]
path = api.get_representation_path(representation)
path = get_representation_path(representation)
# Get reference node from container members
members = get_container_members(node)
@ -247,6 +250,8 @@ class ReferenceLoader(Loader):
self.log.warning("Ignoring file read error:\n%s", exc)
self._organize_containers(content, container["objectName"])
# Reapply alembic settings.
if representation["name"] == "abc" and alembic_data:
alembic_nodes = cmds.ls(
@ -284,7 +289,6 @@ class ReferenceLoader(Loader):
to remove from scene.
"""
from maya import cmds
node = container["objectName"]
@ -318,6 +322,7 @@ class ReferenceLoader(Loader):
@staticmethod
def _organize_containers(nodes, container):
# type: (list, str) -> None
"""Put containers in loaded data to correct hierarchy."""
for node in nodes:
id_attr = "{}.id".format(node)
if not cmds.attributeQuery("id", node=node, exists=True):

View file

@ -6,9 +6,19 @@ import contextlib
import copy
import six
from bson.objectid import ObjectId
from maya import cmds
from avalon import api, io
from avalon import io
from openpype.pipeline import (
discover_loader_plugins,
loaders_from_representation,
load_container,
update_container,
remove_container,
get_representation_path,
)
from openpype.hosts.maya.api.lib import (
matrix_equals,
unique_namespace
@ -120,12 +130,13 @@ def load_package(filepath, name, namespace=None):
root = "{}:{}".format(namespace, name)
containers = []
all_loaders = api.discover(api.Loader)
all_loaders = discover_loader_plugins()
for representation_id, instances in data.items():
# Find the compatible loaders
loaders = api.loaders_from_representation(all_loaders,
representation_id)
loaders = loaders_from_representation(
all_loaders, representation_id
)
for instance in instances:
container = _add(instance=instance,
@ -180,9 +191,11 @@ def _add(instance, representation_id, loaders, namespace, root="|"):
instance['loader'], instance)
raise RuntimeError("Loader is missing.")
container = api.load(Loader,
representation_id,
namespace=instance['namespace'])
container = load_container(
Loader,
representation_id,
namespace=instance['namespace']
)
# Get the root from the loaded container
loaded_root = get_container_transforms({"objectName": container},
@ -271,7 +284,7 @@ def update_package_version(container, version):
# Versioning (from `core.maya.pipeline`)
current_representation = io.find_one({
"_id": io.ObjectId(container["representation"])
"_id": ObjectId(container["representation"])
})
assert current_representation is not None, "This is a bug"
@ -316,17 +329,17 @@ def update_package(set_container, representation):
# Load the original package data
current_representation = io.find_one({
"_id": io.ObjectId(set_container['representation']),
"_id": ObjectId(set_container['representation']),
"type": "representation"
})
current_file = api.get_representation_path(current_representation)
current_file = get_representation_path(current_representation)
assert current_file.endswith(".json")
with open(current_file, "r") as fp:
current_data = json.load(fp)
# Load the new package data
new_file = api.get_representation_path(representation)
new_file = get_representation_path(representation)
assert new_file.endswith(".json")
with open(new_file, "r") as fp:
new_data = json.load(fp)
@ -460,17 +473,17 @@ def update_scene(set_container, containers, current_data, new_data, new_file):
# considered as new element and added afterwards.
processed_containers.pop()
processed_namespaces.remove(container_ns)
api.remove(container)
remove_container(container)
continue
# Check whether the conversion can be done by the Loader.
# They *must* use the same asset, subset and Loader for
# `api.update` to make sense.
# `update_container` to make sense.
old = io.find_one({
"_id": io.ObjectId(representation_current)
"_id": ObjectId(representation_current)
})
new = io.find_one({
"_id": io.ObjectId(representation_new)
"_id": ObjectId(representation_new)
})
is_valid = compare_representations(old=old, new=new)
if not is_valid:
@ -479,20 +492,21 @@ def update_scene(set_container, containers, current_data, new_data, new_file):
continue
new_version = new["context"]["version"]
api.update(container, version=new_version)
update_container(container, version=new_version)
else:
# Remove this container because it's not in the new data
log.warning("Removing content: %s", container_ns)
api.remove(container)
remove_container(container)
# Add new assets
all_loaders = api.discover(api.Loader)
all_loaders = discover_loader_plugins()
for representation_id, instances in new_data.items():
# Find the compatible loaders
loaders = api.loaders_from_representation(all_loaders,
representation_id)
loaders = loaders_from_representation(
all_loaders, representation_id
)
for instance in instances:
# Already processed in update functionality
@ -517,7 +531,7 @@ def update_scene(set_container, containers, current_data, new_data, new_file):
def compare_representations(old, new):
"""Check if the old representation given can be updated
Due to limitations of the `api.update` function we cannot allow
Due to limitations of the `update_container` function we cannot allow
differences in the following data:
* Representation name (extension)

View file

@ -1,11 +1,12 @@
"""Host API required Work Files tool"""
import os
from maya import cmds
from avalon import api
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
def file_extensions():
return api.HOST_WORKFILE_EXTENSIONS["maya"]
return HOST_WORKFILE_EXTENSIONS["maya"]
def has_unsaved_changes():

View file

@ -1,12 +1,18 @@
import json
from avalon import api, io, pipeline
from avalon import io
from bson.objectid import ObjectId
from openpype.pipeline import (
InventoryAction,
get_representation_context,
get_representation_path_from_context,
)
from openpype.hosts.maya.api.lib import (
maintained_selection,
apply_shaders
)
class ImportModelRender(api.InventoryAction):
class ImportModelRender(InventoryAction):
label = "Import Model Render Sets"
icon = "industry"
@ -35,7 +41,7 @@ class ImportModelRender(api.InventoryAction):
nodes.append(n)
repr_doc = io.find_one({
"_id": io.ObjectId(container["representation"]),
"_id": ObjectId(container["representation"]),
})
version_id = repr_doc["parent"]
@ -73,11 +79,11 @@ class ImportModelRender(api.InventoryAction):
"name": self.look_data_type,
})
context = pipeline.get_representation_context(look_repr["_id"])
maya_file = pipeline.get_representation_path_from_context(context)
context = get_representation_context(look_repr["_id"])
maya_file = get_representation_path_from_context(context)
context = pipeline.get_representation_context(json_repr["_id"])
json_file = pipeline.get_representation_path_from_context(context)
context = get_representation_context(json_repr["_id"])
json_file = get_representation_path_from_context(context)
# Import the look file
with maintained_selection():

View file

@ -1,11 +1,10 @@
from maya import cmds
from avalon import api
from openpype.pipeline import InventoryAction
from openpype.hosts.maya.api.plugin import get_reference_node
class ImportReference(api.InventoryAction):
class ImportReference(InventoryAction):
"""Imports selected reference to inside of the file."""
label = "Import Reference"

View file

@ -2,14 +2,14 @@
"""
from avalon import api
from openpype.pipeline import load
from openpype.hosts.maya.api.lib import (
maintained_selection,
unique_namespace
)
class SetFrameRangeLoader(api.Loader):
class SetFrameRangeLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["animation",
@ -43,7 +43,7 @@ class SetFrameRangeLoader(api.Loader):
animationEndTime=end)
class SetFrameRangeWithHandlesLoader(api.Loader):
class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"""Specific loader of Alembic for the avalon.animation family"""
families = ["animation",
@ -81,7 +81,7 @@ class SetFrameRangeWithHandlesLoader(api.Loader):
animationEndTime=end)
class ImportMayaLoader(api.Loader):
class ImportMayaLoader(load.LoaderPlugin):
"""Import action for Maya (unmanaged)
Warning:

View file

@ -1,8 +1,11 @@
import os
import clique
from avalon import api
from openpype.api import get_project_settings
from openpype.pipeline import (
load,
get_representation_path
)
import openpype.hosts.maya.api.plugin
from openpype.hosts.maya.api.plugin import get_reference_node
from openpype.hosts.maya.api.lib import (
@ -106,7 +109,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
node = container["objectName"]
representation["context"].pop("frame", None)
path = api.get_representation_path(representation)
path = get_representation_path(representation)
print(path)
# path = self.fname
print(self.fname)
@ -164,7 +167,7 @@ class AssProxyLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
type="string")
class AssStandinLoader(api.Loader):
class AssStandinLoader(load.LoaderPlugin):
"""Load .ASS file as standin"""
families = ["ass"]
@ -240,7 +243,7 @@ class AssStandinLoader(api.Loader):
import pymel.core as pm
path = api.get_representation_path(representation)
path = get_representation_path(representation)
files_in_path = os.listdir(os.path.split(path)[0])
sequence = 0

View file

@ -1,7 +1,10 @@
from avalon import api
from openpype.pipeline import (
load,
remove_container
)
class AssemblyLoader(api.Loader):
class AssemblyLoader(load.LoaderPlugin):
families = ["assembly"]
representations = ["json"]
@ -48,13 +51,11 @@ class AssemblyLoader(api.Loader):
def update(self, container, representation):
from openpype import setdress
return setdress.update_package(container,
representation)
return setdress.update_package(container, representation)
def remove(self, container):
"""Remove all sub containers"""
from avalon import api
from openpype import setdress
import maya.cmds as cmds
@ -63,7 +64,7 @@ class AssemblyLoader(api.Loader):
for member_container in member_containers:
self.log.info("Removing container %s",
member_container['objectName'])
api.remove(member_container)
remove_container(member_container)
# Remove alembic hierarchy reference
# TODO: Check whether removing all contained references is safe enough

View file

@ -1,10 +1,14 @@
from maya import cmds, mel
from avalon import api, io
from avalon import io
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.hosts.maya.api.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
class AudioLoader(api.Loader):
class AudioLoader(load.LoaderPlugin):
"""Specific loader of audio."""
families = ["audio"]
@ -51,7 +55,7 @@ class AudioLoader(api.Loader):
assert audio_node is not None, "Audio node not found."
path = api.get_representation_path(representation)
path = get_representation_path(representation)
audio_node.filename.set(path)
cmds.setAttr(
container["objectName"] + ".representation",

View file

@ -1,9 +1,13 @@
import os
from avalon import api
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.api import get_project_settings
class GpuCacheLoader(api.Loader):
class GpuCacheLoader(load.LoaderPlugin):
"""Load model Alembic as gpuCache"""
families = ["model"]
@ -73,7 +77,7 @@ class GpuCacheLoader(api.Loader):
import maya.cmds as cmds
path = api.get_representation_path(representation)
path = get_representation_path(representation)
# Update the cache
members = cmds.sets(container['objectName'], query=True)

View file

@ -1,6 +1,10 @@
from Qt import QtWidgets, QtCore
from avalon import api, io
from avalon import io
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.hosts.maya.api.pipeline import containerise
from openpype.hosts.maya.api.lib import unique_namespace
@ -74,7 +78,7 @@ class CameraWindow(QtWidgets.QDialog):
self.close()
class ImagePlaneLoader(api.Loader):
class ImagePlaneLoader(load.LoaderPlugin):
"""Specific loader of plate for image planes on selected camera."""
families = ["image", "plate", "render"]
@ -203,7 +207,7 @@ class ImagePlaneLoader(api.Loader):
assert image_plane_shape is not None, "Image plane not found."
path = api.get_representation_path(representation)
path = get_representation_path(representation)
image_plane_shape.imageName.set(path)
cmds.setAttr(
container["objectName"] + ".representation",

View file

@ -5,7 +5,8 @@ from collections import defaultdict
from Qt import QtWidgets
from avalon import api, io
from avalon import io
from openpype.pipeline import get_representation_path
import openpype.hosts.maya.api.plugin
from openpype.hosts.maya.api import lib
from openpype.widgets.message_window import ScrollMessageBox
@ -77,7 +78,7 @@ class LookLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
})
# Load relationships
shader_relation = api.get_representation_path(json_representation)
shader_relation = get_representation_path(json_representation)
with open(shader_relation, "r") as f:
json_data = json.load(f)

View file

@ -1,8 +1,8 @@
from avalon import api
from maya import mel
from openpype.pipeline import load
class MatchmoveLoader(api.Loader):
class MatchmoveLoader(load.LoaderPlugin):
"""
This will run matchmove script to create track in scene.

View file

@ -5,8 +5,11 @@ import clique
import maya.cmds as cmds
from avalon import api
from openpype.api import get_project_settings
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.hosts.maya.api.lib import (
namespaced,
maintained_selection,
@ -15,7 +18,7 @@ from openpype.hosts.maya.api.lib import (
from openpype.hosts.maya.api.pipeline import containerise
class RedshiftProxyLoader(api.Loader):
class RedshiftProxyLoader(load.LoaderPlugin):
"""Load Redshift proxy"""
families = ["redshiftproxy"]
@ -78,7 +81,7 @@ class RedshiftProxyLoader(api.Loader):
rs_meshes = cmds.ls(members, type="RedshiftProxyMesh")
assert rs_meshes, "Cannot find RedshiftProxyMesh in container"
filename = api.get_representation_path(representation)
filename = get_representation_path(representation)
for rs_mesh in rs_meshes:
cmds.setAttr("{}.fileName".format(rs_mesh),

View file

@ -121,18 +121,10 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
if family == "rig":
self._post_process_rig(name, namespace, context, options)
else:
if "translate" in options:
cmds.setAttr(group_name + ".t", *options["translate"])
return new_nodes
def load(self, context, name=None, namespace=None, options=None):
container = super(ReferenceLoader, self).load(
context, name, namespace, options)
# clean containers if present to AVALON_CONTAINERS
self._organize_containers(self[:], container[0])
def switch(self, container, representation):
self.update(container, representation)

View file

@ -7,10 +7,13 @@ instance.
"""
import json
import six
import sys
import six
from avalon import api
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.hosts.maya.api import lib
from openpype.hosts.maya.api.pipeline import containerise
@ -18,7 +21,7 @@ from maya import cmds
import maya.app.renderSetup.model.renderSetup as renderSetup
class RenderSetupLoader(api.Loader):
class RenderSetupLoader(load.LoaderPlugin):
"""Load json preset for RenderSetup overwriting current one."""
families = ["rendersetup"]
@ -87,7 +90,7 @@ class RenderSetupLoader(api.Loader):
"Render setup setting will be overwritten by new version. All "
"setting specified by user not included in loaded version "
"will be lost.")
path = api.get_representation_path(representation)
path = get_representation_path(representation)
with open(path, "r") as file:
try:
renderSetup.instance().decode(

View file

@ -1,9 +1,10 @@
import os
from avalon import api
from openpype.api import get_project_settings
from openpype.pipeline import load
class LoadVDBtoRedShift(api.Loader):
class LoadVDBtoRedShift(load.LoaderPlugin):
"""Load OpenVDB in a Redshift Volume Shape"""
families = ["vdbcache"]

View file

@ -1,6 +1,10 @@
import os
from avalon import api
from openpype.api import get_project_settings
from openpype.pipeline import (
load,
get_representation_path
)
from maya import cmds
@ -69,7 +73,7 @@ def _fix_duplicate_vvg_callbacks():
matched.add(callback)
class LoadVDBtoVRay(api.Loader):
class LoadVDBtoVRay(load.LoaderPlugin):
families = ["vdbcache"]
representations = ["vdb"]
@ -252,7 +256,7 @@ class LoadVDBtoVRay(api.Loader):
def update(self, container, representation):
path = api.get_representation_path(representation)
path = get_representation_path(representation)
# Find VRayVolumeGrid
members = cmds.sets(container['objectName'], query=True)

View file

@ -7,10 +7,16 @@ loader will use them instead of native vray vrmesh format.
"""
import os
from bson.objectid import ObjectId
import maya.cmds as cmds
from avalon import api, io
from avalon import io
from openpype.api import get_project_settings
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.hosts.maya.api.lib import (
maintained_selection,
namespaced,
@ -19,7 +25,7 @@ from openpype.hosts.maya.api.lib import (
from openpype.hosts.maya.api.pipeline import containerise
class VRayProxyLoader(api.Loader):
class VRayProxyLoader(load.LoaderPlugin):
"""Load VRay Proxy with Alembic or VrayMesh."""
families = ["vrayproxy", "model", "pointcache", "animation"]
@ -100,7 +106,10 @@ class VRayProxyLoader(api.Loader):
assert vraymeshes, "Cannot find VRayMesh in container"
# get all representations for this version
filename = self._get_abc(representation["parent"]) or api.get_representation_path(representation) # noqa: E501
filename = (
self._get_abc(representation["parent"])
or get_representation_path(representation)
)
for vray_mesh in vraymeshes:
cmds.setAttr("{}.fileName".format(vray_mesh),
@ -179,13 +188,13 @@ class VRayProxyLoader(api.Loader):
abc_rep = io.find_one(
{
"type": "representation",
"parent": io.ObjectId(version_id),
"parent": ObjectId(version_id),
"name": "abc"
})
if abc_rep:
self.log.debug("Found, we'll link alembic to vray proxy.")
file_name = api.get_representation_path(abc_rep)
file_name = get_representation_path(abc_rep)
self.log.debug("File: {}".format(self.fname))
return file_name

View file

@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
import os
import maya.cmds as cmds # noqa
from avalon import api
from openpype.api import get_project_settings
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.hosts.maya.api.lib import (
maintained_selection,
namespaced,
@ -11,7 +14,7 @@ from openpype.hosts.maya.api.lib import (
from openpype.hosts.maya.api.pipeline import containerise
class VRaySceneLoader(api.Loader):
class VRaySceneLoader(load.LoaderPlugin):
"""Load Vray scene"""
families = ["vrayscene_layer"]
@ -78,7 +81,7 @@ class VRaySceneLoader(api.Loader):
vraymeshes = cmds.ls(members, type="VRayScene")
assert vraymeshes, "Cannot find VRayScene in container"
filename = api.get_representation_path(representation)
filename = get_representation_path(representation)
for vray_mesh in vraymeshes:
cmds.setAttr("{}.FilePath".format(vray_mesh),

View file

@ -7,13 +7,17 @@ from pprint import pprint
from maya import cmds
from avalon import api, io
from avalon import io
from openpype.api import get_project_settings
from openpype.pipeline import (
load,
get_representation_path
)
from openpype.hosts.maya.api import lib
from openpype.hosts.maya.api.pipeline import containerise
class YetiCacheLoader(api.Loader):
class YetiCacheLoader(load.LoaderPlugin):
families = ["yeticache", "yetiRig"]
representations = ["fur"]
@ -121,8 +125,8 @@ class YetiCacheLoader(api.Loader):
"cannot find fursettings representation"
)
settings_fname = api.get_representation_path(fur_settings)
path = api.get_representation_path(representation)
settings_fname = get_representation_path(fur_settings)
path = get_representation_path(representation)
# Get all node data
with open(settings_fname, "r") as fp:
settings = json.load(fp)

View file

@ -6,7 +6,7 @@ from maya import cmds
import openpype.api
from openpype.hosts.maya.api.lib import maintained_selection
from avalon.pipeline import AVALON_CONTAINER_ID
from openpype.pipeline import AVALON_CONTAINER_ID
class ExtractMayaSceneRaw(openpype.api.Extractor):
@ -58,16 +58,11 @@ class ExtractMayaSceneRaw(openpype.api.Extractor):
else:
members = instance[:]
loaded_containers = None
if set(self.add_for_families).intersection(
set(instance.data.get("families")),
set(instance.data.get("family").lower())):
loaded_containers = self._add_loaded_containers(members)
selection = members
if loaded_containers:
self.log.info(loaded_containers)
selection += loaded_containers
if set(self.add_for_families).intersection(
set(instance.data.get("families", []))) or \
instance.data.get("family") in self.add_for_families:
selection += self._get_loaded_containers(members)
# Perform extraction
self.log.info("Performing extraction ...")
@ -97,15 +92,15 @@ class ExtractMayaSceneRaw(openpype.api.Extractor):
self.log.info("Extracted instance '%s' to: %s" % (instance.name, path))
@staticmethod
def _add_loaded_containers(members):
def _get_loaded_containers(members):
# type: (list) -> list
refs_to_include = [
cmds.referenceQuery(ref, referenceNode=True)
for ref in members
if cmds.referenceQuery(ref, isNodeReferenced=True)
]
refs_to_include = {
cmds.referenceQuery(node, referenceNode=True)
for node in members
if cmds.referenceQuery(node, isNodeReferenced=True)
}
refs_to_include = set(refs_to_include)
members_with_refs = refs_to_include.union(members)
obj_sets = cmds.ls("*.id", long=True, type="objectSet", recursive=True,
objectsOnly=True)
@ -121,7 +116,7 @@ class ExtractMayaSceneRaw(openpype.api.Extractor):
continue
set_content = set(cmds.sets(obj_set, query=True))
if set_content.intersection(refs_to_include):
if set_content.intersection(members_with_refs):
loaded_containers.append(obj_set)
return loaded_containers

View file

@ -32,8 +32,8 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin):
# if a deformer has been created on the shape
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes found with non-related "
"asset IDs: {0}".format(invalid))
raise RuntimeError("Nodes found with mismatching "
"IDs: {0}".format(invalid))
@classmethod
def get_invalid(cls, instance):
@ -65,7 +65,7 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin):
invalid.append(node)
continue
history_id = lib.get_id_from_history(node)
history_id = lib.get_id_from_sibling(node)
if history_id is not None and node_id != history_id:
invalid.append(node)
@ -76,7 +76,7 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin):
for node in cls.get_invalid(instance):
# Get the original id from history
history_id = lib.get_id_from_history(node)
history_id = lib.get_id_from_sibling(node)
if not history_id:
cls.log.error("Could not find ID in history for '%s'", node)
continue

View file

@ -48,7 +48,7 @@ class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin):
invalid = []
for shape in shapes:
history_id = lib.get_id_from_history(shape)
history_id = lib.get_id_from_sibling(shape)
if history_id:
current_id = lib.get_id(shape)
if current_id != history_id:
@ -61,7 +61,7 @@ class ValidateNodeIdsDeformedShape(pyblish.api.InstancePlugin):
for node in cls.get_invalid(instance):
# Get the original id from history
history_id = lib.get_id_from_history(node)
history_id = lib.get_id_from_sibling(node)
if not history_id:
cls.log.error("Could not find ID in history for '%s'", node)
continue

View file

@ -24,6 +24,7 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
openpype.hosts.maya.api.action.SelectInvalidAction,
openpype.api.RepairAction
]
allow_history_only = False
def process(self, instance):
"""Process all meshes"""
@ -32,8 +33,8 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
# if a deformer has been created on the shape
invalid = self.get_invalid(instance)
if invalid:
raise RuntimeError("Nodes found with non-related "
"asset IDs: {0}".format(invalid))
raise RuntimeError("Nodes found with mismatching "
"IDs: {0}".format(invalid))
@classmethod
def get_invalid(cls, instance):
@ -51,10 +52,13 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
noIntermediate=True)
for shape in shapes:
history_id = lib.get_id_from_history(shape)
if history_id:
sibling_id = lib.get_id_from_sibling(
shape,
history_only=cls.allow_history_only
)
if sibling_id:
current_id = lib.get_id(shape)
if current_id != history_id:
if current_id != sibling_id:
invalid.append(shape)
return invalid
@ -63,10 +67,13 @@ class ValidateRigOutSetNodeIds(pyblish.api.InstancePlugin):
def repair(cls, instance):
for node in cls.get_invalid(instance):
# Get the original id from history
history_id = lib.get_id_from_history(node)
if not history_id:
cls.log.error("Could not find ID in history for '%s'", node)
# Get the original id from sibling
sibling_id = lib.get_id_from_sibling(
node,
history_only=cls.allow_history_only
)
if not sibling_id:
cls.log.error("Could not find ID in siblings for '%s'", node)
continue
lib.set_id(node, history_id, overwrite=True)
lib.set_id(node, sibling_id, overwrite=True)

View file

@ -1,6 +1,7 @@
import logging
import contextlib
import nuke
from bson.objectid import ObjectId
from avalon import api, io
@ -70,10 +71,10 @@ def get_handles(asset):
if "visualParent" in data:
vp = data["visualParent"]
if vp is not None:
parent_asset = io.find_one({"_id": io.ObjectId(vp)})
parent_asset = io.find_one({"_id": ObjectId(vp)})
if parent_asset is None:
parent_asset = io.find_one({"_id": io.ObjectId(asset["parent"])})
parent_asset = io.find_one({"_id": ObjectId(asset["parent"])})
if parent_asset is not None:
return get_handles(parent_asset)

View file

@ -6,10 +6,11 @@ import contextlib
from collections import OrderedDict
import clique
from bson.objectid import ObjectId
import nuke
from avalon import api, io, lib
from avalon import api, io
from openpype.api import (
Logger,
@ -20,7 +21,6 @@ from openpype.api import (
get_workdir_data,
get_asset,
get_current_project_settings,
ApplicationManager
)
from openpype.tools.utils import host_tools
from openpype.lib.path_tools import HostDirmap
@ -570,7 +570,7 @@ def check_inventory_versions():
# get representation from io
representation = io.find_one({
"type": "representation",
"_id": io.ObjectId(avalon_knob_data["representation"])
"_id": ObjectId(avalon_knob_data["representation"])
})
# Failsafe for not finding the representation.

View file

@ -6,7 +6,6 @@ import nuke
import pyblish.api
import avalon.api
from avalon import pipeline
import openpype
from openpype.api import (
@ -15,7 +14,14 @@ from openpype.api import (
get_current_project_settings
)
from openpype.lib import register_event_callback
from openpype.pipeline import LegacyCreator
from openpype.pipeline import (
LegacyCreator,
register_loader_plugin_path,
register_inventory_action_path,
deregister_loader_plugin_path,
deregister_inventory_action_path,
AVALON_CONTAINER_ID,
)
from openpype.tools.utils import host_tools
from .command import viewer_update_and_undo_stop
@ -99,9 +105,9 @@ def install():
log.info("Registering Nuke plug-ins..")
pyblish.api.register_plugin_path(PUBLISH_PATH)
avalon.api.register_plugin_path(avalon.api.Loader, LOAD_PATH)
register_loader_plugin_path(LOAD_PATH)
avalon.api.register_plugin_path(LegacyCreator, CREATE_PATH)
avalon.api.register_plugin_path(avalon.api.InventoryAction, INVENTORY_PATH)
register_inventory_action_path(INVENTORY_PATH)
# Register Avalon event for workfiles loading.
register_event_callback("workio.open_file", check_inventory_versions)
@ -125,8 +131,9 @@ def uninstall():
log.info("Deregistering Nuke plug-ins..")
pyblish.deregister_host("nuke")
pyblish.api.deregister_plugin_path(PUBLISH_PATH)
avalon.api.deregister_plugin_path(avalon.api.Loader, LOAD_PATH)
deregister_loader_plugin_path(LOAD_PATH)
avalon.api.deregister_plugin_path(LegacyCreator, CREATE_PATH)
deregister_inventory_action_path(INVENTORY_PATH)
pyblish.api.deregister_callback(
"instanceToggled", on_pyblish_instance_toggled)
@ -326,7 +333,7 @@ def containerise(node,
data = OrderedDict(
[
("schema", "openpype:container-2.0"),
("id", pipeline.AVALON_CONTAINER_ID),
("id", AVALON_CONTAINER_ID),
("name", name),
("namespace", namespace),
("loader", str(loader)),

Some files were not shown because too many files have changed in this diff Show more