fix the positional arguments

This commit is contained in:
Kayla Man 2024-04-11 18:42:01 +08:00
commit 8d9dbb616d
176 changed files with 1236 additions and 514 deletions

View file

@ -31,6 +31,7 @@ from .addon import ApplicationsAddon
__all__ = (
"APPLICATIONS_ADDON_ROOT",
"DEFAULT_ENV_SUBGROUP",
"PLATFORM_NAMES",

View file

@ -4,6 +4,7 @@ import os
import sys
import code
import traceback
from pathlib import Path
import click
import acre
@ -11,7 +12,7 @@ import acre
from ayon_core import AYON_CORE_ROOT
from ayon_core.addon import AddonsManager
from ayon_core.settings import get_general_environments
from ayon_core.lib import initialize_ayon_connection
from ayon_core.lib import initialize_ayon_connection, is_running_from_build
from .cli_commands import Commands
@ -167,16 +168,27 @@ def run(script):
if not script:
print("Error: missing path to script file.")
return
# Remove first argument if it is the same as AYON executable
# - Forward compatibility with future AYON versions.
# - Current AYON launcher keeps the arguments with first argument but
# future versions might remove it.
first_arg = sys.argv[0]
if is_running_from_build():
comp_path = os.getenv("AYON_EXECUTABLE")
else:
comp_path = os.path.join(os.environ["AYON_ROOT"], "start.py")
# Compare paths and remove first argument if it is the same as AYON
if Path(first_arg).resolve() == Path(comp_path).resolve():
sys.argv.pop(0)
args = sys.argv
args.remove("run")
args.remove(script)
sys.argv = args
# Remove 'run' command from sys.argv
sys.argv.remove("run")
args_string = " ".join(args[1:])
print(f"... running: {script} {args_string}")
runpy.run_path(script, run_name="__main__", )
args_string = " ".join(sys.argv[1:])
print(f"... running: {script} {args_string}")
runpy.run_path(script, run_name="__main__")
@main_cli.command()

View file

@ -21,7 +21,7 @@ class BackgroundLoader(api.AfterEffectsLoader):
"""
label = "Load JSON Background"
product_types = {"background"}
representations = ["json"]
representations = {"json"}
def load(self, context, name=None, namespace=None, data=None):
stub = self.get_stub()

View file

@ -20,7 +20,7 @@ class FileLoader(api.AfterEffectsLoader):
"review",
"audio",
}
representations = ["*"]
representations = {"*"}
def load(self, context, name=None, namespace=None, data=None):
stub = self.get_stub()

View file

@ -43,7 +43,7 @@ class AppendBlendLoader(plugin.AssetLoader):
so you could also use it as a new base.
"""
representations = ["blend"]
representations = {"blend"}
product_types = {"workfile"}
label = "Append Workfile"
@ -68,7 +68,7 @@ class ImportBlendLoader(plugin.AssetLoader):
so you could also use it as a new base.
"""
representations = ["blend"]
representations = {"blend"}
product_types = {"workfile"}
label = "Import Workfile"

View file

@ -27,7 +27,7 @@ class CacheModelLoader(plugin.AssetLoader):
At least for now it only supports Alembic files.
"""
product_types = {"model", "pointcache", "animation"}
representations = ["abc"]
representations = {"abc"}
label = "Load Alembic"
icon = "code-fork"

View file

@ -25,7 +25,7 @@ class BlendActionLoader(plugin.AssetLoader):
"""
product_types = {"action"}
representations = ["blend"]
representations = {"blend"}
label = "Link Action"
icon = "code-fork"

View file

@ -17,7 +17,7 @@ class BlendAnimationLoader(plugin.AssetLoader):
"""
product_types = {"animation"}
representations = ["blend"]
representations = {"blend"}
label = "Link Animation"
icon = "code-fork"

View file

@ -21,7 +21,7 @@ class AudioLoader(plugin.AssetLoader):
"""Load audio in Blender."""
product_types = {"audio"}
representations = ["wav"]
representations = {"wav"}
label = "Load Audio"
icon = "volume-up"

View file

@ -21,7 +21,7 @@ class BlendLoader(plugin.AssetLoader):
"""Load assets from a .blend file."""
product_types = {"model", "rig", "layout", "camera"}
representations = ["blend"]
representations = {"blend"}
label = "Append Blend"
icon = "code-fork"

View file

@ -19,7 +19,7 @@ class BlendSceneLoader(plugin.AssetLoader):
"""Load assets from a .blend file."""
product_types = {"blendScene"}
representations = ["blend"]
representations = {"blend"}
label = "Append Blend"
icon = "code-fork"

View file

@ -24,7 +24,7 @@ class AbcCameraLoader(plugin.AssetLoader):
"""
product_types = {"camera"}
representations = ["abc"]
representations = {"abc"}
label = "Load Camera (ABC)"
icon = "code-fork"

View file

@ -24,7 +24,7 @@ class FbxCameraLoader(plugin.AssetLoader):
"""
product_types = {"camera"}
representations = ["fbx"]
representations = {"fbx"}
label = "Load Camera (FBX)"
icon = "code-fork"

View file

@ -24,7 +24,7 @@ class FbxModelLoader(plugin.AssetLoader):
"""
product_types = {"model", "rig"}
representations = ["fbx"]
representations = {"fbx"}
label = "Load FBX"
icon = "code-fork"

View file

@ -27,7 +27,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
"""Load layout published from Unreal."""
product_types = {"layout"}
representations = ["json"]
representations = {"json"}
label = "Load Layout"
icon = "code-fork"

View file

@ -24,7 +24,7 @@ class BlendLookLoader(plugin.AssetLoader):
"""
product_types = {"look"}
representations = ["json"]
representations = {"json"}
label = "Load Look"
icon = "code-fork"

View file

@ -18,7 +18,7 @@ class LoadClip(opfapi.ClipLoader):
"""
product_types = {"render2d", "source", "plate", "render", "review"}
representations = ["*"]
representations = {"*"}
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
)

View file

@ -17,7 +17,7 @@ class LoadClipBatch(opfapi.ClipLoader):
"""
product_types = {"render2d", "source", "plate", "render", "review"}
representations = ["*"]
representations = {"*"}
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
)

View file

@ -16,6 +16,12 @@ from ayon_core.pipeline import (
AVALON_INSTANCE_ID,
AYON_INSTANCE_ID,
)
from ayon_core.pipeline.workfile import get_workdir
from ayon_api import (
get_project,
get_folder_by_path,
get_task_by_name
)
class GenericCreateSaver(Creator):
@ -125,6 +131,8 @@ class GenericCreateSaver(Creator):
product_name = data["productName"]
if (
original_product_name != product_name
or tool.GetData("openpype.task") != data["task"]
or tool.GetData("openpype.folderPath") != data["folderPath"]
or original_format != data["creator_attributes"]["image_format"]
):
self._configure_saver_tool(data, tool, product_name)
@ -145,7 +153,30 @@ class GenericCreateSaver(Creator):
folder_path = formatting_data["folderPath"]
folder_name = folder_path.rsplit("/", 1)[-1]
workdir = os.path.normpath(os.getenv("AYON_WORKDIR"))
# If the folder path and task do not match the current context then the
# workdir is not just the `AYON_WORKDIR`. Hence, we need to actually
# compute the resulting workdir
if (
data["folderPath"] == self.create_context.get_current_folder_path()
and data["task"] == self.create_context.get_current_task_name()
):
workdir = os.path.normpath(os.getenv("AYON_WORKDIR"))
else:
# TODO: Optimize this logic
project_name = self.create_context.get_current_project_name()
project_entity = get_project(project_name)
folder_entity = get_folder_by_path(project_name,
data["folderPath"])
task_entity = get_task_by_name(project_name,
folder_id=folder_entity["id"],
task_name=data["task"])
workdir = get_workdir(
project_entity=project_entity,
folder_entity=folder_entity,
task_entity=task_entity,
host_name=self.create_context.host_name,
)
formatting_data.update({
"workdir": workdir,
"frame": "0" * frame_padding,

View file

@ -0,0 +1,36 @@
import os
from ayon_core.lib import PreLaunchHook
from ayon_core.hosts.fusion import FUSION_HOST_DIR
class FusionLaunchMenuHook(PreLaunchHook):
"""Launch AYON menu on start of Fusion"""
app_groups = ["fusion"]
order = 9
def execute(self):
# Prelaunch hook is optional
settings = self.data["project_settings"][self.host_name]
if not settings["hooks"]["FusionLaunchMenuHook"]["enabled"]:
return
variant = self.application.name
if variant.isnumeric():
version = int(variant)
if version < 18:
print("Skipping launch of OpenPype menu on Fusion start "
"because Fusion version below 18.0 does not support "
"/execute argument on launch. "
f"Version detected: {version}")
return
else:
print(f"Application variant is not numeric: {variant}. "
"Validation for Fusion version 18+ for /execute "
"prelaunch argument skipped.")
path = os.path.join(FUSION_HOST_DIR,
"deploy",
"MenuScripts",
"launch_menu.py").replace("\\", "/")
script = f"fusion:RunScript('{path}')"
self.launch_context.launch_args.extend(["/execute", script])

View file

@ -17,7 +17,7 @@ class FusionSetFrameRangeLoader(load.LoaderPlugin):
"pointcache",
"render",
}
representations = ["*"]
representations = {"*"}
extensions = {"*"}
label = "Set frame range"
@ -54,7 +54,7 @@ class FusionSetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"pointcache",
"render",
}
representations = ["*"]
representations = {"*"}
label = "Set frame range (with handles)"
order = 12

View file

@ -13,7 +13,7 @@ class FusionLoadAlembicMesh(load.LoaderPlugin):
"""Load Alembic mesh into Fusion"""
product_types = {"pointcache", "model"}
representations = ["*"]
representations = {"*"}
extensions = {"abc"}
label = "Load alembic mesh"

View file

@ -13,7 +13,7 @@ class FusionLoadFBXMesh(load.LoaderPlugin):
"""Load FBX mesh into Fusion"""
product_types = {"*"}
representations = ["*"]
representations = {"*"}
extensions = {
"3ds",
"amc",

View file

@ -137,7 +137,7 @@ class FusionLoadSequence(load.LoaderPlugin):
"image",
"online",
}
representations = ["*"]
representations = {"*"}
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
)

View file

@ -17,7 +17,7 @@ class FusionLoadUSD(load.LoaderPlugin):
"""
product_types = {"*"}
representations = ["*"]
representations = {"*"}
extensions = {"usd", "usda", "usdz"}
label = "Load USD"

View file

@ -15,7 +15,7 @@ class FusionLoadWorkfile(load.LoaderPlugin):
"""Load the content of a workfile into Fusion"""
product_types = {"workfile"}
representations = ["*"]
representations = {"*"}
extensions = {"comp"}
label = "Load Workfile"

View file

@ -590,7 +590,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
"reference",
"review",
}
representations = ["*"]
representations = {"*"}
extensions = {"jpeg", "png", "jpg"}
def load(self, context, name=None, namespace=None, data=None):

View file

@ -36,7 +36,7 @@ class ImportAudioLoader(load.LoaderPlugin):
"""Import audio."""
product_types = {"shot", "audio"}
representations = ["wav"]
representations = {"wav"}
label = "Import Audio"
def load(self, context, name=None, namespace=None, data=None):

View file

@ -234,7 +234,7 @@ class BackgroundLoader(load.LoaderPlugin):
Stores the imported asset in a container named after the asset.
"""
product_types = {"background"}
representations = ["json"]
representations = {"json"}
def load(self, context, name=None, namespace=None, data=None):

View file

@ -28,7 +28,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
"reference",
"review",
}
representations = ["*"]
representations = {"*"}
extensions = {"jpeg", "png", "jpg"}
def load(self, context, name=None, namespace=None, data=None):

View file

@ -12,7 +12,7 @@ class ImportPaletteLoader(load.LoaderPlugin):
"""Import palettes."""
product_types = {"palette", "harmony.palette"}
representations = ["plt"]
representations = {"plt"}
label = "Import Palette"
def load(self, context, name=None, namespace=None, data=None):

View file

@ -24,7 +24,7 @@ class TemplateLoader(load.LoaderPlugin):
"""
product_types = {"template", "workfile"}
representations = ["*"]
representations = {"*"}
label = "Load Template"
icon = "gift"

View file

@ -14,7 +14,7 @@ class ImportTemplateLoader(load.LoaderPlugin):
"""Import templates."""
product_types = {"harmony.template", "workfile"}
representations = ["*"]
representations = {"*"}
label = "Import Template"
def load(self, context, name=None, namespace=None, data=None):
@ -61,5 +61,5 @@ class ImportWorkfileLoader(ImportTemplateLoader):
"""Import workfiles."""
product_types = {"workfile"}
representations = ["zip"]
representations = {"zip"}
label = "Import Workfile"

View file

@ -137,7 +137,7 @@ class CreateShotClip(phiero.Creator):
"value": ["<track_name>", "main", "bg", "fg", "bg",
"animatic"],
"type": "QComboBox",
"label": "pRODUCT Name",
"label": "Product Name",
"target": "ui",
"toolTip": "chose product name pattern, if <track_name> is selected, name of track layer will be used", # noqa
"order": 0},
@ -159,7 +159,7 @@ class CreateShotClip(phiero.Creator):
"type": "QCheckBox",
"label": "Include audio",
"target": "tag",
"toolTip": "Process productS with corresponding audio", # noqa
"toolTip": "Process products with corresponding audio", # noqa
"order": 3},
"sourceResolution": {
"value": False,

View file

@ -16,7 +16,7 @@ class LoadClip(phiero.SequenceLoader):
"""
product_types = {"render2d", "source", "plate", "render", "review"}
representations = ["*"]
representations = {"*"}
extensions = set(
ext.lstrip(".") for ext in IMAGE_EXTENSIONS.union(VIDEO_EXTENSIONS)
)

View file

@ -15,7 +15,7 @@ class LoadEffects(load.LoaderPlugin):
"""Loading colorspace soft effect exported from nukestudio"""
product_types = {"effect"}
representations = ["*"]
representations = {"*"}
extension = {"json"}
label = "Load Effects"

View file

@ -4,12 +4,12 @@ import pyblish.api
from ayon_core.pipeline import publish
class ExtractThumnail(publish.Extractor):
class ExtractThumbnail(publish.Extractor):
"""
Extractor for track item's tumnails
Extractor for track item's tumbnails
"""
label = "Extract Thumnail"
label = "Extract Thumbnail"
order = pyblish.api.ExtractorOrder
families = ["plate", "take"]
hosts = ["hiero"]
@ -48,7 +48,7 @@ class ExtractThumnail(publish.Extractor):
self.log.debug(
"__ thumb_path: `{}`, frame: `{}`".format(thumbnail, thumb_frame))
self.log.info("Thumnail was generated to: {}".format(thumb_path))
self.log.info("Thumbnail was generated to: {}".format(thumb_path))
thumb_representation = {
'files': thumb_file,
'stagingDir': staging_dir,

View file

@ -1001,6 +1001,82 @@ def add_self_publish_button(node):
node.setParmTemplateGroup(template)
def get_scene_viewer():
"""
Return an instance of a visible viewport.
There may be many, some could be closed, any visible are current
Returns:
Optional[hou.SceneViewer]: A scene viewer, if any.
"""
panes = hou.ui.paneTabs()
panes = [x for x in panes if x.type() == hou.paneTabType.SceneViewer]
panes = sorted(panes, key=lambda x: x.isCurrentTab())
if panes:
return panes[-1]
return None
def sceneview_snapshot(
sceneview,
filepath="$HIP/thumbnails/$HIPNAME.$F4.jpg",
frame_start=None,
frame_end=None):
"""Take a snapshot of your scene view.
It takes snapshot of your scene view for the given frame range.
So, it's capable of generating snapshots image sequence.
It works in different Houdini context e.g. Objects, Solaris
Example:
This is how the function can be used::
from ayon_core.hosts.houdini.api import lib
sceneview = hou.ui.paneTabOfType(hou.paneTabType.SceneViewer)
lib.sceneview_snapshot(sceneview)
Notes:
.png output will render poorly, so use .jpg.
How it works:
Get the current sceneviewer (may be more than one or hidden)
and screengrab the perspective viewport to a file in the
publish location to be picked up with the publish.
Credits:
https://www.sidefx.com/forum/topic/42808/?page=1#post-354796
Args:
sceneview (hou.SceneViewer): The scene view pane from which you want
to take a snapshot.
filepath (str): thumbnail filepath. it expects `$F4` token
when frame_end is bigger than frame_star other wise
each frame will override its predecessor.
frame_start (int): the frame at which snapshot starts
frame_end (int): the frame at which snapshot ends
"""
if frame_start is None:
frame_start = hou.frame()
if frame_end is None:
frame_end = frame_start
if not isinstance(sceneview, hou.SceneViewer):
log.debug("Wrong Input. {} is not of type hou.SceneViewer."
.format(sceneview))
return
viewport = sceneview.curViewport()
flip_settings = sceneview.flipbookSettings().stash()
flip_settings.frameRange((frame_start, frame_end))
flip_settings.output(filepath)
flip_settings.outputToMPlay(False)
sceneview.flipbook(viewport, flip_settings)
log.debug("A snapshot of sceneview has been saved to: {}".format(filepath))
def update_content_on_context_change():
"""Update all Creator instances to current asset"""
host = registered_host()

View file

@ -15,7 +15,7 @@ class SetFrameRangeLoader(load.LoaderPlugin):
"vdbcache",
"usd",
}
representations = ["abc", "vdb", "usd"]
representations = {"abc", "vdb", "usd"}
label = "Set frame range"
order = 11
@ -52,7 +52,7 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"vdbcache",
"usd",
}
representations = ["abc", "vdb", "usd"]
representations = {"abc", "vdb", "usd"}
label = "Set frame range (with handles)"
order = 12

View file

@ -11,7 +11,7 @@ class AbcLoader(load.LoaderPlugin):
product_types = {"model", "animation", "pointcache", "gpuCache"}
label = "Load Alembic"
representations = ["*"]
representations = {"*"}
extensions = {"abc"}
order = -10
icon = "code-fork"

View file

@ -11,7 +11,7 @@ class AbcArchiveLoader(load.LoaderPlugin):
product_types = {"model", "animation", "pointcache", "gpuCache"}
label = "Load Alembic as Archive"
representations = ["*"]
representations = {"*"}
extensions = {"abc"}
order = -5
icon = "code-fork"

View file

@ -13,7 +13,7 @@ class AssLoader(load.LoaderPlugin):
product_types = {"ass"}
label = "Load Arnold Procedural"
representations = ["ass"]
representations = {"ass"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -14,9 +14,9 @@ class BgeoLoader(load.LoaderPlugin):
label = "Load bgeo"
product_types = {"model", "pointcache", "bgeo"}
representations = [
representations = {
"bgeo", "bgeosc", "bgeogz",
"bgeo.sc", "bgeo.gz", "bgeo.lzma", "bgeo.bz2"]
"bgeo.sc", "bgeo.gz", "bgeo.lzma", "bgeo.bz2"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -89,7 +89,7 @@ class CameraLoader(load.LoaderPlugin):
product_types = {"camera"}
label = "Load Camera (abc)"
representations = ["abc"]
representations = {"abc"}
order = -10
icon = "code-fork"

View file

@ -17,7 +17,7 @@ class FbxLoader(load.LoaderPlugin):
order = -10
product_types = {"*"}
representations = ["*"]
representations = {"*"}
extensions = {"fbx"}
def load(self, context, name=None, namespace=None, data=None):

View file

@ -22,7 +22,7 @@ class FilePathLoader(load.LoaderPlugin):
icon = "link"
color = "white"
product_types = {"*"}
representations = ["*"]
representations = {"*"}
def load(self, context, name=None, namespace=None, data=None):

View file

@ -12,7 +12,7 @@ class HdaLoader(load.LoaderPlugin):
product_types = {"hda"}
label = "Load Hda"
representations = ["hda"]
representations = {"hda"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -54,7 +54,7 @@ class ImageLoader(load.LoaderPlugin):
"online",
}
label = "Load Image (COP2)"
representations = ["*"]
representations = {"*"}
order = -10
icon = "code-fork"

View file

@ -15,7 +15,7 @@ class RedshiftProxyLoader(load.LoaderPlugin):
product_types = {"redshiftproxy"}
label = "Load Redshift Proxy"
representations = ["rs"]
representations = {"rs"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -14,7 +14,7 @@ class USDSublayerLoader(load.LoaderPlugin):
"usdCamera",
}
label = "Sublayer USD"
representations = ["usd", "usda", "usdlc", "usdnc", "abc"]
representations = {"usd", "usda", "usdlc", "usdnc", "abc"}
order = 1
icon = "code-fork"

View file

@ -14,7 +14,7 @@ class USDReferenceLoader(load.LoaderPlugin):
"usdCamera",
}
label = "Reference USD"
representations = ["usd", "usda", "usdlc", "usdnc", "abc"]
representations = {"usd", "usda", "usdlc", "usdnc", "abc"}
order = -8
icon = "code-fork"

View file

@ -9,7 +9,7 @@ class SopUsdImportLoader(load.LoaderPlugin):
label = "Load USD to SOPs"
product_types = {"*"}
representations = ["usd"]
representations = {"usd"}
order = -6
icon = "code-fork"
color = "orange"

View file

@ -13,7 +13,7 @@ class VdbLoader(load.LoaderPlugin):
product_types = {"vdbcache"}
label = "Load VDB"
representations = ["vdb"]
representations = {"vdb"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -10,7 +10,7 @@ class ShowInUsdview(load.LoaderPlugin):
"""Open USD file in usdview"""
label = "Show in usdview"
representations = ["*"]
representations = {"*"}
product_types = {"*"}
extensions = {"usd", "usda", "usdlc", "usdnc", "abc"}
order = 15

View file

@ -0,0 +1,55 @@
import pyblish.api
import tempfile
from ayon_core.pipeline import publish
from ayon_core.hosts.houdini.api import lib
from ayon_core.hosts.houdini.api.pipeline import IS_HEADLESS
class ExtractActiveViewThumbnail(publish.Extractor):
"""Set instance thumbnail to a screengrab of current active viewport.
This makes it so that if an instance does not have a thumbnail set yet that
it will get a thumbnail of the currently active view at the time of
publishing as a fallback.
"""
order = pyblish.api.ExtractorOrder + 0.49
label = "Extract Active View Thumbnail"
families = ["workfile"]
hosts = ["houdini"]
def process(self, instance):
if IS_HEADLESS:
self.log.debug(
"Skip extraction of active view thumbnail, due to being in"
"headless mode."
)
return
thumbnail = instance.data.get("thumbnailPath")
if thumbnail:
# A thumbnail was already set for this instance
return
view_thumbnail = self.get_view_thumbnail(instance)
if not view_thumbnail:
return
self.log.debug("Setting instance thumbnail path to: {}"
.format(view_thumbnail)
)
instance.data["thumbnailPath"] = view_thumbnail
def get_view_thumbnail(self, instance):
sceneview = lib.get_scene_viewer()
if sceneview is None:
self.log.debug("Skipping Extract Active View Thumbnail"
" because no scene view was detected.")
return
with tempfile.NamedTemporaryFile("w", suffix=".jpg", delete=False) as tmp:
lib.sceneview_snapshot(sceneview, tmp.name)
thumbnail_path = tmp.name
instance.context.data["cleanupFullPaths"].append(thumbnail_path)
return thumbnail_path

View file

@ -19,7 +19,7 @@ class FbxLoader(load.LoaderPlugin):
"""Fbx Loader."""
product_types = {"camera"}
representations = ["fbx"]
representations = {"fbx"}
order = -9
icon = "code-fork"
color = "white"

View file

@ -78,7 +78,7 @@ class MaxSceneLoader(load.LoaderPlugin):
"model",
}
representations = ["max"]
representations = {"max"}
order = -8
icon = "code-fork"
color = "green"

View file

@ -16,7 +16,7 @@ class ModelAbcLoader(load.LoaderPlugin):
product_types = {"model"}
label = "Load Model with Alembic"
representations = ["abc"]
representations = {"abc"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -18,7 +18,7 @@ class FbxModelLoader(load.LoaderPlugin):
"""Fbx Model Loader."""
product_types = {"model"}
representations = ["fbx"]
representations = {"fbx"}
order = -9
icon = "code-fork"
color = "white"

View file

@ -20,7 +20,7 @@ class ObjLoader(load.LoaderPlugin):
"""Obj Loader."""
product_types = {"model"}
representations = ["obj"]
representations = {"obj"}
order = -9
icon = "code-fork"
color = "white"

View file

@ -24,7 +24,7 @@ class ModelUSDLoader(load.LoaderPlugin):
product_types = {"model"}
label = "Load Model(USD)"
representations = ["usda"]
representations = {"usda"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -20,7 +20,7 @@ class AbcLoader(load.LoaderPlugin):
product_types = {"camera", "animation", "pointcache"}
label = "Load Alembic"
representations = ["abc"]
representations = {"abc"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -23,7 +23,7 @@ class OxAbcLoader(load.LoaderPlugin):
product_types = {"camera", "animation", "pointcache"}
label = "Load Alembic with Ornatrix"
representations = ["abc"]
representations = {"abc"}
order = -10
icon = "code-fork"
color = "orange"

View file

@ -18,7 +18,7 @@ class PointCloudLoader(load.LoaderPlugin):
"""Point Cloud Loader."""
product_types = {"pointcloud"}
representations = ["prt"]
representations = {"prt"}
order = -8
icon = "code-fork"
color = "green"

View file

@ -24,7 +24,7 @@ class RedshiftProxyLoader(load.LoaderPlugin):
label = "Load Redshift Proxy"
product_types = {"redshiftproxy"}
representations = ["rs"]
representations = {"rs"}
order = -9
icon = "code-fork"
color = "white"

View file

@ -17,7 +17,7 @@ class TyCacheLoader(load.LoaderPlugin):
"""TyCache Loader."""
product_types = {"tycache"}
representations = ["tyc"]
representations = {"tyc"}
order = -8
icon = "code-fork"
color = "green"

View file

@ -38,15 +38,15 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin,
context_label = "{} > {}".format(*context)
instance_label = "{} > {}".format(folderPath, task)
message = (
"Instance '{}' publishes to different context than current "
"context: {}. Current context: {}".format(
"Instance '{}' publishes to different folder or task "
"than current context: {}. Current context: {}".format(
instance.name, instance_label, context_label
)
)
raise PublishValidationError(
message=message,
description=(
"## Publishing to a different context data\n"
"## Publishing to a different context folder or task\n"
"There are publish instances present which are publishing "
"into a different folder path or task than your current context.\n\n"
"Usually this is not what you want but there can be cases "

View file

@ -22,15 +22,15 @@ class MayaAddon(AYONAddon, IHostAddon):
if norm_path not in new_python_paths:
new_python_paths.append(norm_path)
# add vendor path
new_python_paths.append(
os.path.join(MAYA_ROOT_DIR, "vendor", "python")
)
env["PYTHONPATH"] = os.pathsep.join(new_python_paths)
# Set default environments
envs = {
"AYON_LOG_NO_COLORS": "1",
# For python module 'qtpy'
"QT_API": "PySide2",
# For python module 'Qt'
"QT_PREFERRED_BINDING": "PySide2"
}
for key, value in envs.items():
env[key] = value

View file

@ -37,7 +37,7 @@ from ayon_core.pipeline import (
AYON_CONTAINER_ID,
)
from ayon_core.lib import NumberDef
from ayon_core.pipeline.context_tools import get_current_folder_entity
from ayon_core.pipeline.context_tools import get_current_task_entity
from ayon_core.pipeline.create import CreateContext
from ayon_core.lib.profiles_filtering import filter_profiles
@ -1519,24 +1519,30 @@ def extract_alembic(file,
# region ID
def get_id_required_nodes(referenced_nodes=False, nodes=None):
"""Filter out any node which are locked (reference) or readOnly
def get_id_required_nodes(referenced_nodes=False,
nodes=None,
existing_ids=True):
"""Return nodes that should receive a `cbId` attribute.
This includes only mesh and curve nodes, parent transforms of the shape
nodes, file texture nodes and object sets (including shading engines).
This filters out any node which is locked, referenced, read-only,
intermediate object.
Args:
referenced_nodes (bool): set True to filter out reference nodes
referenced_nodes (bool): set True to include referenced nodes
nodes (list, Optional): nodes to consider
existing_ids (bool): set True to include nodes with `cbId` attribute
Returns:
nodes (set): list of filtered nodes
"""
lookup = None
if nodes is None:
# Consider all nodes
nodes = cmds.ls()
else:
# Build a lookup for the only allowed nodes in output based
# on `nodes` input of the function (+ ensure long names)
lookup = set(cmds.ls(nodes, long=True))
if nodes is not None and not nodes:
# User supplied an empty `nodes` list to check so all we can
# do is return the empty result
return set()
def _node_type_exists(node_type):
try:
@ -1545,63 +1551,142 @@ def get_id_required_nodes(referenced_nodes=False, nodes=None):
except RuntimeError:
return False
def iterate(maya_iterator):
while not maya_iterator.isDone():
yield maya_iterator.thisNode()
maya_iterator.next()
# `readOnly` flag is obsolete as of Maya 2016 therefore we explicitly
# remove default nodes and reference nodes
camera_shapes = ["frontShape", "sideShape", "topShape", "perspShape"]
default_camera_shapes = {
"frontShape", "sideShape", "topShape", "perspShape"
}
ignore = set()
if not referenced_nodes:
ignore |= set(cmds.ls(long=True, referencedNodes=True))
# list all defaultNodes to filter out from the rest
ignore |= set(cmds.ls(long=True, defaultNodes=True))
ignore |= set(cmds.ls(camera_shapes, long=True))
# Remove Turtle from the result of `cmds.ls` if Turtle is loaded
# TODO: This should be a less specific check for a single plug-in.
if _node_type_exists("ilrBakeLayer"):
ignore |= set(cmds.ls(type="ilrBakeLayer", long=True))
# Establish set of nodes types to include
types = ["objectSet", "file", "mesh", "nurbsCurve", "nurbsSurface"]
# The filtered types do not include transforms because we only want the
# parent transforms that have a child shape that we filtered to, so we
# include the parents here
types = ["mesh", "nurbsCurve", "nurbsSurface", "file", "objectSet"]
# Check if plugin nodes are available for Maya by checking if the plugin
# is loaded
if cmds.pluginInfo("pgYetiMaya", query=True, loaded=True):
types.append("pgYetiMaya")
# We *always* ignore intermediate shapes, so we filter them out directly
nodes = cmds.ls(nodes, type=types, long=True, noIntermediate=True)
iterator_type = OpenMaya.MIteratorType()
# This tries to be closest matching API equivalents of `types` variable
iterator_type.filterList = [
OpenMaya.MFn.kMesh, # mesh
OpenMaya.MFn.kNurbsSurface, # nurbsSurface
OpenMaya.MFn.kNurbsCurve, # nurbsCurve
OpenMaya.MFn.kFileTexture, # file
OpenMaya.MFn.kSet, # objectSet
OpenMaya.MFn.kPluginShape # pgYetiMaya
]
it = OpenMaya.MItDependencyNodes(iterator_type)
# The items which need to pass the id to their parent
# Add the collected transform to the nodes
dag = cmds.ls(nodes, type="dagNode", long=True) # query only dag nodes
transforms = cmds.listRelatives(dag,
parent=True,
fullPath=True) or []
fn_dep = OpenMaya.MFnDependencyNode()
fn_dag = OpenMaya.MFnDagNode()
result = set()
nodes = set(nodes)
nodes |= set(transforms)
def _should_include_parents(obj):
"""Whether to include parents of obj in output"""
if not obj.hasFn(OpenMaya.MFn.kShape):
return False
nodes -= ignore # Remove the ignored nodes
if not nodes:
return nodes
fn_dag.setObject(obj)
if fn_dag.isIntermediateObject:
return False
# Ensure only nodes from the input `nodes` are returned when a
# filter was applied on function call because we also iterated
# to parents and alike
if lookup is not None:
nodes &= lookup
# Skip default cameras
if (
obj.hasFn(OpenMaya.MFn.kCamera) and
fn_dag.name() in default_camera_shapes
):
return False
# Avoid locked nodes
nodes_list = list(nodes)
locked = cmds.lockNode(nodes_list, query=True, lock=True)
for node, lock in zip(nodes_list, locked):
if lock:
log.warning("Skipping locked node: %s" % node)
nodes.remove(node)
return True
return nodes
def _add_to_result_if_valid(obj):
"""Add to `result` if the object should be included"""
fn_dep.setObject(obj)
if not existing_ids and fn_dep.hasAttribute("cbId"):
return
if not referenced_nodes and fn_dep.isFromReferencedFile:
return
if fn_dep.isDefaultNode:
return
if fn_dep.isLocked:
return
# Skip default cameras
if (
obj.hasFn(OpenMaya.MFn.kCamera) and
fn_dep.name() in default_camera_shapes
):
return
if obj.hasFn(OpenMaya.MFn.kDagNode):
# DAG nodes
fn_dag.setObject(obj)
# Skip intermediate objects
if fn_dag.isIntermediateObject:
return
# DAG nodes can be instanced and thus may have multiple paths.
# We need to identify each path
paths = OpenMaya.MDagPath.getAllPathsTo(obj)
for dag in paths:
path = dag.fullPathName()
result.add(path)
else:
# Dependency node
path = fn_dep.name()
result.add(path)
for obj in iterate(it):
# For any non-intermediate shape node always include the parent
# even if we exclude the shape itself (e.g. when locked, default)
if _should_include_parents(obj):
fn_dag.setObject(obj)
parents = [
fn_dag.parent(index) for index in range(fn_dag.parentCount())
]
for parent_obj in parents:
_add_to_result_if_valid(parent_obj)
_add_to_result_if_valid(obj)
if not result:
return result
# Exclude some additional types
exclude_types = []
if _node_type_exists("ilrBakeLayer"):
# Remove Turtle from the result if Turtle is loaded
exclude_types.append("ilrBakeLayer")
if exclude_types:
exclude_nodes = set(cmds.ls(nodes, long=True, type=exclude_types))
if exclude_nodes:
result -= exclude_nodes
# Filter to explicit input nodes if provided
if nodes is not None:
# The amount of input nodes to filter to can be large and querying
# many nodes can be slow in Maya. As such we want to try and reduce
# it as much as possible, so we include the type filter to try and
# reduce the result of `maya.cmds.ls` here.
nodes = set(cmds.ls(nodes, long=True, type=types + ["dagNode"]))
if nodes:
result &= nodes
else:
return set()
return result
def get_id(node):
@ -2629,21 +2714,21 @@ def reset_frame_range(playback=True, render=True, fps=True):
def reset_scene_resolution():
"""Apply the scene resolution from the project definition
scene resolution can be overwritten by an folder if the folder.attrib
contains any information regarding scene resolution .
The scene resolution will be retrieved from the current task entity's
attributes.
Returns:
None
"""
folder_attributes = get_current_folder_entity()["attrib"]
task_attributes = get_current_task_entity(fields={"attrib"})["attrib"]
# Set resolution
width = folder_attributes.get("resolutionWidth", 1920)
height = folder_attributes.get("resolutionHeight", 1080)
pixelAspect = folder_attributes.get("pixelAspect", 1)
width = task_attributes.get("resolutionWidth", 1920)
height = task_attributes.get("resolutionHeight", 1080)
pixel_aspect = task_attributes.get("pixelAspect", 1)
set_scene_resolution(width, height, pixelAspect)
set_scene_resolution(width, height, pixel_aspect)
def set_context_settings(
@ -3129,7 +3214,7 @@ def load_capture_preset(data):
return options
def get_attr_in_layer(attr, layer):
def get_attr_in_layer(attr, layer, as_string=True):
"""Return attribute value in specified renderlayer.
Same as cmds.getAttr but this gets the attribute's value in a
@ -3147,6 +3232,7 @@ def get_attr_in_layer(attr, layer):
Args:
attr (str): attribute name, ex. "node.attribute"
layer (str): layer name
as_string (bool): whether attribute should convert to a string value
Returns:
The return value from `maya.cmds.getAttr`
@ -3156,7 +3242,8 @@ def get_attr_in_layer(attr, layer):
try:
if cmds.mayaHasRenderSetup():
from . import lib_rendersetup
return lib_rendersetup.get_attr_in_layer(attr, layer)
return lib_rendersetup.get_attr_in_layer(
attr, layer, as_string=as_string)
except AttributeError:
pass
@ -3164,7 +3251,7 @@ def get_attr_in_layer(attr, layer):
current_layer = cmds.editRenderLayerGlobals(query=True,
currentRenderLayer=True)
if layer == current_layer:
return cmds.getAttr(attr)
return cmds.getAttr(attr, asString=as_string)
connections = cmds.listConnections(attr,
plugs=True,
@ -3215,7 +3302,7 @@ def get_attr_in_layer(attr, layer):
value *= conversion
return value
return cmds.getAttr(attr)
return cmds.getAttr(attr, asString=as_string)
def fix_incompatible_containers():
@ -3244,33 +3331,46 @@ def update_content_on_context_change():
"""
This will update scene content to match new folder on context change
"""
scene_sets = cmds.listSets(allSets=True)
folder_entity = get_current_folder_entity()
folder_attributes = folder_entity["attrib"]
new_folder_path = folder_entity["path"]
for s in scene_sets:
try:
if cmds.getAttr("{}.id".format(s)) in {
AYON_INSTANCE_ID, AVALON_INSTANCE_ID
}:
attr = cmds.listAttr(s)
print(s)
if "folderPath" in attr:
print(
" - setting folder to: [ {} ]".format(new_folder_path)
)
cmds.setAttr(
"{}.folderPath".format(s),
new_folder_path, type="string"
)
if "frameStart" in attr:
cmds.setAttr("{}.frameStart".format(s),
folder_attributes["frameStart"])
if "frameEnd" in attr:
cmds.setAttr("{}.frameEnd".format(s),
folder_attributes["frameEnd"],)
except ValueError:
pass
host = registered_host()
create_context = CreateContext(host)
folder_entity = get_current_task_entity(fields={"attrib"})
instance_values = {
"folderPath": create_context.get_current_folder_path(),
"task": create_context.get_current_task_name(),
}
creator_attribute_values = {
"frameStart": folder_entity["attrib"]["frameStart"],
"frameEnd": folder_entity["attrib"]["frameEnd"],
}
has_changes = False
for instance in create_context.instances:
for key, value in instance_values.items():
if key not in instance or instance[key] == value:
continue
# Update instance value
print(f"Updating {instance.product_name} {key} to: {value}")
instance[key] = value
has_changes = True
creator_attributes = instance.creator_attributes
for key, value in creator_attribute_values.items():
if (
key not in creator_attributes
or creator_attributes[key] == value
):
continue
# Update instance creator attribute value
print(f"Updating {instance.product_name} {key} to: {value}")
instance[key] = value
has_changes = True
if has_changes:
create_context.save_changes()
def show_message(title, msg):

View file

@ -297,7 +297,7 @@ class ARenderProducts:
"""
return self._get_attr("defaultRenderGlobals", attribute)
def _get_attr(self, node_attr, attribute=None):
def _get_attr(self, node_attr, attribute=None, as_string=True):
"""Return the value of the attribute in the renderlayer
For readability this allows passing in the attribute in two ways.
@ -317,7 +317,7 @@ class ARenderProducts:
else:
plug = "{}.{}".format(node_attr, attribute)
return lib.get_attr_in_layer(plug, layer=self.layer)
return lib.get_attr_in_layer(plug, layer=self.layer, as_string=as_string)
@staticmethod
def extract_separator(file_prefix):
@ -1133,9 +1133,24 @@ class RenderProductsRedshift(ARenderProducts):
aovs = list(set(aovs) - set(ref_aovs))
products = []
global_aov_enabled = bool(
self._get_attr("redshiftOptions.aovGlobalEnableMode", as_string=False)
)
colorspace = lib.get_color_management_output_transform()
if not global_aov_enabled:
# only beauty output
for camera in cameras:
products.insert(0,
RenderProduct(productName="",
ext=ext,
multipart=self.multipart,
camera=camera,
colorspace=colorspace))
return products
light_groups_enabled = False
has_beauty_aov = False
colorspace = lib.get_color_management_output_transform()
for aov in aovs:
enabled = self._get_attr(aov, "enabled")
if not enabled:

View file

@ -77,7 +77,7 @@ def get_rendersetup_layer(layer):
if conn.endswith(".legacyRenderLayer")), None)
def get_attr_in_layer(node_attr, layer):
def get_attr_in_layer(node_attr, layer, as_string=True):
"""Return attribute value in Render Setup layer.
This will only work for attributes which can be
@ -124,7 +124,7 @@ def get_attr_in_layer(node_attr, layer):
node = history_overrides[-1] if history_overrides else override
node_attr_ = node + ".original"
return get_attribute(node_attr_, asString=True)
return get_attribute(node_attr_, asString=as_string)
layer = get_rendersetup_layer(layer)
rs = renderSetup.instance()
@ -144,7 +144,7 @@ def get_attr_in_layer(node_attr, layer):
# we will let it error out.
rs.switchToLayer(current_layer)
return get_attribute(node_attr, asString=True)
return get_attribute(node_attr, asString=as_string)
overrides = get_attr_overrides(node_attr, layer)
default_layer_value = get_default_layer_value(node_attr)

View file

@ -1,4 +1,5 @@
import os
import json
import logging
from functools import partial
@ -214,8 +215,18 @@ def install(project_settings):
)
return
config = project_settings["maya"]["scriptsmenu"]["definition"]
_menu = project_settings["maya"]["scriptsmenu"]["name"]
menu_settings = project_settings["maya"]["scriptsmenu"]
menu_name = menu_settings["name"]
config = menu_settings["definition"]
if menu_settings.get("definition_type") == "definition_json":
data = menu_settings["definition_json"]
try:
config = json.loads(data)
except json.JSONDecodeError as exc:
print("Skipping studio menu, error decoding JSON definition.")
log.error(exc)
return
if not config:
log.warning("Skipping studio menu, no definition found.")
@ -223,8 +234,8 @@ def install(project_settings):
# run the launcher for Maya menu
studio_menu = launchformaya.main(
title=_menu.title(),
objectName=_menu.title().lower().replace(" ", "_")
title=menu_name.title(),
objectName=menu_name.title().lower().replace(" ", "_")
)
# apply configuration

View file

@ -580,7 +580,8 @@ def on_save():
_remove_workfile_lock()
# Generate ids of the current context on nodes in the scene
nodes = lib.get_id_required_nodes(referenced_nodes=False)
nodes = lib.get_id_required_nodes(referenced_nodes=False,
existing_ids=False)
for node, new_id in lib.generate_ids(nodes):
lib.set_id(node, new_id, overwrite=False)
@ -653,10 +654,6 @@ def on_task_changed():
"Can't set project for new context because path does not exist: {}"
).format(workdir))
with lib.suspended_refresh():
lib.set_context_settings()
lib.update_content_on_context_change()
global _about_to_save
if not lib.IS_HEADLESS and _about_to_save:
# Let's prompt the user to update the context settings or not

View file

@ -286,7 +286,7 @@ class MayaPlaceholderLoadPlugin(PlaceholderPlugin, PlaceholderLoadMixin):
if not container:
return
roots = cmds.sets(container, q=True)
roots = cmds.sets(container, q=True) or []
ref_node = None
try:
ref_node = get_reference_node(roots)

View file

@ -40,8 +40,15 @@ class CreateRenderlayer(plugin.RenderlayerCreator):
def create(self, product_name, instance_data, pre_create_data):
# Only allow a single render instance to exist
if self._get_singleton_node():
raise CreatorError("A Render instance already exists - only "
"one can be configured.")
raise CreatorError(
"A Render instance already exists - only one can be "
"configured.\n\n"
"To render multiple render layers, create extra Render Setup "
"Layers via Maya's Render Setup UI.\n"
"Then refresh the publisher to detect the new layers for "
"rendering.\n\n"
"With a render instance present all Render Setup layers in "
"your workfile are renderable instances.")
# Apply default project render settings on create
if self.render_settings.get("apply_render_settings"):

View file

@ -51,7 +51,7 @@ class AbcLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
"camera",
"pointcache",
}
representations = ["abc"]
representations = {"abc"}
label = "Reference animation"
order = -10
@ -81,7 +81,7 @@ class FbxLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
"animation",
"camera",
}
representations = ["fbx"]
representations = {"fbx"}
label = "Reference animation"
order = -10

View file

@ -19,7 +19,7 @@ class SetFrameRangeLoader(load.LoaderPlugin):
"proxyAbc",
"pointcache",
}
representations = ["abc"]
representations = {"abc"}
label = "Set frame range"
order = 11
@ -54,7 +54,7 @@ class SetFrameRangeWithHandlesLoader(load.LoaderPlugin):
"proxyAbc",
"pointcache",
}
representations = ["abc"]
representations = {"abc"}
label = "Set frame range (with handles)"
order = 12
@ -94,7 +94,7 @@ class ImportMayaLoader(ayon_core.hosts.maya.api.plugin.Loader):
so you could also use it as a new base.
"""
representations = ["ma", "mb", "obj"]
representations = {"ma", "mb", "obj"}
product_types = {
"model",
"pointcache",
@ -125,6 +125,11 @@ class ImportMayaLoader(ayon_core.hosts.maya.api.plugin.Loader):
)
]
@classmethod
def apply_settings(cls, project_settings):
super(ImportMayaLoader, cls).apply_settings(project_settings)
cls.enabled = cls.load_settings["import_loader"].get("enabled", True)
def load(self, context, name=None, namespace=None, data=None):
import maya.cmds as cmds

View file

@ -31,7 +31,7 @@ class ArnoldStandinLoader(load.LoaderPlugin):
product_types = {
"ass", "animation", "model", "proxyAbc", "pointcache", "usd"
}
representations = ["ass", "abc", "usda", "usdc", "usd"]
representations = {"ass", "abc", "usda", "usdc", "usd"}
label = "Load as Arnold standin"
order = -5

View file

@ -13,7 +13,7 @@ from ayon_core.hosts.maya.api import setdress
class AssemblyLoader(load.LoaderPlugin):
product_types = {"assembly"}
representations = ["json"]
representations = {"json"}
label = "Load Set Dress"
order = -9

View file

@ -13,7 +13,7 @@ class AudioLoader(load.LoaderPlugin):
product_types = {"audio"}
label = "Load audio"
representations = ["wav"]
representations = {"wav"}
icon = "volume-up"
color = "orange"

View file

@ -14,7 +14,7 @@ class GpuCacheLoader(load.LoaderPlugin):
"""Load Alembic as gpuCache"""
product_types = {"model", "animation", "proxyAbc", "pointcache"}
representations = ["abc", "gpu_cache"]
representations = {"abc", "gpu_cache"}
label = "Load Gpu Cache"
order = -5

View file

@ -93,7 +93,7 @@ class FileNodeLoader(load.LoaderPlugin):
product_types = {"image", "plate", "render"}
label = "Load file node"
representations = ["exr", "tif", "png", "jpg"]
representations = {"exr", "tif", "png", "jpg"}
icon = "image"
color = "orange"
order = 2

View file

@ -89,7 +89,7 @@ class ImagePlaneLoader(load.LoaderPlugin):
product_types = {"image", "plate", "render"}
label = "Load imagePlane"
representations = ["mov", "exr", "preview", "png", "jpg"]
representations = {"mov", "exr", "preview", "png", "jpg"}
icon = "image"
color = "orange"
@ -171,7 +171,7 @@ class ImagePlaneLoader(load.LoaderPlugin):
plug = "{}.{}".format(image_plane_shape, attr)
cmds.setAttr(plug, value)
movie_representations = ["mov", "preview"]
movie_representations = {"mov", "preview"}
if context["representation"]["name"] in movie_representations:
cmds.setAttr(image_plane_shape + ".type", 2)

View file

@ -18,7 +18,7 @@ class LookLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
"""Specific loader for lookdev"""
product_types = {"look"}
representations = ["ma"]
representations = {"ma"}
label = "Reference look"
order = -10

View file

@ -9,7 +9,7 @@ class MatchmoveLoader(load.LoaderPlugin):
"""
product_types = {"matchmove"}
representations = ["py", "mel"]
representations = {"py", "mel"}
defaults = ["Camera", "Object", "Mocap"]
label = "Run matchmove script"

View file

@ -17,7 +17,7 @@ class MayaUsdLoader(load.LoaderPlugin):
"""Read USD data in a Maya USD Proxy"""
product_types = {"model", "usd", "pointcache", "animation"}
representations = ["usd", "usda", "usdc", "usdz", "abc"]
representations = {"usd", "usda", "usdc", "usdz", "abc"}
label = "Load USD to Maya Proxy"
order = -1

View file

@ -28,7 +28,7 @@ class MultiverseUsdLoader(load.LoaderPlugin):
"pointcache",
"animation",
}
representations = ["usd", "usda", "usdc", "usdz", "abc"]
representations = {"usd", "usda", "usdc", "usdz", "abc"}
label = "Load USD to Multiverse"
order = -10

View file

@ -20,7 +20,7 @@ class MultiverseUsdOverLoader(load.LoaderPlugin):
"""Reference file"""
product_types = {"mvUsdOverride"}
representations = ["usda", "usd", "udsz"]
representations = {"usda", "usd", "udsz"}
label = "Load Usd Override into Compound"
order = -10

View file

@ -23,7 +23,7 @@ class RedshiftProxyLoader(load.LoaderPlugin):
"""Load Redshift proxy"""
product_types = {"redshiftproxy"}
representations = ["rs"]
representations = {"rs"}
label = "Import Redshift Proxy"
order = -10

View file

@ -107,7 +107,7 @@ class ReferenceLoader(plugin.ReferenceLoader):
"matchmove",
}
representations = ["ma", "abc", "fbx", "mb"]
representations = {"ma", "abc", "fbx", "mb"}
label = "Reference"
order = -10
@ -269,7 +269,7 @@ class MayaUSDReferenceLoader(ReferenceLoader):
label = "Reference Maya USD"
product_types = {"usd"}
representations = ["usd"]
representations = {"usd"}
extensions = {"usd", "usda", "usdc"}
options = ReferenceLoader.options + [

View file

@ -52,7 +52,7 @@ class RenderSetupLoader(load.LoaderPlugin):
"""Load json preset for RenderSetup overwriting current one."""
product_types = {"rendersetup"}
representations = ["json"]
representations = {"json"}
defaults = ['Main']
label = "Load RenderSetup template"

View file

@ -13,7 +13,7 @@ class LoadVDBtoArnold(load.LoaderPlugin):
"""Load OpenVDB for Arnold in aiVolume"""
product_types = {"vdbcache"}
representations = ["vdb"]
representations = {"vdb"}
label = "Load VDB to Arnold"
icon = "cloud"

View file

@ -19,7 +19,7 @@ class LoadVDBtoRedShift(load.LoaderPlugin):
"""
product_types = {"vdbcache"}
representations = ["vdb"]
representations = {"vdb"}
label = "Load VDB to RedShift"
icon = "cloud"

View file

@ -78,7 +78,7 @@ class LoadVDBtoVRay(load.LoaderPlugin):
"""Load OpenVDB in a V-Ray Volume Grid"""
product_types = {"vdbcache"}
representations = ["vdb"]
representations = {"vdb"}
label = "Load VDB to VRay"
icon = "cloud"

View file

@ -28,7 +28,7 @@ class VRayProxyLoader(load.LoaderPlugin):
"""Load VRay Proxy with Alembic or VrayMesh."""
product_types = {"vrayproxy", "model", "pointcache", "animation"}
representations = ["vrmesh", "abc"]
representations = {"vrmesh", "abc"}
label = "Import VRay Proxy"
order = -10

View file

@ -18,7 +18,7 @@ class VRaySceneLoader(load.LoaderPlugin):
"""Load Vray scene"""
product_types = {"vrayscene_layer"}
representations = ["vrscene"]
representations = {"vrscene"}
label = "Import VRay Scene"
order = -10

View file

@ -21,7 +21,7 @@ class XgenLoader(ayon_core.hosts.maya.api.plugin.ReferenceLoader):
"""Load Xgen as reference"""
product_types = {"xgen"}
representations = ["ma", "mb"]
representations = {"ma", "mb"}
label = "Reference Xgen"
icon = "code-fork"

View file

@ -37,7 +37,7 @@ class YetiCacheLoader(load.LoaderPlugin):
"""Load Yeti Cache with one or more Yeti nodes"""
product_types = {"yeticache", "yetiRig"}
representations = ["fur"]
representations = {"fur"}
label = "Load Yeti Cache"
order = -9

View file

@ -8,7 +8,7 @@ class YetiRigLoader(plugin.ReferenceLoader):
"""This loader will load Yeti rig."""
product_types = {"yetiRig"}
representations = ["ma"]
representations = {"ma"}
label = "Load Yeti Rig"
order = -9

View file

@ -1,5 +1,3 @@
import json
from maya import cmds
import pyblish.api
@ -11,18 +9,24 @@ class CollectFileDependencies(pyblish.api.ContextPlugin):
label = "Collect File Dependencies"
order = pyblish.api.CollectorOrder - 0.49
hosts = ["maya"]
families = ["renderlayer"]
@classmethod
def apply_settings(cls, project_settings, system_settings):
# Disable plug-in if not used for deadline submission anyway
settings = project_settings["deadline"]["publish"]["MayaSubmitDeadline"] # noqa
cls.enabled = settings.get("asset_dependencies", True)
def process(self, context):
dependencies = []
dependencies = set()
for node in cmds.ls(type="file"):
path = cmds.getAttr("{}.{}".format(node, "fileTextureName"))
if path not in dependencies:
dependencies.append(path)
dependencies.add(path)
for node in cmds.ls(type="AlembicNode"):
path = cmds.getAttr("{}.{}".format(node, "abc_File"))
if path not in dependencies:
dependencies.append(path)
dependencies.add(path)
context.data["fileDependencies"] = dependencies
self.log.debug(json.dumps(dependencies, indent=4))
context.data["fileDependencies"] = list(dependencies)

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