mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-02 17:04:54 +01:00
Merge branch 'develop' into enhancement/OP-5920_abc-options-for-Pointcache-Animation-family
This commit is contained in:
commit
cd26a893b8
302 changed files with 8147 additions and 3467 deletions
12
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
12
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,12 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to OpenPype Tray
|
||||
options:
|
||||
- 3.17.7-nightly.7
|
||||
- 3.17.7-nightly.6
|
||||
- 3.17.7-nightly.5
|
||||
- 3.17.7-nightly.4
|
||||
- 3.17.7-nightly.3
|
||||
- 3.17.7-nightly.2
|
||||
- 3.17.7-nightly.1
|
||||
- 3.17.6
|
||||
- 3.17.6-nightly.3
|
||||
|
|
@ -129,12 +135,6 @@ body:
|
|||
- 3.15.3-nightly.3
|
||||
- 3.15.3-nightly.2
|
||||
- 3.15.3-nightly.1
|
||||
- 3.15.2
|
||||
- 3.15.2-nightly.6
|
||||
- 3.15.2-nightly.5
|
||||
- 3.15.2-nightly.4
|
||||
- 3.15.2-nightly.3
|
||||
- 3.15.2-nightly.2
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
|
|
|||
|
|
@ -296,12 +296,15 @@ def run(script):
|
|||
@click.option("--mongo_url",
|
||||
help="MongoDB for testing.",
|
||||
default=None)
|
||||
@click.option("--dump_databases",
|
||||
help="Dump all databases to data folder.",
|
||||
default=None)
|
||||
def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant,
|
||||
timeout, setup_only, mongo_url, app_group):
|
||||
timeout, setup_only, mongo_url, app_group, dump_databases):
|
||||
"""Run all automatic tests after proper initialization via start.py"""
|
||||
PypeCommands().run_tests(folder, mark, pyargs, test_data_folder,
|
||||
persist, app_variant, timeout, setup_only,
|
||||
mongo_url, app_group)
|
||||
mongo_url, app_group, dump_databases)
|
||||
|
||||
|
||||
@main.command(help="DEPRECATED - run sync server")
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ from .entities import (
|
|||
get_thumbnail_id_from_source,
|
||||
|
||||
get_workfile_info,
|
||||
|
||||
get_asset_name_identifier,
|
||||
)
|
||||
|
||||
from .entity_links import (
|
||||
|
|
@ -108,4 +110,6 @@ __all__ = (
|
|||
"get_linked_representation_id",
|
||||
|
||||
"create_project",
|
||||
|
||||
"get_asset_name_identifier",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,3 +4,22 @@ if not AYON_SERVER_ENABLED:
|
|||
from .mongo.entities import *
|
||||
else:
|
||||
from .server.entities import *
|
||||
|
||||
|
||||
def get_asset_name_identifier(asset_doc):
|
||||
"""Get asset name identifier by asset document.
|
||||
|
||||
This function is added because of AYON implementation where name
|
||||
identifier is not just a name but full path.
|
||||
|
||||
Asset document must have "name" key, and "data.parents" when in AYON mode.
|
||||
|
||||
Args:
|
||||
asset_doc (dict[str, Any]): Asset document.
|
||||
"""
|
||||
|
||||
if not AYON_SERVER_ENABLED:
|
||||
return asset_doc["name"]
|
||||
parents = list(asset_doc["data"]["parents"])
|
||||
parents.append(asset_doc["name"])
|
||||
return "/" + "/".join(parents)
|
||||
|
|
|
|||
|
|
@ -606,7 +606,7 @@ def convert_v4_version_to_v3(version):
|
|||
output_data[dst_key] = version[src_key]
|
||||
|
||||
if "createdAt" in version:
|
||||
created_at = arrow.get(version["createdAt"])
|
||||
created_at = arrow.get(version["createdAt"]).to("local")
|
||||
output_data["time"] = created_at.strftime("%Y%m%dT%H%M%SZ")
|
||||
|
||||
output["data"] = output_data
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ def _get_subsets(
|
|||
|
||||
for subset in con.get_products(
|
||||
project_name,
|
||||
subset_ids,
|
||||
subset_names,
|
||||
product_ids=subset_ids,
|
||||
product_names=subset_names,
|
||||
folder_ids=folder_ids,
|
||||
names_by_folder_ids=names_by_folder_ids,
|
||||
active=active,
|
||||
|
|
@ -113,23 +113,23 @@ def _get_versions(
|
|||
|
||||
queried_versions = con.get_versions(
|
||||
project_name,
|
||||
version_ids,
|
||||
subset_ids,
|
||||
versions,
|
||||
hero,
|
||||
standard,
|
||||
latest,
|
||||
version_ids=version_ids,
|
||||
product_ids=subset_ids,
|
||||
versions=versions,
|
||||
hero=hero,
|
||||
standard=standard,
|
||||
latest=latest,
|
||||
active=active,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
versions = []
|
||||
version_entities = []
|
||||
hero_versions = []
|
||||
for version in queried_versions:
|
||||
if version["version"] < 0:
|
||||
hero_versions.append(version)
|
||||
else:
|
||||
versions.append(convert_v4_version_to_v3(version))
|
||||
version_entities.append(convert_v4_version_to_v3(version))
|
||||
|
||||
if hero_versions:
|
||||
subset_ids = set()
|
||||
|
|
@ -159,9 +159,9 @@ def _get_versions(
|
|||
break
|
||||
conv_hero = convert_v4_version_to_v3(hero_version)
|
||||
conv_hero["version_id"] = version_id
|
||||
versions.append(conv_hero)
|
||||
version_entities.append(conv_hero)
|
||||
|
||||
return versions
|
||||
return version_entities
|
||||
|
||||
|
||||
def get_asset_by_id(project_name, asset_id, fields=None):
|
||||
|
|
@ -182,6 +182,19 @@ def get_asset_by_name(project_name, asset_name, fields=None):
|
|||
return None
|
||||
|
||||
|
||||
def _folders_query(project_name, con, fields, **kwargs):
|
||||
if fields is None or "tasks" in fields:
|
||||
folders = get_folders_with_tasks(
|
||||
con, project_name, fields=fields, **kwargs
|
||||
)
|
||||
|
||||
else:
|
||||
folders = con.get_folders(project_name, fields=fields, **kwargs)
|
||||
|
||||
for folder in folders:
|
||||
yield folder
|
||||
|
||||
|
||||
def get_assets(
|
||||
project_name,
|
||||
asset_ids=None,
|
||||
|
|
@ -201,20 +214,39 @@ def get_assets(
|
|||
fields = folder_fields_v3_to_v4(fields, con)
|
||||
kwargs = dict(
|
||||
folder_ids=asset_ids,
|
||||
folder_names=asset_names,
|
||||
parent_ids=parent_ids,
|
||||
active=active,
|
||||
fields=fields
|
||||
)
|
||||
if not asset_names:
|
||||
for folder in _folders_query(project_name, con, fields, **kwargs):
|
||||
yield convert_v4_folder_to_v3(folder, project_name)
|
||||
return
|
||||
|
||||
if fields is None or "tasks" in fields:
|
||||
folders = get_folders_with_tasks(con, project_name, **kwargs)
|
||||
new_asset_names = set()
|
||||
folder_paths = set()
|
||||
for name in asset_names:
|
||||
if "/" in name:
|
||||
folder_paths.add(name)
|
||||
else:
|
||||
new_asset_names.add(name)
|
||||
|
||||
else:
|
||||
folders = con.get_folders(project_name, **kwargs)
|
||||
yielded_ids = set()
|
||||
if folder_paths:
|
||||
for folder in _folders_query(
|
||||
project_name, con, fields, folder_paths=folder_paths, **kwargs
|
||||
):
|
||||
yielded_ids.add(folder["id"])
|
||||
yield convert_v4_folder_to_v3(folder, project_name)
|
||||
|
||||
for folder in folders:
|
||||
yield convert_v4_folder_to_v3(folder, project_name)
|
||||
if not new_asset_names:
|
||||
return
|
||||
|
||||
for folder in _folders_query(
|
||||
project_name, con, fields, folder_names=new_asset_names, **kwargs
|
||||
):
|
||||
if folder["id"] not in yielded_ids:
|
||||
yielded_ids.add(folder["id"])
|
||||
yield convert_v4_folder_to_v3(folder, project_name)
|
||||
|
||||
|
||||
def get_archived_assets(
|
||||
|
|
@ -507,11 +539,11 @@ def get_representations(
|
|||
|
||||
representations = con.get_representations(
|
||||
project_name,
|
||||
representation_ids,
|
||||
representation_names,
|
||||
version_ids,
|
||||
names_by_version_ids,
|
||||
active,
|
||||
representation_ids=representation_ids,
|
||||
representation_names=representation_names,
|
||||
version_ids=version_ids,
|
||||
names_by_version_ids=names_by_version_ids,
|
||||
active=active,
|
||||
fields=fields
|
||||
)
|
||||
for representation in representations:
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook):
|
|||
"tvpaint",
|
||||
"substancepainter",
|
||||
"aftereffects",
|
||||
"wrap"
|
||||
}
|
||||
launch_types = {LaunchTypes.local}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ class CopyTemplateWorkfile(PreLaunchHook):
|
|||
|
||||
# Before `AddLastWorkfileToLaunchArgs`
|
||||
order = 0
|
||||
app_groups = {"blender", "photoshop", "tvpaint", "aftereffects"}
|
||||
app_groups = {"blender", "photoshop", "tvpaint", "aftereffects",
|
||||
"wrap"}
|
||||
launch_types = {LaunchTypes.local}
|
||||
|
||||
def execute(self):
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ class HostBase(object):
|
|||
if project_name:
|
||||
items.append(project_name)
|
||||
if asset_name:
|
||||
items.append(asset_name)
|
||||
items.append(asset_name.lstrip("/"))
|
||||
if task_name:
|
||||
items.append(task_name)
|
||||
if items:
|
||||
|
|
|
|||
|
|
@ -56,16 +56,15 @@ class RenderCreator(Creator):
|
|||
use_composition_name = (pre_create_data.get("use_composition_name") or
|
||||
len(comps) > 1)
|
||||
for comp in comps:
|
||||
composition_name = re.sub(
|
||||
"[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS),
|
||||
"",
|
||||
comp.name
|
||||
)
|
||||
if use_composition_name:
|
||||
if "{composition}" not in subset_name_from_ui.lower():
|
||||
subset_name_from_ui += "{Composition}"
|
||||
|
||||
composition_name = re.sub(
|
||||
"[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS),
|
||||
"",
|
||||
comp.name
|
||||
)
|
||||
|
||||
dynamic_fill = prepare_template_data({"composition":
|
||||
composition_name})
|
||||
subset_name = subset_name_from_ui.format(**dynamic_fill)
|
||||
|
|
@ -81,6 +80,8 @@ class RenderCreator(Creator):
|
|||
inst.subset_name))
|
||||
|
||||
data["members"] = [comp.id]
|
||||
data["orig_comp_name"] = composition_name
|
||||
|
||||
new_instance = CreatedInstance(self.family, subset_name, data,
|
||||
self)
|
||||
if "farm" in pre_create_data:
|
||||
|
|
@ -88,7 +89,7 @@ class RenderCreator(Creator):
|
|||
new_instance.creator_attributes["farm"] = use_farm
|
||||
|
||||
review = pre_create_data["mark_for_review"]
|
||||
new_instance.creator_attributes["mark_for_review"] = review
|
||||
new_instance. creator_attributes["mark_for_review"] = review
|
||||
|
||||
api.get_stub().imprint(new_instance.id,
|
||||
new_instance.data_to_store())
|
||||
|
|
@ -150,16 +151,18 @@ class RenderCreator(Creator):
|
|||
subset_change.new_value)
|
||||
|
||||
def remove_instances(self, instances):
|
||||
"""Removes metadata and renames to original comp name if available."""
|
||||
for instance in instances:
|
||||
self._remove_instance_from_context(instance)
|
||||
self.host.remove_instance(instance)
|
||||
|
||||
subset = instance.data["subset"]
|
||||
comp_id = instance.data["members"][0]
|
||||
comp = api.get_stub().get_item(comp_id)
|
||||
orig_comp_name = instance.data.get("orig_comp_name")
|
||||
if comp:
|
||||
new_comp_name = comp.name.replace(subset, '')
|
||||
if not new_comp_name:
|
||||
if orig_comp_name:
|
||||
new_comp_name = orig_comp_name
|
||||
else:
|
||||
new_comp_name = "dummyCompName"
|
||||
api.get_stub().rename_item(comp_id,
|
||||
new_comp_name)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from openpype import AYON_SERVER_ENABLED
|
||||
import openpype.hosts.aftereffects.api as api
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.pipeline import (
|
||||
|
|
@ -43,6 +44,14 @@ class AEWorkfileCreator(AutoCreator):
|
|||
task_name = context.get_current_task_name()
|
||||
host_name = context.host_name
|
||||
|
||||
existing_asset_name = None
|
||||
if existing_instance is not None:
|
||||
if AYON_SERVER_ENABLED:
|
||||
existing_asset_name = existing_instance.get("folderPath")
|
||||
|
||||
if existing_asset_name is None:
|
||||
existing_asset_name = existing_instance["asset"]
|
||||
|
||||
if existing_instance is None:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
|
|
@ -50,10 +59,13 @@ class AEWorkfileCreator(AutoCreator):
|
|||
project_name, host_name
|
||||
)
|
||||
data = {
|
||||
"asset": asset_name,
|
||||
"task": task_name,
|
||||
"variant": self.default_variant
|
||||
}
|
||||
if AYON_SERVER_ENABLED:
|
||||
data["folderPath"] = asset_name
|
||||
else:
|
||||
data["asset"] = asset_name
|
||||
data.update(self.get_dynamic_data(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name, None
|
||||
|
|
@ -68,7 +80,7 @@ class AEWorkfileCreator(AutoCreator):
|
|||
new_instance.data_to_store())
|
||||
|
||||
elif (
|
||||
existing_instance["asset"] != asset_name
|
||||
existing_asset_name != asset_name
|
||||
or existing_instance["task"] != task_name
|
||||
):
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
|
|
@ -76,6 +88,10 @@ class AEWorkfileCreator(AutoCreator):
|
|||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name
|
||||
)
|
||||
existing_instance["asset"] = asset_name
|
||||
if AYON_SERVER_ENABLED:
|
||||
existing_instance["folderPath"] = asset_name
|
||||
else:
|
||||
existing_instance["asset"] = asset_name
|
||||
|
||||
existing_instance["task"] = task_name
|
||||
existing_instance["subset"] = subset_name
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.client import get_asset_name_identifier
|
||||
from openpype.pipeline.create import get_subset_name
|
||||
|
||||
|
||||
|
|
@ -48,9 +50,11 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
|
|||
asset_entity = context.data["assetEntity"]
|
||||
project_entity = context.data["projectEntity"]
|
||||
|
||||
asset_name = get_asset_name_identifier(asset_entity)
|
||||
|
||||
instance_data = {
|
||||
"active": True,
|
||||
"asset": asset_entity["name"],
|
||||
"asset": asset_name,
|
||||
"task": task,
|
||||
"frameStart": context.data['frameStart'],
|
||||
"frameEnd": context.data['frameEnd'],
|
||||
|
|
|
|||
|
|
@ -60,8 +60,9 @@ class ExtractLocalRender(publish.Extractor):
|
|||
first_repre = not representations
|
||||
if instance.data["review"] and first_repre:
|
||||
repre_data["tags"] = ["review"]
|
||||
thumbnail_path = os.path.join(staging_dir, files[0])
|
||||
instance.data["thumbnailSource"] = thumbnail_path
|
||||
# TODO return back when Extract from source same as regular
|
||||
# thumbnail_path = os.path.join(staging_dir, files[0])
|
||||
# instance.data["thumbnailSource"] = thumbnail_path
|
||||
|
||||
representations.append(repre_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ from typing import Dict, List, Optional
|
|||
|
||||
import bpy
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.pipeline import (
|
||||
Creator,
|
||||
CreatedInstance,
|
||||
LoaderPlugin,
|
||||
get_current_task_name,
|
||||
)
|
||||
from openpype.lib import BoolDef
|
||||
|
||||
|
|
@ -225,7 +225,12 @@ class BaseCreator(Creator):
|
|||
bpy.context.scene.collection.children.link(instances)
|
||||
|
||||
# Create asset group
|
||||
name = prepare_scene_name(instance_data["asset"], subset_name)
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset_name = instance_data["folderPath"]
|
||||
else:
|
||||
asset_name = instance_data["asset"]
|
||||
|
||||
name = prepare_scene_name(asset_name, subset_name)
|
||||
if self.create_as_asset_group:
|
||||
# Create instance as empty
|
||||
instance_node = bpy.data.objects.new(name=name, object_data=None)
|
||||
|
|
@ -281,7 +286,14 @@ class BaseCreator(Creator):
|
|||
|
||||
Args:
|
||||
update_list(List[UpdateData]): Changed instances
|
||||
and their changes, as a list of tuples."""
|
||||
and their changes, as a list of tuples.
|
||||
"""
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset_name_key = "folderPath"
|
||||
else:
|
||||
asset_name_key = "asset"
|
||||
|
||||
for created_instance, changes in update_list:
|
||||
data = created_instance.data_to_store()
|
||||
node = created_instance.transient_data["instance_node"]
|
||||
|
|
@ -295,11 +307,12 @@ class BaseCreator(Creator):
|
|||
|
||||
# Rename the instance node in the scene if subset or asset changed
|
||||
if (
|
||||
"subset" in changes.changed_keys
|
||||
or "asset" in changes.changed_keys
|
||||
"subset" in changes.changed_keys
|
||||
or asset_name_key in changes.changed_keys
|
||||
):
|
||||
asset_name = data[asset_name_key]
|
||||
name = prepare_scene_name(
|
||||
asset=data["asset"], subset=data["subset"]
|
||||
asset=asset_name, subset=data["subset"]
|
||||
)
|
||||
node.name = name
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ def get_render_product(output_path, name, aov_sep):
|
|||
instance (pyblish.api.Instance): The instance to publish.
|
||||
ext (str): The image format to render.
|
||||
"""
|
||||
filepath = os.path.join(output_path, name)
|
||||
filepath = output_path / name.lstrip("/")
|
||||
render_product = f"{filepath}{aov_sep}beauty.####"
|
||||
render_product = render_product.replace("\\", "/")
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ def set_node_tree(output_path, name, aov_sep, ext, multilayer):
|
|||
return []
|
||||
|
||||
output.file_slots.clear()
|
||||
output.base_path = output_path
|
||||
output.base_path = str(output_path)
|
||||
|
||||
aov_file_products = []
|
||||
|
||||
|
|
@ -191,8 +191,9 @@ def set_node_tree(output_path, name, aov_sep, ext, multilayer):
|
|||
|
||||
output.file_slots.new(filepath)
|
||||
|
||||
aov_file_products.append(
|
||||
(render_pass.name, os.path.join(output_path, filepath)))
|
||||
filename = str(output_path / filepath.lstrip("/"))
|
||||
|
||||
aov_file_products.append((render_pass.name, filename))
|
||||
|
||||
node_input = output.inputs[-1]
|
||||
|
||||
|
|
@ -214,12 +215,11 @@ def imprint_render_settings(node, data):
|
|||
def prepare_rendering(asset_group):
|
||||
name = asset_group.name
|
||||
|
||||
filepath = bpy.data.filepath
|
||||
filepath = Path(bpy.data.filepath)
|
||||
assert filepath, "Workfile not saved. Please save the file first."
|
||||
|
||||
file_path = os.path.dirname(filepath)
|
||||
file_name = os.path.basename(filepath)
|
||||
file_name, _ = os.path.splitext(file_name)
|
||||
dirpath = filepath.parent
|
||||
file_name = Path(filepath.name).stem
|
||||
|
||||
project = get_current_project_name()
|
||||
settings = get_project_settings(project)
|
||||
|
|
@ -232,7 +232,7 @@ def prepare_rendering(asset_group):
|
|||
set_render_format(ext, multilayer)
|
||||
aov_list, custom_passes = set_render_passes(settings)
|
||||
|
||||
output_path = os.path.join(file_path, render_folder, file_name)
|
||||
output_path = Path.joinpath(dirpath, render_folder, file_name)
|
||||
|
||||
render_product = get_render_product(output_path, name, aov_sep)
|
||||
aov_file_product = set_node_tree(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import bpy
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.pipeline import CreatedInstance, AutoCreator
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.hosts.blender.api.plugin import BaseCreator
|
||||
|
|
@ -24,7 +25,7 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
|
||||
def create(self):
|
||||
"""Create workfile instances."""
|
||||
current_instance = next(
|
||||
existing_instance = next(
|
||||
(
|
||||
instance for instance in self.create_context.instances
|
||||
if instance.creator_identifier == self.identifier
|
||||
|
|
@ -37,16 +38,27 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
task_name = self.create_context.get_current_task_name()
|
||||
host_name = self.create_context.host_name
|
||||
|
||||
if not current_instance:
|
||||
existing_asset_name = None
|
||||
if existing_instance is not None:
|
||||
if AYON_SERVER_ENABLED:
|
||||
existing_asset_name = existing_instance.get("folderPath")
|
||||
|
||||
if existing_asset_name is None:
|
||||
existing_asset_name = existing_instance["asset"]
|
||||
|
||||
if not existing_instance:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
task_name, task_name, asset_doc, project_name, host_name
|
||||
)
|
||||
data = {
|
||||
"asset": asset_name,
|
||||
"task": task_name,
|
||||
"variant": task_name,
|
||||
}
|
||||
if AYON_SERVER_ENABLED:
|
||||
data["folderPath"] = asset_name
|
||||
else:
|
||||
data["asset"] = asset_name
|
||||
data.update(
|
||||
self.get_dynamic_data(
|
||||
task_name,
|
||||
|
|
@ -54,7 +66,7 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
asset_doc,
|
||||
project_name,
|
||||
host_name,
|
||||
current_instance,
|
||||
existing_instance,
|
||||
)
|
||||
)
|
||||
self.log.info("Auto-creating workfile instance...")
|
||||
|
|
@ -65,17 +77,21 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
current_instance.transient_data["instance_node"] = instance_node
|
||||
self._add_instance_to_context(current_instance)
|
||||
elif (
|
||||
current_instance["asset"] != asset_name
|
||||
or current_instance["task"] != task_name
|
||||
existing_asset_name != asset_name
|
||||
or existing_instance["task"] != task_name
|
||||
):
|
||||
# Update instance context if it's different
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
task_name, task_name, asset_doc, project_name, host_name
|
||||
)
|
||||
current_instance["asset"] = asset_name
|
||||
current_instance["task"] = task_name
|
||||
current_instance["subset"] = subset_name
|
||||
if AYON_SERVER_ENABLED:
|
||||
existing_instance["folderPath"] = asset_name
|
||||
else:
|
||||
existing_instance["asset"] = asset_name
|
||||
|
||||
existing_instance["task"] = task_name
|
||||
existing_instance["subset"] = subset_name
|
||||
|
||||
def collect_instances(self):
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import pyblish.api
|
|||
|
||||
|
||||
class CollectBlenderRender(pyblish.api.InstancePlugin):
|
||||
"""Gather all publishable render layers from renderSetup."""
|
||||
"""Gather all publishable render instances."""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.01
|
||||
hosts = ["blender"]
|
||||
families = ["render"]
|
||||
label = "Collect Render Layers"
|
||||
label = "Collect Render"
|
||||
sync_workfile_version = False
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -78,8 +78,6 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
|
||||
assert render_data, "No render data found."
|
||||
|
||||
self.log.debug(f"render_data: {dict(render_data)}")
|
||||
|
||||
render_product = render_data.get("render_product")
|
||||
aov_file_product = render_data.get("aov_file_product")
|
||||
ext = render_data.get("image_format")
|
||||
|
|
@ -101,7 +99,7 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
expected_files = expected_beauty | expected_aovs
|
||||
|
||||
instance.data.update({
|
||||
"family": "render.farm",
|
||||
"families": ["render", "render.farm"],
|
||||
"frameStart": frame_start,
|
||||
"frameEnd": frame_end,
|
||||
"frameStartHandle": frame_handle_start,
|
||||
|
|
@ -120,5 +118,3 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
"colorspaceView": "ACES 1.0 SDR-video",
|
||||
"renderProducts": colorspace.ARenderProduct(),
|
||||
})
|
||||
|
||||
self.log.debug(f"data: {instance.data}")
|
||||
|
|
|
|||
|
|
@ -19,7 +19,10 @@ class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.abc"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
filename = f"{instance_name}.abc"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ class ExtractAnimationABC(
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.abc"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
filename = f"{instance_name}.abc"
|
||||
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
# Define extract output file path
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.blend"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
filename = f"{instance_name}.blend"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@ class ExtractBlendAnimation(
|
|||
# Define extract output file path
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.blend"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
filename = f"{instance_name}.blend"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@ class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.abc"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
filename = f"{instance_name}.abc"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
|
|
|
|||
|
|
@ -20,7 +20,10 @@ class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.fbx"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
filename = f"{instance_name}.fbx"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@ class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = f"{instance.name}.fbx"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
filename = f"{instance_name}.fbx"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
# Perform extraction
|
||||
|
|
|
|||
|
|
@ -145,7 +145,10 @@ class ExtractAnimationFBX(
|
|||
|
||||
root.select_set(True)
|
||||
armature.select_set(True)
|
||||
fbx_filename = f"{instance.name}_{armature.name}.fbx"
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
fbx_filename = f"{instance_name}_{armature.name}.fbx"
|
||||
filepath = os.path.join(stagingdir, fbx_filename)
|
||||
|
||||
override = plugin.create_blender_context(
|
||||
|
|
@ -178,7 +181,7 @@ class ExtractAnimationFBX(
|
|||
pair[1].user_clear()
|
||||
bpy.data.actions.remove(pair[1])
|
||||
|
||||
json_filename = f"{instance.name}.json"
|
||||
json_filename = f"{instance_name}.json"
|
||||
json_path = os.path.join(stagingdir, json_filename)
|
||||
|
||||
json_dict = {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from openpype.hosts.blender.api.pipeline import AVALON_PROPERTY
|
|||
class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||
"""Extract a layout."""
|
||||
|
||||
label = "Extract Layout"
|
||||
label = "Extract Layout (JSON)"
|
||||
hosts = ["blender"]
|
||||
families = ["layout"]
|
||||
optional = True
|
||||
|
|
@ -224,7 +224,11 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
json_data.append(json_element)
|
||||
|
||||
json_filename = "{}.json".format(instance.name)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
json_filename = f"{instance_name}.json"
|
||||
|
||||
json_path = os.path.join(stagingdir, json_filename)
|
||||
|
||||
with open(json_path, "w+") as file:
|
||||
|
|
|
|||
|
|
@ -51,7 +51,10 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# get output path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = instance.name
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
filename = f"{asset_name}_{subset}"
|
||||
|
||||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
self.log.debug(f"Outputting images to {path}")
|
||||
|
|
|
|||
|
|
@ -26,8 +26,15 @@ class ExtractThumbnail(publish.Extractor):
|
|||
def process(self, instance):
|
||||
self.log.debug("Extracting capture..")
|
||||
|
||||
if instance.data.get("thumbnailSource"):
|
||||
self.log.debug("Thumbnail source found, skipping...")
|
||||
return
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = instance.name
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
filename = f"{asset_name}_{subset}"
|
||||
|
||||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
self.log.debug(f"Outputting images to {path}")
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class IncrementWorkfileVersion(
|
|||
optional = True
|
||||
hosts = ["blender"]
|
||||
families = ["animation", "model", "rig", "action", "layout", "blendScene",
|
||||
"pointcache", "render"]
|
||||
"pointcache", "render.farm"]
|
||||
|
||||
def process(self, context):
|
||||
if not self.is_active(context.data):
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class ValidateDeadlinePublish(pyblish.api.InstancePlugin,
|
|||
"""
|
||||
|
||||
order = ValidateContentsOrder
|
||||
families = ["render.farm"]
|
||||
families = ["render"]
|
||||
hosts = ["blender"]
|
||||
label = "Validate Render Output for Deadline"
|
||||
optional = True
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
|
||||
from openpype.client import get_asset_name_identifier
|
||||
|
||||
|
||||
class CollectCelactionInstances(pyblish.api.ContextPlugin):
|
||||
""" Adds the celaction render instances """
|
||||
|
|
@ -17,8 +19,10 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
|
|||
asset_entity = context.data["assetEntity"]
|
||||
project_entity = context.data["projectEntity"]
|
||||
|
||||
asset_name = get_asset_name_identifier(asset_entity)
|
||||
|
||||
shared_instance_data = {
|
||||
"asset": asset_entity["name"],
|
||||
"asset": asset_name,
|
||||
"frameStart": asset_entity["data"]["frameStart"],
|
||||
"frameEnd": asset_entity["data"]["frameEnd"],
|
||||
"handleStart": asset_entity["data"]["handleStart"],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype.client import get_asset_name_identifier
|
||||
import openpype.hosts.flame.api as opfapi
|
||||
from openpype.hosts.flame.otio import flame_export
|
||||
from openpype.pipeline.create import get_subset_name
|
||||
|
|
@ -33,13 +34,15 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
|||
project_settings=context.data["project_settings"]
|
||||
)
|
||||
|
||||
asset_name = get_asset_name_identifier(asset_doc)
|
||||
|
||||
# adding otio timeline to context
|
||||
with opfapi.maintained_segment_selection(sequence) as selected_seg:
|
||||
otio_timeline = flame_export.create_otio_timeline(sequence)
|
||||
|
||||
instance_data = {
|
||||
"name": subset_name,
|
||||
"asset": asset_doc["name"],
|
||||
"asset": asset_name,
|
||||
"subset": subset_name,
|
||||
"family": "workfile",
|
||||
"families": []
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import os
|
||||
import sys
|
||||
|
||||
from qtpy import QtWidgets, QtCore, QtGui
|
||||
|
|
@ -18,6 +19,10 @@ from openpype.resources import get_openpype_icon_filepath
|
|||
from .pipeline import FusionEventHandler
|
||||
from .pulse import FusionPulse
|
||||
|
||||
|
||||
MENU_LABEL = os.environ["AVALON_LABEL"]
|
||||
|
||||
|
||||
self = sys.modules[__name__]
|
||||
self.menu = None
|
||||
|
||||
|
|
@ -26,7 +31,7 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
def __init__(self, *args, **kwargs):
|
||||
super(OpenPypeMenu, self).__init__(*args, **kwargs)
|
||||
|
||||
self.setObjectName("OpenPypeMenu")
|
||||
self.setObjectName(f"{MENU_LABEL}Menu")
|
||||
|
||||
icon_path = get_openpype_icon_filepath()
|
||||
icon = QtGui.QIcon(icon_path)
|
||||
|
|
@ -41,7 +46,7 @@ class OpenPypeMenu(QtWidgets.QWidget):
|
|||
| QtCore.Qt.WindowStaysOnTopHint
|
||||
)
|
||||
self.render_mode_widget = None
|
||||
self.setWindowTitle("OpenPype")
|
||||
self.setWindowTitle(MENU_LABEL)
|
||||
|
||||
asset_label = QtWidgets.QLabel("Context", self)
|
||||
asset_label.setStyleSheet(
|
||||
|
|
|
|||
60
openpype/hosts/fusion/deploy/ayon/Config/menu.fu
Normal file
60
openpype/hosts/fusion/deploy/ayon/Config/menu.fu
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
Action
|
||||
{
|
||||
ID = "AYON_Menu",
|
||||
Category = "AYON",
|
||||
Name = "AYON Menu",
|
||||
|
||||
Targets =
|
||||
{
|
||||
Composition =
|
||||
{
|
||||
Execute = _Lua [=[
|
||||
local scriptPath = app:MapPath("AYON:../MenuScripts/launch_menu.py")
|
||||
if bmd.fileexists(scriptPath) == false then
|
||||
print("[AYON Error] Can't run file: " .. scriptPath)
|
||||
else
|
||||
target:RunScript(scriptPath)
|
||||
end
|
||||
]=],
|
||||
},
|
||||
},
|
||||
},
|
||||
Action
|
||||
{
|
||||
ID = "AYON_Install_PySide2",
|
||||
Category = "AYON",
|
||||
Name = "Install PySide2",
|
||||
|
||||
Targets =
|
||||
{
|
||||
Composition =
|
||||
{
|
||||
Execute = _Lua [=[
|
||||
local scriptPath = app:MapPath("AYON:../MenuScripts/install_pyside2.py")
|
||||
if bmd.fileexists(scriptPath) == false then
|
||||
print("[AYON Error] Can't run file: " .. scriptPath)
|
||||
else
|
||||
target:RunScript(scriptPath)
|
||||
end
|
||||
]=],
|
||||
},
|
||||
},
|
||||
},
|
||||
Menus
|
||||
{
|
||||
Target = "ChildFrame",
|
||||
|
||||
Before "Help"
|
||||
{
|
||||
Sub "AYON"
|
||||
{
|
||||
"AYON_Menu{}",
|
||||
"_",
|
||||
Sub "Admin" {
|
||||
"AYON_Install_PySide2{}"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
19
openpype/hosts/fusion/deploy/ayon/fusion_shared.prefs
Normal file
19
openpype/hosts/fusion/deploy/ayon/fusion_shared.prefs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
Locked = true,
|
||||
Global = {
|
||||
Paths = {
|
||||
Map = {
|
||||
["AYON:"] = "$(OPENPYPE_FUSION)/deploy/ayon",
|
||||
["Config:"] = "UserPaths:Config;AYON:Config",
|
||||
["Scripts:"] = "UserPaths:Scripts;Reactor:System/Scripts",
|
||||
},
|
||||
},
|
||||
Script = {
|
||||
PythonVersion = 3,
|
||||
Python3Forced = true
|
||||
},
|
||||
UserInterface = {
|
||||
Language = "en_US"
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
Composition =
|
||||
{
|
||||
Execute = _Lua [=[
|
||||
local scriptPath = app:MapPath("OpenPype:MenuScripts/openpype_menu.py")
|
||||
local scriptPath = app:MapPath("OpenPype:../MenuScripts/launch_menu.py")
|
||||
if bmd.fileexists(scriptPath) == false then
|
||||
print("[OpenPype Error] Can't run file: " .. scriptPath)
|
||||
else
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
Composition =
|
||||
{
|
||||
Execute = _Lua [=[
|
||||
local scriptPath = app:MapPath("OpenPype:MenuScripts/install_pyside2.py")
|
||||
local scriptPath = app:MapPath("OpenPype:../MenuScripts/install_pyside2.py")
|
||||
if bmd.fileexists(scriptPath) == false then
|
||||
print("[OpenPype Error] Can't run file: " .. scriptPath)
|
||||
else
|
||||
|
|
@ -3,7 +3,7 @@ Locked = true,
|
|||
Global = {
|
||||
Paths = {
|
||||
Map = {
|
||||
["OpenPype:"] = "$(OPENPYPE_FUSION)/deploy",
|
||||
["OpenPype:"] = "$(OPENPYPE_FUSION)/deploy/openpype",
|
||||
["Config:"] = "UserPaths:Config;OpenPype:Config",
|
||||
["Scripts:"] = "UserPaths:Scripts;Reactor:System/Scripts",
|
||||
},
|
||||
|
|
@ -2,6 +2,7 @@ import os
|
|||
import shutil
|
||||
import platform
|
||||
from pathlib import Path
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.hosts.fusion import (
|
||||
FUSION_HOST_DIR,
|
||||
FUSION_VERSIONS_DICT,
|
||||
|
|
@ -161,6 +162,13 @@ class FusionCopyPrefsPrelaunch(PreLaunchHook):
|
|||
# profile directory variables to customize Fusion
|
||||
# to define where it can read custom scripts and tools from
|
||||
master_prefs_variable = f"FUSION{profile_version}_MasterPrefs"
|
||||
master_prefs = Path(FUSION_HOST_DIR, "deploy", "fusion_shared.prefs")
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
master_prefs = Path(
|
||||
FUSION_HOST_DIR, "deploy", "ayon", "fusion_shared.prefs")
|
||||
else:
|
||||
master_prefs = Path(
|
||||
FUSION_HOST_DIR, "deploy", "openpype", "fusion_shared.prefs")
|
||||
|
||||
self.log.info(f"Setting {master_prefs_variable}: {master_prefs}")
|
||||
self.launch_context.env[master_prefs_variable] = str(master_prefs)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from openpype.hosts.fusion.api import (
|
||||
get_current_comp
|
||||
)
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.pipeline import (
|
||||
AutoCreator,
|
||||
|
|
@ -68,6 +69,13 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
task_name = self.create_context.get_current_task_name()
|
||||
host_name = self.create_context.host_name
|
||||
|
||||
if existing_instance is None:
|
||||
existing_instance_asset = None
|
||||
elif AYON_SERVER_ENABLED:
|
||||
existing_instance_asset = existing_instance["folderPath"]
|
||||
else:
|
||||
existing_instance_asset = existing_instance["asset"]
|
||||
|
||||
if existing_instance is None:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
|
|
@ -75,10 +83,13 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
project_name, host_name
|
||||
)
|
||||
data = {
|
||||
"asset": asset_name,
|
||||
"task": task_name,
|
||||
"variant": self.default_variant
|
||||
}
|
||||
if AYON_SERVER_ENABLED:
|
||||
data["folderPath"] = asset_name
|
||||
else:
|
||||
data["asset"] = asset_name
|
||||
data.update(self.get_dynamic_data(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name, None
|
||||
|
|
@ -91,7 +102,7 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
self._add_instance_to_context(new_instance)
|
||||
|
||||
elif (
|
||||
existing_instance["asset"] != asset_name
|
||||
existing_instance_asset != asset_name
|
||||
or existing_instance["task"] != task_name
|
||||
):
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
|
|
@ -99,6 +110,9 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name
|
||||
)
|
||||
existing_instance["asset"] = asset_name
|
||||
if AYON_SERVER_ENABLED:
|
||||
existing_instance["folderPath"] = asset_name
|
||||
else:
|
||||
existing_instance["asset"] = asset_name
|
||||
existing_instance["task"] = task_name
|
||||
existing_instance["subset"] = subset_name
|
||||
|
|
|
|||
|
|
@ -25,20 +25,24 @@ def enabled_savers(comp, savers):
|
|||
"""
|
||||
passthrough_key = "TOOLB_PassThrough"
|
||||
original_states = {}
|
||||
enabled_save_names = {saver.Name for saver in savers}
|
||||
enabled_saver_names = {saver.Name for saver in savers}
|
||||
|
||||
all_savers = comp.GetToolList(False, "Saver").values()
|
||||
savers_by_name = {saver.Name: saver for saver in all_savers}
|
||||
|
||||
try:
|
||||
all_savers = comp.GetToolList(False, "Saver").values()
|
||||
for saver in all_savers:
|
||||
original_state = saver.GetAttrs()[passthrough_key]
|
||||
original_states[saver] = original_state
|
||||
original_states[saver.Name] = original_state
|
||||
|
||||
# The passthrough state we want to set (passthrough != enabled)
|
||||
state = saver.Name not in enabled_save_names
|
||||
state = saver.Name not in enabled_saver_names
|
||||
if state != original_state:
|
||||
saver.SetAttrs({passthrough_key: state})
|
||||
yield
|
||||
finally:
|
||||
for saver, original_state in original_states.items():
|
||||
for saver_name, original_state in original_states.items():
|
||||
saver = savers_by_name[saver_name]
|
||||
saver.SetAttrs({"TOOLB_PassThrough": original_state})
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ var LD_OPENHARMONY_PATH = System.getenv('LIB_OPENHARMONY_PATH');
|
|||
LD_OPENHARMONY_PATH = LD_OPENHARMONY_PATH + '/openHarmony.js';
|
||||
LD_OPENHARMONY_PATH = LD_OPENHARMONY_PATH.replace(/\\/g, "/");
|
||||
include(LD_OPENHARMONY_PATH);
|
||||
this.__proto__['$'] = $;
|
||||
//this.__proto__['$'] = $;
|
||||
|
||||
function Client() {
|
||||
var self = this;
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ class ExtractRender(pyblish.api.InstancePlugin):
|
|||
|
||||
args = [application_path, "-batch",
|
||||
"-frames", str(frame_start), str(frame_end),
|
||||
"-scene", scene_path]
|
||||
self.log.info(f"running [ {application_path} {' '.join(args)}")
|
||||
scene_path]
|
||||
self.log.info(f"running: {' '.join(args)}")
|
||||
proc = subprocess.Popen(
|
||||
args,
|
||||
stdout=subprocess.PIPE,
|
||||
|
|
|
|||
|
|
@ -95,18 +95,18 @@ def menu_install():
|
|||
|
||||
menu.addSeparator()
|
||||
|
||||
publish_action = menu.addAction("Publish...")
|
||||
publish_action.setIcon(QtGui.QIcon("icons:Output.png"))
|
||||
publish_action.triggered.connect(
|
||||
lambda *args: publish(hiero.ui.mainWindow())
|
||||
)
|
||||
|
||||
creator_action = menu.addAction("Create...")
|
||||
creator_action.setIcon(QtGui.QIcon("icons:CopyRectangle.png"))
|
||||
creator_action.triggered.connect(
|
||||
lambda: host_tools.show_creator(parent=main_window)
|
||||
)
|
||||
|
||||
publish_action = menu.addAction("Publish...")
|
||||
publish_action.setIcon(QtGui.QIcon("icons:Output.png"))
|
||||
publish_action.triggered.connect(
|
||||
lambda *args: publish(hiero.ui.mainWindow())
|
||||
)
|
||||
|
||||
loader_action = menu.addAction("Load...")
|
||||
loader_action.setIcon(QtGui.QIcon("icons:CopyRectangle.png"))
|
||||
loader_action.triggered.connect(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import qargparse
|
|||
from openpype.settings import get_current_project_settings
|
||||
from openpype.lib import Logger
|
||||
from openpype.pipeline import LoaderPlugin, LegacyCreator
|
||||
from openpype.pipeline.context_tools import get_current_project_asset
|
||||
from openpype.pipeline.load import get_representation_path_from_context
|
||||
from . import lib
|
||||
|
||||
|
|
@ -32,7 +31,7 @@ def load_stylesheet():
|
|||
class CreatorWidget(QtWidgets.QDialog):
|
||||
|
||||
# output items
|
||||
items = dict()
|
||||
items = {}
|
||||
|
||||
def __init__(self, name, info, ui_inputs, parent=None):
|
||||
super(CreatorWidget, self).__init__(parent)
|
||||
|
|
@ -494,9 +493,8 @@ class ClipLoader:
|
|||
joint `data` key with asset.data dict into the representation
|
||||
|
||||
"""
|
||||
asset_name = self.context["representation"]["context"]["asset"]
|
||||
asset_doc = get_current_project_asset(asset_name)
|
||||
log.debug("__ asset_doc: {}".format(pformat(asset_doc)))
|
||||
|
||||
asset_doc = self.context["asset"]
|
||||
self.data["assetData"] = asset_doc["data"]
|
||||
|
||||
def _make_track_item(self, source_bin_item, audio=False):
|
||||
|
|
@ -644,8 +642,8 @@ class PublishClip:
|
|||
Returns:
|
||||
hiero.core.TrackItem: hiero track item object with pype tag
|
||||
"""
|
||||
vertical_clip_match = dict()
|
||||
tag_data = dict()
|
||||
vertical_clip_match = {}
|
||||
tag_data = {}
|
||||
types = {
|
||||
"shot": "shot",
|
||||
"folder": "folder",
|
||||
|
|
@ -707,9 +705,10 @@ class PublishClip:
|
|||
self._create_parents()
|
||||
|
||||
def convert(self):
|
||||
|
||||
# solve track item data and add them to tag data
|
||||
self._convert_to_tag_data()
|
||||
tag_hierarchy_data = self._convert_to_tag_data()
|
||||
|
||||
self.tag_data.update(tag_hierarchy_data)
|
||||
|
||||
# if track name is in review track name and also if driving track name
|
||||
# is not in review track name: skip tag creation
|
||||
|
|
@ -723,16 +722,23 @@ class PublishClip:
|
|||
if self.rename:
|
||||
# rename track item
|
||||
self.track_item.setName(new_name)
|
||||
self.tag_data["asset"] = new_name
|
||||
self.tag_data["asset_name"] = new_name
|
||||
else:
|
||||
self.tag_data["asset"] = self.ti_name
|
||||
self.tag_data["asset_name"] = self.ti_name
|
||||
self.tag_data["hierarchyData"]["shot"] = self.ti_name
|
||||
|
||||
# AYON unique identifier
|
||||
folder_path = "/{}/{}".format(
|
||||
tag_hierarchy_data["hierarchy"],
|
||||
self.tag_data["asset_name"]
|
||||
)
|
||||
self.tag_data["folderPath"] = folder_path
|
||||
if self.tag_data["heroTrack"] and self.review_layer:
|
||||
self.tag_data.update({"reviewTrack": self.review_layer})
|
||||
else:
|
||||
self.tag_data.update({"reviewTrack": None})
|
||||
|
||||
# TODO: remove debug print
|
||||
log.debug("___ self.tag_data: {}".format(
|
||||
pformat(self.tag_data)
|
||||
))
|
||||
|
|
@ -891,7 +897,7 @@ class PublishClip:
|
|||
tag_hierarchy_data = hero_data
|
||||
|
||||
# add data to return data dict
|
||||
self.tag_data.update(tag_hierarchy_data)
|
||||
return tag_hierarchy_data
|
||||
|
||||
def _solve_tag_hierarchy_data(self, hierarchy_formatting_data):
|
||||
""" Solve tag data from hierarchy data and templates. """
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import json
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.client import get_asset_name_identifier
|
||||
|
||||
|
||||
class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
||||
"""Collect frames from tags.
|
||||
|
|
@ -99,6 +101,9 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
|
||||
# first collect all available subset tag frames
|
||||
subset_data = {}
|
||||
context_asset_doc = context.data["assetEntity"]
|
||||
context_asset_name = get_asset_name_identifier(context_asset_doc)
|
||||
|
||||
for tag_data in sequence_tags:
|
||||
frame = int(tag_data["start"])
|
||||
|
||||
|
|
@ -115,7 +120,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
subset_data[subset] = {
|
||||
"frames": [frame],
|
||||
"format": tag_data["format"],
|
||||
"asset": context.data["assetEntity"]["name"]
|
||||
"asset": context_asset_name
|
||||
}
|
||||
return subset_data
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import pyblish
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.pipeline.editorial import is_overlapping_otio_ranges
|
||||
|
||||
from openpype.hosts.hiero import api as phiero
|
||||
from openpype.hosts.hiero.api.otio import hiero_export
|
||||
import hiero
|
||||
|
||||
import hiero
|
||||
# # developer reload modules
|
||||
from pprint import pformat
|
||||
|
||||
|
|
@ -80,25 +83,24 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
if k not in ("id", "applieswhole", "label")
|
||||
})
|
||||
|
||||
asset = tag_data["asset"]
|
||||
asset, asset_name = self._get_asset_data(tag_data)
|
||||
|
||||
subset = tag_data["subset"]
|
||||
|
||||
# insert family into families
|
||||
family = tag_data["family"]
|
||||
families = [str(f) for f in tag_data["families"]]
|
||||
families.insert(0, str(family))
|
||||
|
||||
# form label
|
||||
label = asset
|
||||
if asset != clip_name:
|
||||
label = "{} -".format(asset)
|
||||
if asset_name != clip_name:
|
||||
label += " ({})".format(clip_name)
|
||||
label += " {}".format(subset)
|
||||
label += " {}".format("[" + ", ".join(families) + "]")
|
||||
|
||||
data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"label": label,
|
||||
"asset": asset,
|
||||
"asset_name": asset_name,
|
||||
"item": track_item,
|
||||
"families": families,
|
||||
"publish": tag_data["publish"],
|
||||
|
|
@ -176,9 +178,9 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
})
|
||||
|
||||
def create_shot_instance(self, context, **data):
|
||||
subset = "shotMain"
|
||||
master_layer = data.get("heroTrack")
|
||||
hierarchy_data = data.get("hierarchyData")
|
||||
asset = data.get("asset")
|
||||
item = data.get("item")
|
||||
clip_name = item.name()
|
||||
|
||||
|
|
@ -189,23 +191,21 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
return
|
||||
|
||||
asset = data["asset"]
|
||||
subset = "shotMain"
|
||||
asset_name = data["asset_name"]
|
||||
|
||||
# insert family into families
|
||||
family = "shot"
|
||||
|
||||
# form label
|
||||
label = asset
|
||||
if asset != clip_name:
|
||||
label = "{} -".format(asset)
|
||||
if asset_name != clip_name:
|
||||
label += " ({}) ".format(clip_name)
|
||||
label += " {}".format(subset)
|
||||
label += " [{}]".format(family)
|
||||
|
||||
data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"label": label,
|
||||
"subset": subset,
|
||||
"asset": asset,
|
||||
"family": family,
|
||||
"families": []
|
||||
})
|
||||
|
|
@ -215,7 +215,33 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
self.log.debug(
|
||||
"_ instance.data: {}".format(pformat(instance.data)))
|
||||
|
||||
def _get_asset_data(self, data):
|
||||
folder_path = data.pop("folderPath", None)
|
||||
|
||||
if data.get("asset_name"):
|
||||
asset_name = data["asset_name"]
|
||||
else:
|
||||
asset_name = data["asset"]
|
||||
|
||||
# backward compatibility for clip tags
|
||||
# which are missing folderPath key
|
||||
# TODO remove this in future versions
|
||||
if not folder_path:
|
||||
hierarchy_path = data["hierarchy"]
|
||||
folder_path = "/{}/{}".format(
|
||||
hierarchy_path,
|
||||
asset_name
|
||||
)
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset = folder_path
|
||||
else:
|
||||
asset = asset_name
|
||||
|
||||
return asset, asset_name
|
||||
|
||||
def create_audio_instance(self, context, **data):
|
||||
subset = "audioMain"
|
||||
master_layer = data.get("heroTrack")
|
||||
|
||||
if not master_layer:
|
||||
|
|
@ -230,23 +256,21 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
return
|
||||
|
||||
asset = data["asset"]
|
||||
subset = "audioMain"
|
||||
asset_name = data["asset_name"]
|
||||
|
||||
# insert family into families
|
||||
family = "audio"
|
||||
|
||||
# form label
|
||||
label = asset
|
||||
if asset != clip_name:
|
||||
label = "{} -".format(asset)
|
||||
if asset_name != clip_name:
|
||||
label += " ({}) ".format(clip_name)
|
||||
label += " {}".format(subset)
|
||||
label += " [{}]".format(family)
|
||||
|
||||
data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"label": label,
|
||||
"subset": subset,
|
||||
"asset": asset,
|
||||
"family": family,
|
||||
"families": ["clip"]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from qtpy.QtGui import QPixmap
|
|||
|
||||
import hiero.ui
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.hosts.hiero.api.otio import hiero_export
|
||||
|
||||
|
||||
|
|
@ -17,9 +18,11 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin):
|
|||
order = pyblish.api.CollectorOrder - 0.491
|
||||
|
||||
def process(self, context):
|
||||
|
||||
asset = context.data["asset"]
|
||||
subset = "workfile"
|
||||
asset_name = asset
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset_name = asset_name.split("/")[-1]
|
||||
|
||||
active_timeline = hiero.ui.activeSequence()
|
||||
project = active_timeline.project()
|
||||
fps = active_timeline.framerate().toFloat()
|
||||
|
|
@ -27,7 +30,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin):
|
|||
# adding otio timeline to context
|
||||
otio_timeline = hiero_export.create_otio_timeline()
|
||||
|
||||
# get workfile thumnail paths
|
||||
# get workfile thumbnail paths
|
||||
tmp_staging = tempfile.mkdtemp(prefix="pyblish_tmp_")
|
||||
thumbnail_name = "workfile_thumbnail.png"
|
||||
thumbnail_path = os.path.join(tmp_staging, thumbnail_name)
|
||||
|
|
@ -49,8 +52,8 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin):
|
|||
}
|
||||
|
||||
# get workfile paths
|
||||
curent_file = project.path()
|
||||
staging_dir, base_name = os.path.split(curent_file)
|
||||
current_file = project.path()
|
||||
staging_dir, base_name = os.path.split(current_file)
|
||||
|
||||
# creating workfile representation
|
||||
workfile_representation = {
|
||||
|
|
@ -59,13 +62,16 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin):
|
|||
'files': base_name,
|
||||
"stagingDir": staging_dir,
|
||||
}
|
||||
|
||||
family = "workfile"
|
||||
instance_data = {
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"asset": asset,
|
||||
"subset": "{}{}".format(asset, subset.capitalize()),
|
||||
"label": "{} - {}Main".format(
|
||||
asset, family),
|
||||
"name": "{}_{}".format(asset_name, family),
|
||||
"asset": context.data["asset"],
|
||||
# TODO use 'get_subset_name'
|
||||
"subset": "{}{}Main".format(asset_name, family.capitalize()),
|
||||
"item": project,
|
||||
"family": "workfile",
|
||||
"family": family,
|
||||
"families": [],
|
||||
"representations": [workfile_representation, thumb_representation]
|
||||
}
|
||||
|
|
@ -78,7 +84,7 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin):
|
|||
"activeProject": project,
|
||||
"activeTimeline": active_timeline,
|
||||
"otioTimeline": otio_timeline,
|
||||
"currentFile": curent_file,
|
||||
"currentFile": current_file,
|
||||
"colorspace": self.get_colorspace(project),
|
||||
"fps": fps
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from pyblish import api
|
||||
from openpype.client import get_assets
|
||||
|
||||
from openpype.client import get_assets, get_asset_name_identifier
|
||||
|
||||
|
||||
class CollectAssetBuilds(api.ContextPlugin):
|
||||
|
|
@ -19,10 +20,13 @@ class CollectAssetBuilds(api.ContextPlugin):
|
|||
def process(self, context):
|
||||
project_name = context.data["projectName"]
|
||||
asset_builds = {}
|
||||
for asset in get_assets(project_name):
|
||||
if asset["data"]["entityType"] == "AssetBuild":
|
||||
self.log.debug("Found \"{}\" in database.".format(asset))
|
||||
asset_builds[asset["name"]] = asset
|
||||
for asset_doc in get_assets(project_name):
|
||||
if asset_doc["data"].get("entityType") != "AssetBuild":
|
||||
continue
|
||||
|
||||
asset_name = get_asset_name_identifier(asset_doc)
|
||||
self.log.debug("Found \"{}\" in database.".format(asset_doc))
|
||||
asset_builds[asset_name] = asset_doc
|
||||
|
||||
for instance in context:
|
||||
if instance.data["family"] != "clip":
|
||||
|
|
@ -50,9 +54,7 @@ class CollectAssetBuilds(api.ContextPlugin):
|
|||
# Collect asset builds.
|
||||
data = {"assetbuilds": []}
|
||||
for name in asset_names:
|
||||
data["assetbuilds"].append(
|
||||
asset_builds[name]
|
||||
)
|
||||
data["assetbuilds"].append(asset_builds[name])
|
||||
self.log.debug(
|
||||
"Found asset builds: {}".format(data["assetbuilds"])
|
||||
)
|
||||
|
|
|
|||
|
|
@ -121,8 +121,8 @@ def get_id_required_nodes():
|
|||
return list(nodes)
|
||||
|
||||
|
||||
def get_output_parameter(node):
|
||||
"""Return the render output parameter name of the given node
|
||||
def get_export_parameter(node):
|
||||
"""Return the export output parameter of the given node
|
||||
|
||||
Example:
|
||||
root = hou.node("/obj")
|
||||
|
|
@ -137,13 +137,70 @@ def get_output_parameter(node):
|
|||
hou.Parm
|
||||
|
||||
"""
|
||||
node_type = node.type().description()
|
||||
|
||||
node_type = node.type().name()
|
||||
if node_type == "geometry":
|
||||
# Ensures the proper Take is selected for each ROP to retrieve the correct
|
||||
# ifd
|
||||
try:
|
||||
rop_take = hou.takes.findTake(node.parm("take").eval())
|
||||
if rop_take is not None:
|
||||
hou.takes.setCurrentTake(rop_take)
|
||||
except AttributeError:
|
||||
# hou object doesn't always have the 'takes' attribute
|
||||
pass
|
||||
|
||||
if node_type == "Mantra" and node.parm("soho_outputmode").eval():
|
||||
return node.parm("soho_diskfile")
|
||||
elif node_type == "Alfred":
|
||||
return node.parm("alf_diskfile")
|
||||
elif (node_type == "RenderMan" or node_type == "RenderMan RIS"):
|
||||
pre_ris22 = node.parm("rib_outputmode") and \
|
||||
node.parm("rib_outputmode").eval()
|
||||
ris22 = node.parm("diskfile") and node.parm("diskfile").eval()
|
||||
if pre_ris22 or ris22:
|
||||
return node.parm("soho_diskfile")
|
||||
elif node_type == "Redshift" and node.parm("RS_archive_enable").eval():
|
||||
return node.parm("RS_archive_file")
|
||||
elif node_type == "Wedge" and node.parm("driver").eval():
|
||||
return get_export_parameter(node.node(node.parm("driver").eval()))
|
||||
elif node_type == "Arnold":
|
||||
return node.parm("ar_ass_file")
|
||||
elif node_type == "Alembic" and node.parm("use_sop_path").eval():
|
||||
return node.parm("sop_path")
|
||||
elif node_type == "Shotgun Mantra" and node.parm("soho_outputmode").eval():
|
||||
return node.parm("sgtk_soho_diskfile")
|
||||
elif node_type == "Shotgun Alembic" and node.parm("use_sop_path").eval():
|
||||
return node.parm("sop_path")
|
||||
elif node.type().nameWithCategory() == "Driver/vray_renderer":
|
||||
return node.parm("render_export_filepath")
|
||||
|
||||
raise TypeError("Node type '%s' not supported" % node_type)
|
||||
|
||||
|
||||
def get_output_parameter(node):
|
||||
"""Return the render output parameter of the given node
|
||||
|
||||
Example:
|
||||
root = hou.node("/obj")
|
||||
my_alembic_node = root.createNode("alembic")
|
||||
get_output_parameter(my_alembic_node)
|
||||
# Result: "output"
|
||||
|
||||
Args:
|
||||
node(hou.Node): node instance
|
||||
|
||||
Returns:
|
||||
hou.Parm
|
||||
|
||||
"""
|
||||
node_type = node.type().description()
|
||||
category = node.type().category().name()
|
||||
|
||||
# Figure out which type of node is being rendered
|
||||
if node_type == "Geometry" or node_type == "Filmbox FBX" or \
|
||||
(node_type == "ROP Output Driver" and category == "Sop"):
|
||||
return node.parm("sopoutput")
|
||||
elif node_type == "alembic":
|
||||
return node.parm("filename")
|
||||
elif node_type == "comp":
|
||||
elif node_type == "Composite":
|
||||
return node.parm("copoutput")
|
||||
elif node_type == "opengl":
|
||||
return node.parm("picture")
|
||||
|
|
@ -152,6 +209,17 @@ def get_output_parameter(node):
|
|||
return node.parm("ar_ass_file")
|
||||
elif node_type == "Redshift_Proxy_Output":
|
||||
return node.parm("RS_archive_file")
|
||||
elif node_type == "ifd":
|
||||
if node.evalParm("soho_outputmode"):
|
||||
return node.parm("soho_diskfile")
|
||||
elif node_type == "Octane":
|
||||
return node.parm("HO_img_fileName")
|
||||
elif node_type == "Fetch":
|
||||
inner_node = node.node(node.parm("source").eval())
|
||||
if inner_node:
|
||||
return get_output_parameter(inner_node)
|
||||
elif node.type().nameWithCategory() == "Driver/vray_renderer":
|
||||
return node.parm("SettingsOutput_img_file_path")
|
||||
|
||||
raise TypeError("Node type '%s' not supported" % node_type)
|
||||
|
||||
|
|
|
|||
|
|
@ -66,10 +66,6 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
|||
register_event_callback("new", on_new)
|
||||
|
||||
self._has_been_setup = True
|
||||
# add houdini vendor packages
|
||||
hou_pythonpath = os.path.join(HOUDINI_HOST_DIR, "vendor")
|
||||
|
||||
sys.path.append(hou_pythonpath)
|
||||
|
||||
# Set asset settings for the empty scene directly after launch of
|
||||
# Houdini so it initializes into the correct scene FPS,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from abc import (
|
|||
)
|
||||
import six
|
||||
import hou
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.pipeline import (
|
||||
CreatorError,
|
||||
LegacyCreator,
|
||||
|
|
@ -142,12 +144,13 @@ class HoudiniCreatorBase(object):
|
|||
|
||||
@staticmethod
|
||||
def create_instance_node(
|
||||
node_name, parent,
|
||||
node_type="geometry"):
|
||||
asset_name, node_name, parent, node_type="geometry"
|
||||
):
|
||||
# type: (str, str, str) -> hou.Node
|
||||
"""Create node representing instance.
|
||||
|
||||
Arguments:
|
||||
asset_name (str): Asset name.
|
||||
node_name (str): Name of the new node.
|
||||
parent (str): Name of the parent node.
|
||||
node_type (str, optional): Type of the node.
|
||||
|
|
@ -182,8 +185,13 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
|
|||
if node_type is None:
|
||||
node_type = "geometry"
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset_name = instance_data["folderPath"]
|
||||
else:
|
||||
asset_name = instance_data["asset"]
|
||||
|
||||
instance_node = self.create_instance_node(
|
||||
subset_name, "/out", node_type)
|
||||
asset_name, subset_name, "/out", node_type)
|
||||
|
||||
self.customize_node_look(instance_node)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Creator plugin for creating Arnold ASS files."""
|
||||
from openpype.hosts.houdini.api import plugin
|
||||
from openpype.lib import BoolDef
|
||||
|
||||
|
||||
class CreateArnoldAss(plugin.HoudiniCreator):
|
||||
|
|
@ -21,6 +22,9 @@ class CreateArnoldAss(plugin.HoudiniCreator):
|
|||
|
||||
instance_data.pop("active", None)
|
||||
instance_data.update({"node_type": "arnold"})
|
||||
creator_attributes = instance_data.setdefault(
|
||||
"creator_attributes", dict())
|
||||
creator_attributes["farm"] = pre_create_data["farm"]
|
||||
|
||||
instance = super(CreateArnoldAss, self).create(
|
||||
subset_name,
|
||||
|
|
@ -52,3 +56,15 @@ class CreateArnoldAss(plugin.HoudiniCreator):
|
|||
# Lock any parameters in this list
|
||||
to_lock = ["ar_ass_export_enable", "family", "id"]
|
||||
self.lock_parameters(instance_node, to_lock)
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
return [
|
||||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=False)
|
||||
]
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
attrs = super().get_pre_create_attr_defs()
|
||||
# Use same attributes as for instance attributes
|
||||
return attrs + self.get_instance_attr_defs()
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
# Default extension
|
||||
ext = "exr"
|
||||
|
||||
# Default to split export and render jobs
|
||||
export_job = True
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
import hou
|
||||
|
||||
|
|
@ -48,6 +51,15 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
"ar_exr_half_precision": 1 # half precision
|
||||
}
|
||||
|
||||
if pre_create_data.get("export_job"):
|
||||
ass_filepath = \
|
||||
"{export_dir}{subset_name}/{subset_name}.$F4.ass".format(
|
||||
export_dir=hou.text.expandString("$HIP/pyblish/ass/"),
|
||||
subset_name=subset_name,
|
||||
)
|
||||
parms["ar_ass_export_enable"] = 1
|
||||
parms["ar_ass_file"] = ass_filepath
|
||||
|
||||
instance_node.setParms(parms)
|
||||
|
||||
# Lock any parameters in this list
|
||||
|
|
@ -66,6 +78,9 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=True),
|
||||
BoolDef("export_job",
|
||||
label="Split export and render jobs",
|
||||
default=self.export_job),
|
||||
EnumDef("image_format",
|
||||
image_format_enum,
|
||||
default=self.ext,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
"""Creator plugin for creating pointcache bgeo files."""
|
||||
from openpype.hosts.houdini.api import plugin
|
||||
from openpype.pipeline import CreatedInstance, CreatorError
|
||||
from openpype.lib import EnumDef
|
||||
import hou
|
||||
from openpype.lib import EnumDef, BoolDef
|
||||
|
||||
|
||||
class CreateBGEO(plugin.HoudiniCreator):
|
||||
|
|
@ -18,6 +18,9 @@ class CreateBGEO(plugin.HoudiniCreator):
|
|||
instance_data.pop("active", None)
|
||||
|
||||
instance_data.update({"node_type": "geometry"})
|
||||
creator_attributes = instance_data.setdefault(
|
||||
"creator_attributes", dict())
|
||||
creator_attributes["farm"] = pre_create_data["farm"]
|
||||
|
||||
instance = super(CreateBGEO, self).create(
|
||||
subset_name,
|
||||
|
|
@ -58,6 +61,13 @@ class CreateBGEO(plugin.HoudiniCreator):
|
|||
|
||||
instance_node.setParms(parms)
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
return [
|
||||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=False)
|
||||
]
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
attrs = super().get_pre_create_attr_defs()
|
||||
bgeo_enum = [
|
||||
|
|
@ -89,7 +99,7 @@ class CreateBGEO(plugin.HoudiniCreator):
|
|||
|
||||
return attrs + [
|
||||
EnumDef("bgeo_type", bgeo_enum, label="BGEO Options"),
|
||||
]
|
||||
] + self.get_instance_attr_defs()
|
||||
|
||||
def get_network_categories(self):
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ class CreateHDA(plugin.HoudiniCreator):
|
|||
icon = "gears"
|
||||
maintain_selection = False
|
||||
|
||||
def _check_existing(self, subset_name):
|
||||
def _check_existing(self, asset_name, subset_name):
|
||||
# type: (str) -> bool
|
||||
"""Check if existing subset name versions already exists."""
|
||||
# Get all subsets of the current asset
|
||||
project_name = self.project_name
|
||||
asset_doc = get_asset_by_name(
|
||||
project_name, self.data["asset"], fields=["_id"]
|
||||
project_name, asset_name, fields=["_id"]
|
||||
)
|
||||
subset_docs = get_subsets(
|
||||
project_name, asset_ids=[asset_doc["_id"]], fields=["name"]
|
||||
|
|
@ -35,7 +35,8 @@ class CreateHDA(plugin.HoudiniCreator):
|
|||
return subset_name.lower() in existing_subset_names_low
|
||||
|
||||
def create_instance_node(
|
||||
self, node_name, parent, node_type="geometry"):
|
||||
self, asset_name, node_name, parent, node_type="geometry"
|
||||
):
|
||||
|
||||
parent_node = hou.node("/obj")
|
||||
if self.selected_nodes:
|
||||
|
|
@ -61,7 +62,7 @@ class CreateHDA(plugin.HoudiniCreator):
|
|||
hda_file_name="$HIP/{}.hda".format(node_name)
|
||||
)
|
||||
hda_node.layoutChildren()
|
||||
elif self._check_existing(node_name):
|
||||
elif self._check_existing(asset_name, node_name):
|
||||
raise plugin.OpenPypeCreatorError(
|
||||
("subset {} is already published with different HDA"
|
||||
"definition.").format(node_name))
|
||||
|
|
|
|||
56
openpype/hosts/houdini/plugins/create/create_mantra_ifd.py
Normal file
56
openpype/hosts/houdini/plugins/create/create_mantra_ifd.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Creator plugin for creating pointcache alembics."""
|
||||
from openpype.hosts.houdini.api import plugin
|
||||
from openpype.pipeline import CreatedInstance
|
||||
from openpype.lib import BoolDef
|
||||
|
||||
|
||||
class CreateMantraIFD(plugin.HoudiniCreator):
|
||||
"""Mantra .ifd Archive"""
|
||||
identifier = "io.openpype.creators.houdini.mantraifd"
|
||||
label = "Mantra IFD"
|
||||
family = "mantraifd"
|
||||
icon = "gears"
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
import hou
|
||||
instance_data.pop("active", None)
|
||||
instance_data.update({"node_type": "ifd"})
|
||||
creator_attributes = instance_data.setdefault(
|
||||
"creator_attributes", dict())
|
||||
creator_attributes["farm"] = pre_create_data["farm"]
|
||||
instance = super(CreateMantraIFD, self).create(
|
||||
subset_name,
|
||||
instance_data,
|
||||
pre_create_data) # type: CreatedInstance
|
||||
|
||||
instance_node = hou.node(instance.get("instance_node"))
|
||||
|
||||
filepath = "{}{}".format(
|
||||
hou.text.expandString("$HIP/pyblish/"),
|
||||
"{}.$F4.ifd".format(subset_name))
|
||||
parms = {
|
||||
# Render frame range
|
||||
"trange": 1,
|
||||
# Arnold ROP settings
|
||||
"soho_diskfile": filepath,
|
||||
"soho_outputmode": 1
|
||||
}
|
||||
|
||||
instance_node.setParms(parms)
|
||||
|
||||
# Lock any parameters in this list
|
||||
to_lock = ["soho_outputmode", "family", "id"]
|
||||
self.lock_parameters(instance_node, to_lock)
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
return [
|
||||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=False)
|
||||
]
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
attrs = super().get_pre_create_attr_defs()
|
||||
# Use same attributes as for instance attributes
|
||||
return attrs + self.get_instance_attr_defs()
|
||||
|
|
@ -12,6 +12,9 @@ class CreateMantraROP(plugin.HoudiniCreator):
|
|||
family = "mantra_rop"
|
||||
icon = "magic"
|
||||
|
||||
# Default to split export and render jobs
|
||||
export_job = True
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
import hou # noqa
|
||||
|
||||
|
|
@ -44,6 +47,15 @@ class CreateMantraROP(plugin.HoudiniCreator):
|
|||
"vm_picture": filepath,
|
||||
}
|
||||
|
||||
if pre_create_data.get("export_job"):
|
||||
ifd_filepath = \
|
||||
"{export_dir}{subset_name}/{subset_name}.$F4.ifd".format(
|
||||
export_dir=hou.text.expandString("$HIP/pyblish/ifd/"),
|
||||
subset_name=subset_name,
|
||||
)
|
||||
parms["soho_outputmode"] = 1
|
||||
parms["soho_diskfile"] = ifd_filepath
|
||||
|
||||
if self.selected_nodes:
|
||||
# If camera found in selection
|
||||
# we will use as render camera
|
||||
|
|
@ -78,6 +90,9 @@ class CreateMantraROP(plugin.HoudiniCreator):
|
|||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=True),
|
||||
BoolDef("export_job",
|
||||
label="Split export and render jobs",
|
||||
default=self.export_job),
|
||||
EnumDef("image_format",
|
||||
image_format_enum,
|
||||
default="exr",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Creator plugin for creating pointcache alembics."""
|
||||
from openpype.hosts.houdini.api import plugin
|
||||
from openpype.lib import BoolDef
|
||||
|
||||
import hou
|
||||
|
||||
|
||||
|
||||
class CreatePointCache(plugin.HoudiniCreator):
|
||||
"""Alembic ROP to pointcache"""
|
||||
identifier = "io.openpype.creators.houdini.pointcache"
|
||||
|
|
@ -15,6 +17,9 @@ class CreatePointCache(plugin.HoudiniCreator):
|
|||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
instance_data.pop("active", None)
|
||||
instance_data.update({"node_type": "alembic"})
|
||||
creator_attributes = instance_data.setdefault(
|
||||
"creator_attributes", dict())
|
||||
creator_attributes["farm"] = pre_create_data["farm"]
|
||||
|
||||
instance = super(CreatePointCache, self).create(
|
||||
subset_name,
|
||||
|
|
@ -105,3 +110,15 @@ class CreatePointCache(plugin.HoudiniCreator):
|
|||
else:
|
||||
return min(outputs,
|
||||
key=lambda node: node.evalParm('outputidx'))
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
return [
|
||||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=False)
|
||||
]
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
attrs = super().get_pre_create_attr_defs()
|
||||
# Use same attributes as for instance attributes
|
||||
return attrs + self.get_instance_attr_defs()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"""Creator plugin for creating Redshift proxies."""
|
||||
from openpype.hosts.houdini.api import plugin
|
||||
import hou
|
||||
from openpype.lib import BoolDef
|
||||
|
||||
|
||||
class CreateRedshiftProxy(plugin.HoudiniCreator):
|
||||
|
|
@ -24,6 +25,9 @@ class CreateRedshiftProxy(plugin.HoudiniCreator):
|
|||
# TODO: Somehow enforce so that it only shows the original limited
|
||||
# attributes of the Redshift_Proxy_Output node type
|
||||
instance_data.update({"node_type": "Redshift_Proxy_Output"})
|
||||
creator_attributes = instance_data.setdefault(
|
||||
"creator_attributes", dict())
|
||||
creator_attributes["farm"] = pre_create_data["farm"]
|
||||
|
||||
instance = super(CreateRedshiftProxy, self).create(
|
||||
subset_name,
|
||||
|
|
@ -50,3 +54,15 @@ class CreateRedshiftProxy(plugin.HoudiniCreator):
|
|||
hou.ropNodeTypeCategory(),
|
||||
hou.sopNodeTypeCategory()
|
||||
]
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
return [
|
||||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=False)
|
||||
]
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
attrs = super().get_pre_create_attr_defs()
|
||||
# Use same attributes as for instance attributes
|
||||
return attrs + self.get_instance_attr_defs()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
"""Creator plugin for creating VDB Caches."""
|
||||
from openpype.hosts.houdini.api import plugin
|
||||
from openpype.pipeline import CreatedInstance
|
||||
from openpype.lib import BoolDef
|
||||
|
||||
import hou
|
||||
|
||||
|
|
@ -19,15 +20,20 @@ class CreateVDBCache(plugin.HoudiniCreator):
|
|||
|
||||
instance_data.pop("active", None)
|
||||
instance_data.update({"node_type": "geometry"})
|
||||
|
||||
creator_attributes = instance_data.setdefault(
|
||||
"creator_attributes", dict())
|
||||
creator_attributes["farm"] = pre_create_data["farm"]
|
||||
instance = super(CreateVDBCache, self).create(
|
||||
subset_name,
|
||||
instance_data,
|
||||
pre_create_data) # type: CreatedInstance
|
||||
|
||||
instance_node = hou.node(instance.get("instance_node"))
|
||||
file_path = "{}{}".format(
|
||||
hou.text.expandString("$HIP/pyblish/"),
|
||||
"{}.$F4.vdb".format(subset_name))
|
||||
parms = {
|
||||
"sopoutput": "$HIP/pyblish/{}.$F4.vdb".format(subset_name),
|
||||
"sopoutput": file_path,
|
||||
"initsim": True,
|
||||
"trange": 1
|
||||
}
|
||||
|
|
@ -103,3 +109,15 @@ class CreateVDBCache(plugin.HoudiniCreator):
|
|||
else:
|
||||
return min(outputs,
|
||||
key=lambda node: node.evalParm('outputidx'))
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
return [
|
||||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=False)
|
||||
]
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
attrs = super().get_pre_create_attr_defs()
|
||||
# Use same attributes as for instance attributes
|
||||
return attrs + self.get_instance_attr_defs()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ class CreateVrayROP(plugin.HoudiniCreator):
|
|||
icon = "magic"
|
||||
ext = "exr"
|
||||
|
||||
# Default to split export and render jobs
|
||||
export_job = True
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
|
||||
instance_data.pop("active", None)
|
||||
|
|
@ -52,6 +55,17 @@ class CreateVrayROP(plugin.HoudiniCreator):
|
|||
"SettingsEXR_bits_per_channel": "16" # half precision
|
||||
}
|
||||
|
||||
if pre_create_data.get("export_job"):
|
||||
scene_filepath = \
|
||||
"{export_dir}{subset_name}/{subset_name}.$F4.vrscene".format(
|
||||
export_dir=hou.text.expandString("$HIP/pyblish/vrscene/"),
|
||||
subset_name=subset_name,
|
||||
)
|
||||
# Setting render_export_mode to "2" because that's for
|
||||
# "Export only" ("1" is for "Export & Render")
|
||||
parms["render_export_mode"] = "2"
|
||||
parms["render_export_filepath"] = scene_filepath
|
||||
|
||||
if self.selected_nodes:
|
||||
# set up the render camera from the selected node
|
||||
camera = None
|
||||
|
|
@ -140,6 +154,9 @@ class CreateVrayROP(plugin.HoudiniCreator):
|
|||
BoolDef("farm",
|
||||
label="Submitting to Farm",
|
||||
default=True),
|
||||
BoolDef("export_job",
|
||||
label="Split export and render jobs",
|
||||
default=self.export_job),
|
||||
EnumDef("image_format",
|
||||
image_format_enum,
|
||||
default=self.ext,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Creator plugin for creating workfiles."""
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.hosts.houdini.api import plugin
|
||||
from openpype.hosts.houdini.api.lib import read, imprint
|
||||
from openpype.hosts.houdini.api.pipeline import CONTEXT_CONTAINER
|
||||
|
|
@ -30,16 +31,27 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator):
|
|||
task_name = self.create_context.get_current_task_name()
|
||||
host_name = self.host_name
|
||||
|
||||
if current_instance is None:
|
||||
current_instance_asset = None
|
||||
elif AYON_SERVER_ENABLED:
|
||||
current_instance_asset = current_instance["folderPath"]
|
||||
else:
|
||||
current_instance_asset = current_instance["asset"]
|
||||
|
||||
if current_instance is None:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
variant, task_name, asset_doc, project_name, host_name
|
||||
)
|
||||
data = {
|
||||
"asset": asset_name,
|
||||
"task": task_name,
|
||||
"variant": variant
|
||||
}
|
||||
if AYON_SERVER_ENABLED:
|
||||
data["folderPath"] = asset_name
|
||||
else:
|
||||
data["asset"] = asset_name
|
||||
|
||||
data.update(
|
||||
self.get_dynamic_data(
|
||||
variant, task_name, asset_doc,
|
||||
|
|
@ -51,15 +63,18 @@ class CreateWorkfile(plugin.HoudiniCreatorBase, AutoCreator):
|
|||
)
|
||||
self._add_instance_to_context(current_instance)
|
||||
elif (
|
||||
current_instance["asset"] != asset_name
|
||||
or current_instance["task"] != task_name
|
||||
current_instance_asset != asset_name
|
||||
or current_instance["task"] != task_name
|
||||
):
|
||||
# Update instance context if is not the same
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
variant, task_name, asset_doc, project_name, host_name
|
||||
)
|
||||
current_instance["asset"] = asset_name
|
||||
if AYON_SERVER_ENABLED:
|
||||
current_instance["folderPath"] = asset_name
|
||||
else:
|
||||
current_instance["asset"] = asset_name
|
||||
current_instance["task"] = task_name
|
||||
current_instance["subset"] = subset_name
|
||||
|
||||
|
|
|
|||
|
|
@ -40,6 +40,25 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin):
|
|||
default_prefix = evalParmNoFrame(rop, "ar_picture")
|
||||
render_products = []
|
||||
|
||||
# Store whether we are splitting the render job (export + render)
|
||||
export_job = bool(rop.parm("ar_ass_export_enable").eval())
|
||||
instance.data["exportJob"] = export_job
|
||||
export_prefix = None
|
||||
export_products = []
|
||||
if export_job:
|
||||
export_prefix = evalParmNoFrame(
|
||||
rop, "ar_ass_file", pad_character="0"
|
||||
)
|
||||
beauty_export_product = self.get_render_product_name(
|
||||
prefix=export_prefix,
|
||||
suffix=None)
|
||||
export_products.append(beauty_export_product)
|
||||
self.log.debug(
|
||||
"Found export product: {}".format(beauty_export_product)
|
||||
)
|
||||
instance.data["ifdFile"] = beauty_export_product
|
||||
instance.data["exportFiles"] = list(export_products)
|
||||
|
||||
# Default beauty AOV
|
||||
beauty_product = self.get_render_product_name(prefix=default_prefix,
|
||||
suffix=None)
|
||||
|
|
|
|||
75
openpype/hosts/houdini/plugins/publish/collect_cache_farm.py
Normal file
75
openpype/hosts/houdini/plugins/publish/collect_cache_farm.py
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
import hou
|
||||
from openpype.hosts.houdini.api import lib
|
||||
|
||||
|
||||
class CollectDataforCache(pyblish.api.InstancePlugin):
|
||||
"""Collect data for caching to Deadline."""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.04
|
||||
families = ["ass", "pointcache",
|
||||
"mantraifd", "redshiftproxy",
|
||||
"vdbcache"]
|
||||
hosts = ["houdini"]
|
||||
targets = ["local", "remote"]
|
||||
label = "Collect Data for Cache"
|
||||
|
||||
def process(self, instance):
|
||||
creator_attribute = instance.data["creator_attributes"]
|
||||
farm_enabled = creator_attribute["farm"]
|
||||
instance.data["farm"] = farm_enabled
|
||||
if not farm_enabled:
|
||||
self.log.debug("Caching on farm is disabled. "
|
||||
"Skipping farm collecting.")
|
||||
return
|
||||
# Why do we need this particular collector to collect the expected
|
||||
# output files from a ROP node. Don't we have a dedicated collector
|
||||
# for that yet?
|
||||
# Collect expected files
|
||||
ropnode = hou.node(instance.data["instance_node"])
|
||||
output_parm = lib.get_output_parameter(ropnode)
|
||||
expected_filepath = output_parm.eval()
|
||||
instance.data.setdefault("files", list())
|
||||
instance.data.setdefault("expectedFiles", list())
|
||||
if instance.data.get("frames"):
|
||||
files = self.get_files(instance, expected_filepath)
|
||||
# list of files
|
||||
instance.data["files"].extend(files)
|
||||
else:
|
||||
# single file
|
||||
instance.data["files"].append(output_parm.eval())
|
||||
cache_files = {"_": instance.data["files"]}
|
||||
# Convert instance family to pointcache if it is bgeo or abc
|
||||
# because ???
|
||||
for family in instance.data["families"]:
|
||||
if family == "bgeo" or "abc":
|
||||
instance.data["family"] = "pointcache"
|
||||
break
|
||||
instance.data.update({
|
||||
"plugin": "Houdini",
|
||||
"publish": True
|
||||
})
|
||||
instance.data["families"].append("publish.hou")
|
||||
instance.data["expectedFiles"].append(cache_files)
|
||||
|
||||
self.log.debug("{}".format(instance.data))
|
||||
|
||||
def get_files(self, instance, output_parm):
|
||||
"""Get the files with the frame range data
|
||||
|
||||
Args:
|
||||
instance (_type_): instance
|
||||
output_parm (_type_): path of output parameter
|
||||
|
||||
Returns:
|
||||
files: a list of files
|
||||
"""
|
||||
directory = os.path.dirname(output_parm)
|
||||
|
||||
files = [
|
||||
os.path.join(directory, frame).replace("\\", "/")
|
||||
for frame in instance.data["frames"]
|
||||
]
|
||||
|
||||
return files
|
||||
39
openpype/hosts/houdini/plugins/publish/collect_chunk_size.py
Normal file
39
openpype/hosts/houdini/plugins/publish/collect_chunk_size.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import pyblish.api
|
||||
from openpype.lib import NumberDef
|
||||
from openpype.pipeline import OpenPypePyblishPluginMixin
|
||||
|
||||
|
||||
class CollectChunkSize(pyblish.api.InstancePlugin,
|
||||
OpenPypePyblishPluginMixin):
|
||||
"""Collect chunk size for cache submission to Deadline."""
|
||||
|
||||
order = pyblish.api.CollectorOrder + 0.05
|
||||
families = ["ass", "pointcache",
|
||||
"vdbcache", "mantraifd",
|
||||
"redshiftproxy"]
|
||||
hosts = ["houdini"]
|
||||
targets = ["local", "remote"]
|
||||
label = "Collect Chunk Size"
|
||||
chunkSize = 999999
|
||||
|
||||
def process(self, instance):
|
||||
# need to get the chunk size info from the setting
|
||||
attr_values = self.get_attr_values_from_data(instance.data)
|
||||
instance.data["chunkSize"] = attr_values.get("chunkSize")
|
||||
|
||||
@classmethod
|
||||
def apply_settings(cls, project_settings):
|
||||
project_setting = project_settings["houdini"]["publish"]["CollectChunkSize"] # noqa
|
||||
cls.chunkSize = project_setting["chunk_size"]
|
||||
|
||||
@classmethod
|
||||
def get_attribute_defs(cls):
|
||||
return [
|
||||
NumberDef("chunkSize",
|
||||
minimum=1,
|
||||
maximum=999999,
|
||||
decimals=0,
|
||||
default=cls.chunkSize,
|
||||
label="Frame Per Task")
|
||||
|
||||
]
|
||||
|
|
@ -16,7 +16,8 @@ class CollectFrames(pyblish.api.InstancePlugin):
|
|||
order = pyblish.api.CollectorOrder + 0.1
|
||||
label = "Collect Frames"
|
||||
families = ["vdbcache", "imagesequence", "ass",
|
||||
"redshiftproxy", "review", "bgeo"]
|
||||
"mantraifd", "redshiftproxy", "review",
|
||||
"bgeo"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
|
|
|
|||
|
|
@ -44,6 +44,25 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin):
|
|||
default_prefix = evalParmNoFrame(rop, "vm_picture")
|
||||
render_products = []
|
||||
|
||||
# Store whether we are splitting the render job (export + render)
|
||||
export_job = bool(rop.parm("soho_outputmode").eval())
|
||||
instance.data["exportJob"] = export_job
|
||||
export_prefix = None
|
||||
export_products = []
|
||||
if export_job:
|
||||
export_prefix = evalParmNoFrame(
|
||||
rop, "soho_diskfile", pad_character="0"
|
||||
)
|
||||
beauty_export_product = self.get_render_product_name(
|
||||
prefix=export_prefix,
|
||||
suffix=None)
|
||||
export_products.append(beauty_export_product)
|
||||
self.log.debug(
|
||||
"Found export product: {}".format(beauty_export_product)
|
||||
)
|
||||
instance.data["ifdFile"] = beauty_export_product
|
||||
instance.data["exportFiles"] = list(export_products)
|
||||
|
||||
# Default beauty AOV
|
||||
beauty_product = self.get_render_product_name(
|
||||
prefix=default_prefix, suffix=None
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype.client import get_subset_by_name, get_asset_by_name
|
||||
from openpype.client import (
|
||||
get_subset_by_name,
|
||||
get_asset_by_name,
|
||||
get_asset_name_identifier,
|
||||
)
|
||||
import openpype.lib.usdlib as usdlib
|
||||
|
||||
|
||||
|
|
@ -51,8 +55,9 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin):
|
|||
self.log.debug("Add bootstrap for: %s" % bootstrap)
|
||||
|
||||
project_name = instance.context.data["projectName"]
|
||||
asset = get_asset_by_name(project_name, instance.data["asset"])
|
||||
assert asset, "Asset must exist: %s" % asset
|
||||
asset_name = instance.data["asset"]
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
assert asset_doc, "Asset must exist: %s" % asset_name
|
||||
|
||||
# Check which are not about to be created and don't exist yet
|
||||
required = {"shot": ["usdShot"], "asset": ["usdAsset"]}.get(bootstrap)
|
||||
|
|
@ -67,19 +72,21 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin):
|
|||
required += list(layers)
|
||||
|
||||
self.log.debug("Checking required bootstrap: %s" % required)
|
||||
for subset in required:
|
||||
if self._subset_exists(project_name, instance, subset, asset):
|
||||
for subset_name in required:
|
||||
if self._subset_exists(
|
||||
project_name, instance, subset_name, asset_doc
|
||||
):
|
||||
continue
|
||||
|
||||
self.log.debug(
|
||||
"Creating {0} USD bootstrap: {1} {2}".format(
|
||||
bootstrap, asset["name"], subset
|
||||
bootstrap, asset_name, subset_name
|
||||
)
|
||||
)
|
||||
|
||||
new = instance.context.create_instance(subset)
|
||||
new.data["subset"] = subset
|
||||
new.data["label"] = "{0} ({1})".format(subset, asset["name"])
|
||||
new = instance.context.create_instance(subset_name)
|
||||
new.data["subset"] = subset_name
|
||||
new.data["label"] = "{0} ({1})".format(subset_name, asset_name)
|
||||
new.data["family"] = "usd.bootstrap"
|
||||
new.data["comment"] = "Automated bootstrap USD file."
|
||||
new.data["publishFamilies"] = ["usd"]
|
||||
|
|
@ -91,21 +98,23 @@ class CollectUsdBootstrap(pyblish.api.InstancePlugin):
|
|||
for key in ["asset"]:
|
||||
new.data[key] = instance.data[key]
|
||||
|
||||
def _subset_exists(self, project_name, instance, subset, asset):
|
||||
def _subset_exists(self, project_name, instance, subset_name, asset_doc):
|
||||
"""Return whether subset exists in current context or in database."""
|
||||
# Allow it to be created during this publish session
|
||||
context = instance.context
|
||||
|
||||
asset_doc_name = get_asset_name_identifier(asset_doc)
|
||||
for inst in context:
|
||||
if (
|
||||
inst.data["subset"] == subset
|
||||
and inst.data["asset"] == asset["name"]
|
||||
inst.data["subset"] == subset_name
|
||||
and inst.data["asset"] == asset_doc_name
|
||||
):
|
||||
return True
|
||||
|
||||
# Or, if they already exist in the database we can
|
||||
# skip them too.
|
||||
if get_subset_by_name(
|
||||
project_name, subset, asset["_id"], fields=["_id"]
|
||||
project_name, subset_name, asset_doc["_id"], fields=["_id"]
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -45,7 +45,26 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
|
|||
render_products = []
|
||||
# TODO: add render elements if render element
|
||||
|
||||
beauty_product = self.get_beauty_render_product(default_prefix)
|
||||
# Store whether we are splitting the render job in an export + render
|
||||
export_job = rop.parm("render_export_mode").eval() == "2"
|
||||
instance.data["exportJob"] = export_job
|
||||
export_prefix = None
|
||||
export_products = []
|
||||
if export_job:
|
||||
export_prefix = evalParmNoFrame(
|
||||
rop, "render_export_filepath", pad_character="0"
|
||||
)
|
||||
beauty_export_product = self.get_render_product_name(
|
||||
prefix=export_prefix,
|
||||
suffix=None)
|
||||
export_products.append(beauty_export_product)
|
||||
self.log.debug(
|
||||
"Found export product: {}".format(beauty_export_product)
|
||||
)
|
||||
instance.data["ifdFile"] = beauty_export_product
|
||||
instance.data["exportFiles"] = list(export_products)
|
||||
|
||||
beauty_product = self.get_render_product_name(default_prefix)
|
||||
render_products.append(beauty_product)
|
||||
files_by_aov = {
|
||||
"RGB Color": self.generate_expected_files(instance,
|
||||
|
|
@ -79,7 +98,7 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
|
|||
instance.data["colorspaceDisplay"] = colorspace_data["display"]
|
||||
instance.data["colorspaceView"] = colorspace_data["view"]
|
||||
|
||||
def get_beauty_render_product(self, prefix, suffix="<reName>"):
|
||||
def get_render_product_name(self, prefix, suffix="<reName>"):
|
||||
"""Return the beauty output filename if render element enabled
|
||||
"""
|
||||
# Remove aov suffix from the product: `prefix.aov_suffix` -> `prefix`
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@ class ExtractAlembic(publish.Extractor):
|
|||
label = "Extract Alembic"
|
||||
hosts = ["houdini"]
|
||||
families = ["abc", "camera"]
|
||||
targets = ["local", "remote"]
|
||||
|
||||
def process(self, instance):
|
||||
if instance.data.get("farm"):
|
||||
self.log.debug("Should be processed on farm, skipping.")
|
||||
return
|
||||
|
||||
ropnode = hou.node(instance.data["instance_node"])
|
||||
|
||||
|
|
|
|||
|
|
@ -14,9 +14,12 @@ class ExtractAss(publish.Extractor):
|
|||
label = "Extract Ass"
|
||||
families = ["ass"]
|
||||
hosts = ["houdini"]
|
||||
targets = ["local", "remote"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
if instance.data.get("farm"):
|
||||
self.log.debug("Should be processed on farm, skipping.")
|
||||
return
|
||||
ropnode = hou.node(instance.data["instance_node"])
|
||||
|
||||
# Get the filename from the filename parameter
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ class ExtractBGEO(publish.Extractor):
|
|||
families = ["bgeo"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
if instance.data.get("farm"):
|
||||
self.log.debug("Should be processed on farm, skipping.")
|
||||
return
|
||||
ropnode = hou.node(instance.data["instance_node"])
|
||||
|
||||
# Get the filename from the filename parameter
|
||||
|
|
|
|||
51
openpype/hosts/houdini/plugins/publish/extract_mantra_ifd.py
Normal file
51
openpype/hosts/houdini/plugins/publish/extract_mantra_ifd.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.pipeline import publish
|
||||
|
||||
import hou
|
||||
|
||||
|
||||
class ExtractMantraIFD(publish.Extractor):
|
||||
|
||||
order = pyblish.api.ExtractorOrder
|
||||
label = "Extract Mantra ifd"
|
||||
hosts = ["houdini"]
|
||||
families = ["mantraifd"]
|
||||
targets = ["local", "remote"]
|
||||
|
||||
def process(self, instance):
|
||||
if instance.data.get("farm"):
|
||||
self.log.debug("Should be processed on farm, skipping.")
|
||||
return
|
||||
|
||||
ropnode = hou.node(instance.data.get("instance_node"))
|
||||
output = ropnode.evalParm("soho_diskfile")
|
||||
staging_dir = os.path.dirname(output)
|
||||
instance.data["stagingDir"] = staging_dir
|
||||
|
||||
files = instance.data["frames"]
|
||||
missing_frames = [
|
||||
frame
|
||||
for frame in instance.data["frames"]
|
||||
if not os.path.exists(
|
||||
os.path.normpath(os.path.join(staging_dir, frame)))
|
||||
]
|
||||
if missing_frames:
|
||||
raise RuntimeError("Failed to complete Mantra ifd extraction. "
|
||||
"Missing output files: {}".format(
|
||||
missing_frames))
|
||||
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
representation = {
|
||||
'name': 'ifd',
|
||||
'ext': 'ifd',
|
||||
'files': files,
|
||||
"stagingDir": staging_dir,
|
||||
"frameStart": instance.data["frameStart"],
|
||||
"frameEnd": instance.data["frameEnd"],
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
|
@ -14,9 +14,12 @@ class ExtractRedshiftProxy(publish.Extractor):
|
|||
label = "Extract Redshift Proxy"
|
||||
families = ["redshiftproxy"]
|
||||
hosts = ["houdini"]
|
||||
targets = ["local", "remote"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
if instance.data.get("farm"):
|
||||
self.log.debug("Should be processed on farm, skipping.")
|
||||
return
|
||||
ropnode = hou.node(instance.data.get("instance_node"))
|
||||
|
||||
# Get the filename from the filename parameter
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ class ExtractVDBCache(publish.Extractor):
|
|||
hosts = ["houdini"]
|
||||
|
||||
def process(self, instance):
|
||||
|
||||
if instance.data.get("farm"):
|
||||
self.log.debug("Should be processed on farm, skipping.")
|
||||
return
|
||||
ropnode = hou.node(instance.data["instance_node"])
|
||||
|
||||
# Get the filename from the filename parameter
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ class IncrementCurrentFile(pyblish.api.ContextPlugin):
|
|||
"arnold_rop",
|
||||
"mantra_rop",
|
||||
"karma_rop",
|
||||
"usdrender"]
|
||||
"usdrender",
|
||||
"publish.hou"]
|
||||
optional = True
|
||||
|
||||
def process(self, context):
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class ValidateHoudiniNotApprenticeLicense(pyblish.api.InstancePlugin):
|
|||
"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
families = ["usd", "abc"]
|
||||
families = ["usd", "abc", "fbx", "camera"]
|
||||
hosts = ["houdini"]
|
||||
label = "Houdini Apprentice License"
|
||||
|
||||
|
|
|
|||
|
|
@ -54,12 +54,13 @@ class ValidateSubsetName(pyblish.api.InstancePlugin,
|
|||
rop_node = hou.node(instance.data["instance_node"])
|
||||
|
||||
# Check subset name
|
||||
asset_doc = instance.data["assetEntity"]
|
||||
subset_name = get_subset_name(
|
||||
family=instance.data["family"],
|
||||
variant=instance.data["variant"],
|
||||
task_name=instance.data["task"],
|
||||
asset_doc=instance.data["assetEntity"],
|
||||
dynamic_data={"asset": instance.data["asset"]}
|
||||
asset_doc=asset_doc,
|
||||
dynamic_data={"asset": asset_doc["name"]}
|
||||
)
|
||||
|
||||
if instance.data.get("subset") != subset_name:
|
||||
|
|
@ -76,12 +77,13 @@ class ValidateSubsetName(pyblish.api.InstancePlugin,
|
|||
rop_node = hou.node(instance.data["instance_node"])
|
||||
|
||||
# Check subset name
|
||||
asset_doc = instance.data["assetEntity"]
|
||||
subset_name = get_subset_name(
|
||||
family=instance.data["family"],
|
||||
variant=instance.data["variant"],
|
||||
task_name=instance.data["task"],
|
||||
asset_doc=instance.data["assetEntity"],
|
||||
dynamic_data={"asset": instance.data["asset"]}
|
||||
asset_doc=asset_doc,
|
||||
dynamic_data={"asset": asset_doc["name"]}
|
||||
)
|
||||
|
||||
instance.data["subset"] = subset_name
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
"""OpenPype startup script."""
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.hosts.houdini.api import HoudiniHost
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
|
||||
|
||||
def main():
|
||||
print("Installing OpenPype ...")
|
||||
print("Installing {} ...".format(
|
||||
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||
install_host(HoudiniHost())
|
||||
|
||||
|
||||
|
|
|
|||
14
openpype/hosts/houdini/startup/python3.10libs/pythonrc.py
Normal file
14
openpype/hosts/houdini/startup/python3.10libs/pythonrc.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""OpenPype startup script."""
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.hosts.houdini.api import HoudiniHost
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
|
||||
|
||||
def main():
|
||||
print("Installing {} ...".format(
|
||||
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||
install_host(HoudiniHost())
|
||||
|
||||
|
||||
main()
|
||||
|
|
@ -2,10 +2,12 @@
|
|||
"""OpenPype startup script."""
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.hosts.houdini.api import HoudiniHost
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
|
||||
|
||||
def main():
|
||||
print("Installing OpenPype ...")
|
||||
print("Installing {} ...".format(
|
||||
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||
install_host(HoudiniHost())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
"""OpenPype startup script."""
|
||||
from openpype.pipeline import install_host
|
||||
from openpype.hosts.houdini.api import HoudiniHost
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
|
||||
|
||||
def main():
|
||||
print("Installing OpenPype ...")
|
||||
print("Installing {} ...".format(
|
||||
"AYON" if AYON_SERVER_ENABLED else "OpenPype"))
|
||||
install_host(HoudiniHost())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
import os
|
||||
import hou
|
||||
import husdoutputprocessors.base as base
|
||||
|
||||
import colorbleed.usdlib as usdlib
|
||||
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.pipeline import Anatomy, get_current_project_name
|
||||
|
||||
|
||||
class AvalonURIOutputProcessor(base.OutputProcessorBase):
|
||||
"""Process Avalon URIs into their full path equivalents.
|
||||
|
||||
"""
|
||||
|
||||
_parameters = None
|
||||
_param_prefix = 'avalonurioutputprocessor_'
|
||||
_parms = {
|
||||
"use_publish_paths": _param_prefix + "use_publish_paths"
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
""" There is only one object of each output processor class that is
|
||||
ever created in a Houdini session. Therefore be very careful
|
||||
about what data gets put in this object.
|
||||
"""
|
||||
self._use_publish_paths = False
|
||||
self._cache = dict()
|
||||
|
||||
def displayName(self):
|
||||
return 'Avalon URI Output Processor'
|
||||
|
||||
def parameters(self):
|
||||
|
||||
if not self._parameters:
|
||||
parameters = hou.ParmTemplateGroup()
|
||||
use_publish_path = hou.ToggleParmTemplate(
|
||||
name=self._parms["use_publish_paths"],
|
||||
label='Resolve Reference paths to publish paths',
|
||||
default_value=False,
|
||||
help=("When enabled any paths for Layers, References or "
|
||||
"Payloads are resolved to published master versions.\n"
|
||||
"This is usually only used by the publishing pipeline, "
|
||||
"but can be used for testing too."))
|
||||
parameters.append(use_publish_path)
|
||||
self._parameters = parameters.asDialogScript()
|
||||
|
||||
return self._parameters
|
||||
|
||||
def beginSave(self, config_node, t):
|
||||
parm = self._parms["use_publish_paths"]
|
||||
self._use_publish_paths = config_node.parm(parm).evalAtTime(t)
|
||||
self._cache.clear()
|
||||
|
||||
def endSave(self):
|
||||
self._use_publish_paths = None
|
||||
self._cache.clear()
|
||||
|
||||
def processAsset(self,
|
||||
asset_path,
|
||||
asset_path_for_save,
|
||||
referencing_layer_path,
|
||||
asset_is_layer,
|
||||
for_save):
|
||||
"""
|
||||
Args:
|
||||
asset_path (str): The incoming file path you want to alter or not.
|
||||
asset_path_for_save (bool): Whether the current path is a
|
||||
referenced path in the USD file. When True, return the path
|
||||
you want inside USD file.
|
||||
referencing_layer_path (str): ???
|
||||
asset_is_layer (bool): Whether this asset is a USD layer file.
|
||||
If this is False, the asset is something else (for example,
|
||||
a texture or volume file).
|
||||
for_save (bool): Whether the asset path is for a file to be saved
|
||||
out. If so, then return actual written filepath.
|
||||
|
||||
Returns:
|
||||
The refactored asset path.
|
||||
|
||||
"""
|
||||
|
||||
# Retrieve from cache if this query occurred before (optimization)
|
||||
cache_key = (asset_path, asset_path_for_save, asset_is_layer, for_save)
|
||||
if cache_key in self._cache:
|
||||
return self._cache[cache_key]
|
||||
|
||||
relative_template = "{asset}_{subset}.{ext}"
|
||||
uri_data = usdlib.parse_avalon_uri(asset_path)
|
||||
if uri_data:
|
||||
|
||||
if for_save:
|
||||
# Set save output path to a relative path so other
|
||||
# processors can potentially manage it easily?
|
||||
path = relative_template.format(**uri_data)
|
||||
|
||||
print("Avalon URI Resolver: %s -> %s" % (asset_path, path))
|
||||
self._cache[cache_key] = path
|
||||
return path
|
||||
|
||||
if self._use_publish_paths:
|
||||
# Resolve to an Avalon published asset for embedded paths
|
||||
path = self._get_usd_master_path(**uri_data)
|
||||
else:
|
||||
path = relative_template.format(**uri_data)
|
||||
|
||||
print("Avalon URI Resolver: %s -> %s" % (asset_path, path))
|
||||
self._cache[cache_key] = path
|
||||
return path
|
||||
|
||||
self._cache[cache_key] = asset_path
|
||||
return asset_path
|
||||
|
||||
def _get_usd_master_path(self,
|
||||
asset,
|
||||
subset,
|
||||
ext):
|
||||
"""Get the filepath for a .usd file of a subset.
|
||||
|
||||
This will return the path to an unversioned master file generated by
|
||||
`usd_master_file.py`.
|
||||
|
||||
"""
|
||||
|
||||
PROJECT = get_current_project_name()
|
||||
anatomy = Anatomy(PROJECT)
|
||||
asset_doc = get_asset_by_name(PROJECT, asset)
|
||||
if not asset_doc:
|
||||
raise RuntimeError("Invalid asset name: '%s'" % asset)
|
||||
|
||||
template_obj = anatomy.templates_obj["publish"]["path"]
|
||||
path = template_obj.format_strict({
|
||||
"project": PROJECT,
|
||||
"asset": asset_doc["name"],
|
||||
"subset": subset,
|
||||
"representation": ext,
|
||||
"version": 0 # stub version zero
|
||||
})
|
||||
|
||||
# Remove the version folder
|
||||
subset_folder = os.path.dirname(os.path.dirname(path))
|
||||
master_folder = os.path.join(subset_folder, "master")
|
||||
fname = "{0}.{1}".format(subset, ext)
|
||||
|
||||
return os.path.join(master_folder, fname).replace("\\", "/")
|
||||
|
||||
|
||||
output_processor = AvalonURIOutputProcessor()
|
||||
|
||||
|
||||
def usdOutputProcessor():
|
||||
return output_processor
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
import hou
|
||||
import husdoutputprocessors.base as base
|
||||
import os
|
||||
|
||||
|
||||
class StagingDirOutputProcessor(base.OutputProcessorBase):
|
||||
"""Output all USD Rop file nodes into the Staging Directory
|
||||
|
||||
Ignore any folders and paths set in the Configured Layers
|
||||
and USD Rop node, just take the filename and save into a
|
||||
single directory.
|
||||
|
||||
"""
|
||||
theParameters = None
|
||||
parameter_prefix = "stagingdiroutputprocessor_"
|
||||
stagingdir_parm_name = parameter_prefix + "stagingDir"
|
||||
|
||||
def __init__(self):
|
||||
self.staging_dir = None
|
||||
|
||||
def displayName(self):
|
||||
return 'StagingDir Output Processor'
|
||||
|
||||
def parameters(self):
|
||||
if not self.theParameters:
|
||||
parameters = hou.ParmTemplateGroup()
|
||||
rootdirparm = hou.StringParmTemplate(
|
||||
self.stagingdir_parm_name,
|
||||
'Staging Directory', 1,
|
||||
string_type=hou.stringParmType.FileReference,
|
||||
file_type=hou.fileType.Directory
|
||||
)
|
||||
parameters.append(rootdirparm)
|
||||
self.theParameters = parameters.asDialogScript()
|
||||
return self.theParameters
|
||||
|
||||
def beginSave(self, config_node, t):
|
||||
|
||||
# Use the Root Directory parameter if it is set.
|
||||
root_dir_parm = config_node.parm(self.stagingdir_parm_name)
|
||||
if root_dir_parm:
|
||||
self.staging_dir = root_dir_parm.evalAtTime(t)
|
||||
|
||||
if not self.staging_dir:
|
||||
out_file_parm = config_node.parm('lopoutput')
|
||||
if out_file_parm:
|
||||
self.staging_dir = out_file_parm.evalAtTime(t)
|
||||
if self.staging_dir:
|
||||
(self.staging_dir, filename) = os.path.split(self.staging_dir)
|
||||
|
||||
def endSave(self):
|
||||
self.staging_dir = None
|
||||
|
||||
def processAsset(self, asset_path,
|
||||
asset_path_for_save,
|
||||
referencing_layer_path,
|
||||
asset_is_layer,
|
||||
for_save):
|
||||
"""
|
||||
Args:
|
||||
asset_path (str): The incoming file path you want to alter or not.
|
||||
asset_path_for_save (bool): Whether the current path is a
|
||||
referenced path in the USD file. When True, return the path
|
||||
you want inside USD file.
|
||||
referencing_layer_path (str): ???
|
||||
asset_is_layer (bool): Whether this asset is a USD layer file.
|
||||
If this is False, the asset is something else (for example,
|
||||
a texture or volume file).
|
||||
for_save (bool): Whether the asset path is for a file to be saved
|
||||
out. If so, then return actual written filepath.
|
||||
|
||||
Returns:
|
||||
The refactored asset path.
|
||||
|
||||
"""
|
||||
|
||||
# Treat save paths as being relative to the output path.
|
||||
if for_save and self.staging_dir:
|
||||
# Whenever we're processing a Save Path make sure to
|
||||
# resolve it to the Staging Directory
|
||||
filename = os.path.basename(asset_path)
|
||||
return os.path.join(self.staging_dir, filename)
|
||||
|
||||
return asset_path
|
||||
|
||||
|
||||
output_processor = StagingDirOutputProcessor()
|
||||
def usdOutputProcessor():
|
||||
return output_processor
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""3dsmax menu definition of OpenPype."""
|
||||
"""3dsmax menu definition of AYON."""
|
||||
import os
|
||||
from qtpy import QtWidgets, QtCore
|
||||
from pymxs import runtime as rt
|
||||
|
||||
|
|
@ -8,7 +9,7 @@ from openpype.hosts.max.api import lib
|
|||
|
||||
|
||||
class OpenPypeMenu(object):
|
||||
"""Object representing OpenPype menu.
|
||||
"""Object representing OpenPype/AYON menu.
|
||||
|
||||
This is using "hack" to inject itself before "Help" menu of 3dsmax.
|
||||
For some reason `postLoadingMenus` event doesn't fire, and main menu
|
||||
|
|
@ -50,17 +51,17 @@ class OpenPypeMenu(object):
|
|||
return list(self.main_widget.findChildren(QtWidgets.QMenuBar))[0]
|
||||
|
||||
def get_or_create_openpype_menu(
|
||||
self, name: str = "&OpenPype",
|
||||
self, name: str = "&Openpype",
|
||||
before: str = "&Help") -> QtWidgets.QAction:
|
||||
"""Create OpenPype menu.
|
||||
"""Create AYON menu.
|
||||
|
||||
Args:
|
||||
name (str, Optional): OpenPypep menu name.
|
||||
name (str, Optional): AYON menu name.
|
||||
before (str, Optional): Name of the 3dsmax main menu item to
|
||||
add OpenPype menu before.
|
||||
add AYON menu before.
|
||||
|
||||
Returns:
|
||||
QtWidgets.QAction: OpenPype menu action.
|
||||
QtWidgets.QAction: AYON menu action.
|
||||
|
||||
"""
|
||||
if self.menu is not None:
|
||||
|
|
@ -77,15 +78,15 @@ class OpenPypeMenu(object):
|
|||
|
||||
if before in item.title():
|
||||
help_action = item.menuAction()
|
||||
|
||||
op_menu = QtWidgets.QMenu("&OpenPype")
|
||||
tab_menu_label = os.environ.get("AVALON_LABEL") or "AYON"
|
||||
op_menu = QtWidgets.QMenu("&{}".format(tab_menu_label))
|
||||
menu_bar.insertMenu(help_action, op_menu)
|
||||
|
||||
self.menu = op_menu
|
||||
return op_menu
|
||||
|
||||
def build_openpype_menu(self) -> QtWidgets.QAction:
|
||||
"""Build items in OpenPype menu."""
|
||||
"""Build items in AYON menu."""
|
||||
openpype_menu = self.get_or_create_openpype_menu()
|
||||
load_action = QtWidgets.QAction("Load...", openpype_menu)
|
||||
load_action.triggered.connect(self.load_callback)
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ def containerise(name: str, nodes: list, context,
|
|||
|
||||
|
||||
def load_custom_attribute_data():
|
||||
"""Re-loading the Openpype/AYON custom parameter built by the creator
|
||||
"""Re-loading the AYON custom parameter built by the creator
|
||||
|
||||
Returns:
|
||||
attribute: re-loading the custom OP attributes set in Maxscript
|
||||
|
|
@ -213,7 +213,7 @@ def import_custom_attribute_data(container: str, selections: list):
|
|||
|
||||
|
||||
def update_custom_attribute_data(container: str, selections: list):
|
||||
"""Updating the Openpype/AYON custom parameter built by the creator
|
||||
"""Updating the AYON custom parameter built by the creator
|
||||
|
||||
Args:
|
||||
container (str): target container which adds custom attributes
|
||||
|
|
|
|||
|
|
@ -198,8 +198,8 @@ def _render_preview_animation_max_pre_2024(
|
|||
res_width, res_height, filename=filepath
|
||||
)
|
||||
dib = rt.gw.getViewportDib()
|
||||
dib_width = rt.renderWidth
|
||||
dib_height = rt.renderHeight
|
||||
dib_width = float(dib.width)
|
||||
dib_height = float(dib.height)
|
||||
# aspect ratio
|
||||
viewportRatio = dib_width / dib_height
|
||||
renderRatio = float(res_width / res_height)
|
||||
|
|
|
|||
|
|
@ -71,8 +71,6 @@ DEFAULT_MATRIX = [1.0, 0.0, 0.0, 0.0,
|
|||
INT_FPS = {15, 24, 25, 30, 48, 50, 60, 44100, 48000}
|
||||
FLOAT_FPS = {23.98, 23.976, 29.97, 47.952, 59.94}
|
||||
|
||||
RENDERLIKE_INSTANCE_FAMILIES = ["rendering", "vrayscene"]
|
||||
|
||||
|
||||
DISPLAY_LIGHTS_ENUM = [
|
||||
{"label": "Use Project Settings", "value": "project_settings"},
|
||||
|
|
@ -2818,194 +2816,6 @@ class shelf():
|
|||
cmds.shelfLayout(self.name, p="ShelfLayout")
|
||||
|
||||
|
||||
def _get_render_instances():
|
||||
"""Return all 'render-like' instances.
|
||||
|
||||
This returns list of instance sets that needs to receive information
|
||||
about render layer changes.
|
||||
|
||||
Returns:
|
||||
list: list of instances
|
||||
|
||||
"""
|
||||
objectset = cmds.ls("*.id", long=True, exactType="objectSet",
|
||||
recursive=True, objectsOnly=True)
|
||||
|
||||
instances = []
|
||||
for objset in objectset:
|
||||
if not cmds.attributeQuery("id", node=objset, exists=True):
|
||||
continue
|
||||
|
||||
id_attr = "{}.id".format(objset)
|
||||
if cmds.getAttr(id_attr) != "pyblish.avalon.instance":
|
||||
continue
|
||||
|
||||
has_family = cmds.attributeQuery("family",
|
||||
node=objset,
|
||||
exists=True)
|
||||
if not has_family:
|
||||
continue
|
||||
|
||||
if cmds.getAttr(
|
||||
"{}.family".format(objset)) in RENDERLIKE_INSTANCE_FAMILIES:
|
||||
instances.append(objset)
|
||||
|
||||
return instances
|
||||
|
||||
|
||||
renderItemObserverList = []
|
||||
|
||||
|
||||
class RenderSetupListObserver:
|
||||
"""Observer to catch changes in render setup layers."""
|
||||
|
||||
def listItemAdded(self, item):
|
||||
print("--- adding ...")
|
||||
self._add_render_layer(item)
|
||||
|
||||
def listItemRemoved(self, item):
|
||||
print("--- removing ...")
|
||||
self._remove_render_layer(item.name())
|
||||
|
||||
def _add_render_layer(self, item):
|
||||
render_sets = _get_render_instances()
|
||||
layer_name = item.name()
|
||||
|
||||
for render_set in render_sets:
|
||||
members = cmds.sets(render_set, query=True) or []
|
||||
|
||||
namespace_name = "_{}".format(render_set)
|
||||
if not cmds.namespace(exists=namespace_name):
|
||||
index = 1
|
||||
namespace_name = "_{}".format(render_set)
|
||||
try:
|
||||
cmds.namespace(rm=namespace_name)
|
||||
except RuntimeError:
|
||||
# namespace is not empty, so we leave it untouched
|
||||
pass
|
||||
orignal_namespace_name = namespace_name
|
||||
while(cmds.namespace(exists=namespace_name)):
|
||||
namespace_name = "{}{}".format(
|
||||
orignal_namespace_name, index)
|
||||
index += 1
|
||||
|
||||
namespace = cmds.namespace(add=namespace_name)
|
||||
|
||||
if members:
|
||||
# if set already have namespaced members, use the same
|
||||
# namespace as others.
|
||||
namespace = members[0].rpartition(":")[0]
|
||||
else:
|
||||
namespace = namespace_name
|
||||
|
||||
render_layer_set_name = "{}:{}".format(namespace, layer_name)
|
||||
if render_layer_set_name in members:
|
||||
continue
|
||||
print(" - creating set for {}".format(layer_name))
|
||||
maya_set = cmds.sets(n=render_layer_set_name, empty=True)
|
||||
cmds.sets(maya_set, forceElement=render_set)
|
||||
rio = RenderSetupItemObserver(item)
|
||||
print("- adding observer for {}".format(item.name()))
|
||||
item.addItemObserver(rio.itemChanged)
|
||||
renderItemObserverList.append(rio)
|
||||
|
||||
def _remove_render_layer(self, layer_name):
|
||||
render_sets = _get_render_instances()
|
||||
|
||||
for render_set in render_sets:
|
||||
members = cmds.sets(render_set, query=True)
|
||||
if not members:
|
||||
continue
|
||||
|
||||
# all sets under set should have the same namespace
|
||||
namespace = members[0].rpartition(":")[0]
|
||||
render_layer_set_name = "{}:{}".format(namespace, layer_name)
|
||||
|
||||
if render_layer_set_name in members:
|
||||
print(" - removing set for {}".format(layer_name))
|
||||
cmds.delete(render_layer_set_name)
|
||||
|
||||
|
||||
class RenderSetupItemObserver:
|
||||
"""Handle changes in render setup items."""
|
||||
|
||||
def __init__(self, item):
|
||||
self.item = item
|
||||
self.original_name = item.name()
|
||||
|
||||
def itemChanged(self, *args, **kwargs):
|
||||
"""Item changed callback."""
|
||||
if self.item.name() == self.original_name:
|
||||
return
|
||||
|
||||
render_sets = _get_render_instances()
|
||||
|
||||
for render_set in render_sets:
|
||||
members = cmds.sets(render_set, query=True)
|
||||
if not members:
|
||||
continue
|
||||
|
||||
# all sets under set should have the same namespace
|
||||
namespace = members[0].rpartition(":")[0]
|
||||
render_layer_set_name = "{}:{}".format(
|
||||
namespace, self.original_name)
|
||||
|
||||
if render_layer_set_name in members:
|
||||
print(" <> renaming {} to {}".format(self.original_name,
|
||||
self.item.name()))
|
||||
cmds.rename(render_layer_set_name,
|
||||
"{}:{}".format(
|
||||
namespace, self.item.name()))
|
||||
self.original_name = self.item.name()
|
||||
|
||||
|
||||
renderListObserver = RenderSetupListObserver()
|
||||
|
||||
|
||||
def add_render_layer_change_observer():
|
||||
import maya.app.renderSetup.model.renderSetup as renderSetup
|
||||
|
||||
rs = renderSetup.instance()
|
||||
render_sets = _get_render_instances()
|
||||
|
||||
layers = rs.getRenderLayers()
|
||||
for render_set in render_sets:
|
||||
members = cmds.sets(render_set, query=True)
|
||||
if not members:
|
||||
continue
|
||||
# all sets under set should have the same namespace
|
||||
namespace = members[0].rpartition(":")[0]
|
||||
for layer in layers:
|
||||
render_layer_set_name = "{}:{}".format(namespace, layer.name())
|
||||
if render_layer_set_name not in members:
|
||||
continue
|
||||
rio = RenderSetupItemObserver(layer)
|
||||
print("- adding observer for {}".format(layer.name()))
|
||||
layer.addItemObserver(rio.itemChanged)
|
||||
renderItemObserverList.append(rio)
|
||||
|
||||
|
||||
def add_render_layer_observer():
|
||||
import maya.app.renderSetup.model.renderSetup as renderSetup
|
||||
|
||||
print("> adding renderSetup observer ...")
|
||||
rs = renderSetup.instance()
|
||||
rs.addListObserver(renderListObserver)
|
||||
pass
|
||||
|
||||
|
||||
def remove_render_layer_observer():
|
||||
import maya.app.renderSetup.model.renderSetup as renderSetup
|
||||
|
||||
print("< removing renderSetup observer ...")
|
||||
rs = renderSetup.instance()
|
||||
try:
|
||||
rs.removeListObserver(renderListObserver)
|
||||
except ValueError:
|
||||
# no observer set yet
|
||||
pass
|
||||
|
||||
|
||||
def update_content_on_context_change():
|
||||
"""
|
||||
This will update scene content to match new asset on context change
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@ class RenderSettings(object):
|
|||
def set_default_renderer_settings(self, renderer=None):
|
||||
"""Set basic settings based on renderer."""
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds
|
||||
import maya.mel as mel
|
||||
from maya import cmds # noqa: F401
|
||||
import maya.mel as mel # noqa: F401
|
||||
|
||||
if not renderer:
|
||||
renderer = cmds.getAttr(
|
||||
|
|
@ -126,6 +126,10 @@ class RenderSettings(object):
|
|||
"""Sets settings for Arnold."""
|
||||
from mtoa.core import createOptions # noqa
|
||||
from mtoa.aovs import AOVInterface # noqa
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds # noqa: F401
|
||||
import maya.mel as mel # noqa: F401
|
||||
|
||||
createOptions()
|
||||
render_settings = self._project_settings["maya"]["RenderSettings"]
|
||||
arnold_render_presets = render_settings["arnold_renderer"] # noqa
|
||||
|
|
@ -172,6 +176,10 @@ class RenderSettings(object):
|
|||
|
||||
def _set_redshift_settings(self, width, height):
|
||||
"""Sets settings for Redshift."""
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds # noqa: F401
|
||||
import maya.mel as mel # noqa: F401
|
||||
|
||||
render_settings = self._project_settings["maya"]["RenderSettings"]
|
||||
redshift_render_presets = render_settings["redshift_renderer"]
|
||||
|
||||
|
|
@ -224,6 +232,10 @@ class RenderSettings(object):
|
|||
|
||||
def _set_renderman_settings(self, width, height, aov_separator):
|
||||
"""Sets settings for Renderman"""
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds # noqa: F401
|
||||
import maya.mel as mel # noqa: F401
|
||||
|
||||
rman_render_presets = (
|
||||
self._project_settings
|
||||
["maya"]
|
||||
|
|
@ -285,6 +297,11 @@ class RenderSettings(object):
|
|||
def _set_vray_settings(self, aov_separator, width, height):
|
||||
# type: (str, int, int) -> None
|
||||
"""Sets important settings for Vray."""
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds # noqa: F401
|
||||
import maya.mel as mel # noqa: F401
|
||||
|
||||
|
||||
settings = cmds.ls(type="VRaySettingsNode")
|
||||
node = settings[0] if settings else cmds.createNode("VRaySettingsNode")
|
||||
render_settings = self._project_settings["maya"]["RenderSettings"]
|
||||
|
|
@ -357,6 +374,10 @@ class RenderSettings(object):
|
|||
|
||||
@staticmethod
|
||||
def _set_global_output_settings():
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds # noqa: F401
|
||||
import maya.mel as mel # noqa: F401
|
||||
|
||||
# enable animation
|
||||
cmds.setAttr("defaultRenderGlobals.outFormatControl", 0)
|
||||
cmds.setAttr("defaultRenderGlobals.animation", 1)
|
||||
|
|
@ -364,6 +385,10 @@ class RenderSettings(object):
|
|||
cmds.setAttr("defaultRenderGlobals.extensionPadding", 4)
|
||||
|
||||
def _additional_attribs_setter(self, additional_attribs):
|
||||
# Not all hosts can import this module.
|
||||
from maya import cmds # noqa: F401
|
||||
import maya.mel as mel # noqa: F401
|
||||
|
||||
for item in additional_attribs:
|
||||
attribute, value = item
|
||||
attribute = str(attribute) # ensure str conversion from settings
|
||||
|
|
|
|||
|
|
@ -580,20 +580,11 @@ def on_save():
|
|||
lib.set_id(node, new_id, overwrite=False)
|
||||
|
||||
|
||||
def _update_render_layer_observers():
|
||||
# Helper to trigger update for all renderlayer observer logic
|
||||
lib.remove_render_layer_observer()
|
||||
lib.add_render_layer_observer()
|
||||
lib.add_render_layer_change_observer()
|
||||
|
||||
|
||||
def on_open():
|
||||
"""On scene open let's assume the containers have changed."""
|
||||
|
||||
from openpype.widgets import popup
|
||||
|
||||
utils.executeDeferred(_update_render_layer_observers)
|
||||
|
||||
# Validate FPS after update_task_from_path to
|
||||
# ensure it is using correct FPS for the asset
|
||||
lib.validate_fps()
|
||||
|
|
@ -630,7 +621,6 @@ def on_new():
|
|||
with lib.suspended_refresh():
|
||||
lib.set_context_settings()
|
||||
|
||||
utils.executeDeferred(_update_render_layer_observers)
|
||||
_remove_workfile_lock()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import six
|
|||
from maya import cmds
|
||||
from maya.app.renderSetup.model import renderSetup
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.lib import BoolDef, Logger
|
||||
from openpype.settings import get_project_settings
|
||||
from openpype.pipeline import (
|
||||
|
|
@ -449,14 +450,16 @@ class RenderlayerCreator(NewCreator, MayaCreatorBase):
|
|||
# this instance will not have the `instance_node` data yet
|
||||
# until it's been saved/persisted at least once.
|
||||
project_name = self.create_context.get_current_project_name()
|
||||
|
||||
asset_name = self.create_context.get_current_asset_name()
|
||||
instance_data = {
|
||||
"asset": self.create_context.get_current_asset_name(),
|
||||
"task": self.create_context.get_current_task_name(),
|
||||
"variant": layer.name(),
|
||||
}
|
||||
asset_doc = get_asset_by_name(project_name,
|
||||
instance_data["asset"])
|
||||
if AYON_SERVER_ENABLED:
|
||||
instance_data["folderPath"] = asset_name
|
||||
else:
|
||||
instance_data["asset"] = asset_name
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
layer.name(),
|
||||
instance_data["task"],
|
||||
|
|
|
|||
|
|
@ -45,10 +45,14 @@ class CreateMultishotLayout(plugin.MayaCreator):
|
|||
above is done.
|
||||
"""
|
||||
|
||||
current_folder = get_folder_by_name(
|
||||
project_name=get_current_project_name(),
|
||||
folder_name=get_current_asset_name(),
|
||||
)
|
||||
project_name = get_current_project_name()
|
||||
folder_path = get_current_asset_name()
|
||||
if "/" in folder_path:
|
||||
current_folder = get_folder_by_path(project_name, folder_path)
|
||||
else:
|
||||
current_folder = get_folder_by_name(
|
||||
project_name, folder_name=folder_path
|
||||
)
|
||||
|
||||
current_path_parts = current_folder["path"].split("/")
|
||||
|
||||
|
|
@ -154,7 +158,7 @@ class CreateMultishotLayout(plugin.MayaCreator):
|
|||
# Create layout instance by the layout creator
|
||||
|
||||
instance_data = {
|
||||
"asset": shot["name"],
|
||||
"folderPath": shot["path"],
|
||||
"variant": layout_creator.get_default_variant()
|
||||
}
|
||||
if layout_task:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import json
|
|||
|
||||
from maya import cmds
|
||||
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.hosts.maya.api import (
|
||||
lib,
|
||||
plugin
|
||||
|
|
@ -43,7 +44,11 @@ class CreateReview(plugin.MayaCreator):
|
|||
members = cmds.ls(selection=True)
|
||||
|
||||
project_name = self.project_name
|
||||
asset_doc = get_asset_by_name(project_name, instance_data["asset"])
|
||||
if AYON_SERVER_ENABLED:
|
||||
asset_name = instance_data["folderPath"]
|
||||
else:
|
||||
asset_name = instance_data["asset"]
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
task_name = instance_data["task"]
|
||||
preset = lib.get_capture_preset(
|
||||
task_name,
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class CreateUnrealSkeletalMesh(plugin.MayaCreator):
|
|||
# We reorganize the geometry that was originally added into the
|
||||
# set into either 'joints_SET' or 'geometry_SET' based on the
|
||||
# joint_hints from project settings
|
||||
members = cmds.sets(instance_node, query=True)
|
||||
members = cmds.sets(instance_node, query=True) or []
|
||||
cmds.sets(clear=instance_node)
|
||||
|
||||
geometry_set = cmds.sets(name="geometry_SET", empty=True)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Creator plugin for creating workfiles."""
|
||||
from openpype import AYON_SERVER_ENABLED
|
||||
from openpype.pipeline import CreatedInstance, AutoCreator
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.client import get_asset_by_name, get_asset_name_identifier
|
||||
from openpype.hosts.maya.api import plugin
|
||||
from maya import cmds
|
||||
|
||||
|
|
@ -29,16 +30,27 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator):
|
|||
task_name = self.create_context.get_current_task_name()
|
||||
host_name = self.create_context.host_name
|
||||
|
||||
if current_instance is None:
|
||||
current_instance_asset = None
|
||||
elif AYON_SERVER_ENABLED:
|
||||
current_instance_asset = current_instance["folderPath"]
|
||||
else:
|
||||
current_instance_asset = current_instance["asset"]
|
||||
|
||||
if current_instance is None:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
variant, task_name, asset_doc, project_name, host_name
|
||||
)
|
||||
data = {
|
||||
"asset": asset_name,
|
||||
"task": task_name,
|
||||
"variant": variant
|
||||
}
|
||||
if AYON_SERVER_ENABLED:
|
||||
data["folderPath"] = asset_name
|
||||
else:
|
||||
data["asset"] = asset_name
|
||||
|
||||
data.update(
|
||||
self.get_dynamic_data(
|
||||
variant, task_name, asset_doc,
|
||||
|
|
@ -50,15 +62,20 @@ class CreateWorkfile(plugin.MayaCreatorBase, AutoCreator):
|
|||
)
|
||||
self._add_instance_to_context(current_instance)
|
||||
elif (
|
||||
current_instance["asset"] != asset_name
|
||||
or current_instance["task"] != task_name
|
||||
current_instance_asset != asset_name
|
||||
or current_instance["task"] != task_name
|
||||
):
|
||||
# Update instance context if is not the same
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
variant, task_name, asset_doc, project_name, host_name
|
||||
)
|
||||
current_instance["asset"] = asset_name
|
||||
asset_name = get_asset_name_identifier(asset_doc)
|
||||
|
||||
if AYON_SERVER_ENABLED:
|
||||
current_instance["folderPath"] = asset_name
|
||||
else:
|
||||
current_instance["asset"] = asset_name
|
||||
current_instance["task"] = task_name
|
||||
current_instance["subset"] = subset_name
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class ImportModelRender(InventoryAction):
|
|||
)
|
||||
|
||||
def process(self, containers):
|
||||
from maya import cmds
|
||||
from maya import cmds # noqa: F401
|
||||
|
||||
project_name = get_current_project_name()
|
||||
for container in containers:
|
||||
|
|
@ -66,7 +66,7 @@ class ImportModelRender(InventoryAction):
|
|||
None
|
||||
"""
|
||||
|
||||
from maya import cmds
|
||||
from maya import cmds # noqa: F401
|
||||
|
||||
project_name = get_current_project_name()
|
||||
repre_docs = get_representations(
|
||||
|
|
@ -85,12 +85,7 @@ class ImportModelRender(InventoryAction):
|
|||
if scene_type_regex.fullmatch(repre_name):
|
||||
look_repres.append(repre_doc)
|
||||
|
||||
# QUESTION should we care if there is more then one look
|
||||
# representation? (since it's based on regex match)
|
||||
look_repre = None
|
||||
if look_repres:
|
||||
look_repre = look_repres[0]
|
||||
|
||||
look_repre = look_repres[0] if look_repres else None
|
||||
# QUESTION shouldn't be json representation validated too?
|
||||
if not look_repre:
|
||||
print("No model render sets for this model version..")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from openpype.pipeline import (
|
|||
)
|
||||
from openpype.pipeline.load.utils import get_representation_path_from_context
|
||||
from openpype.pipeline.colorspace import (
|
||||
get_imageio_colorspace_from_filepath,
|
||||
get_imageio_file_rules_colorspace_from_filepath,
|
||||
get_imageio_config,
|
||||
get_imageio_file_rules
|
||||
)
|
||||
|
|
@ -285,10 +285,10 @@ class FileNodeLoader(load.LoaderPlugin):
|
|||
)
|
||||
|
||||
path = get_representation_path_from_context(context)
|
||||
colorspace = get_imageio_colorspace_from_filepath(
|
||||
path=path,
|
||||
host_name=host_name,
|
||||
project_name=project_name,
|
||||
colorspace = get_imageio_file_rules_colorspace_from_filepath(
|
||||
path,
|
||||
host_name,
|
||||
project_name,
|
||||
config_data=config_data,
|
||||
file_rules=file_rules,
|
||||
project_settings=project_settings
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
|
|||
class MayaUSDReferenceLoader(ReferenceLoader):
|
||||
"""Reference USD file to native Maya nodes using MayaUSDImport reference"""
|
||||
|
||||
label = "Reference Maya USD"
|
||||
families = ["usd"]
|
||||
representations = ["usd"]
|
||||
extensions = {"usd", "usda", "usdc"}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue