Merge branch 'develop' into enhancement/OP-5920_abc-options-for-Pointcache-Animation-family

This commit is contained in:
Oscar Domingo 2023-12-12 10:01:54 +00:00
commit cd26a893b8
302 changed files with 8147 additions and 3467 deletions

View file

@ -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

View file

@ -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")

View file

@ -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",
)

View file

@ -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)

View file

@ -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

View file

@ -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:

View file

@ -27,6 +27,7 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook):
"tvpaint",
"substancepainter",
"aftereffects",
"wrap"
}
launch_types = {LaunchTypes.local}

View file

@ -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):

View file

@ -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:

View file

@ -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)

View file

@ -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

View file

@ -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'],

View file

@ -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)

View file

@ -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

View file

@ -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(

View file

@ -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):

View file

@ -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}")

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 = {

View file

@ -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:

View 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}")

View file

@ -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}")

View file

@ -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):

View file

@ -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

View file

@ -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"],

View file

@ -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": []

View file

@ -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(

View 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{}"
}
}
},
},
}

View 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"
},
},
}

View file

@ -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

View file

@ -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",
},

View file

@ -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)

View file

@ -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

View file

@ -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})

View file

@ -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;

View file

@ -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,

View file

@ -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(

View file

@ -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. """

View file

@ -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

View file

@ -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"]
})

View file

@ -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
}

View file

@ -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"])
)

View file

@ -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)

View file

@ -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,

View file

@ -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)

View file

@ -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()

View file

@ -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,

View file

@ -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 [

View file

@ -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))

View 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()

View file

@ -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",

View file

@ -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()

View file

@ -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()

View file

@ -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()

View file

@ -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,

View file

@ -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

View file

@ -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)

View 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

View 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")
]

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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`

View file

@ -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"])

View file

@ -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

View file

@ -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

View 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)

View file

@ -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

View file

@ -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

View file

@ -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):

View file

@ -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"

View file

@ -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

View file

@ -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())

View 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()

View file

@ -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())

View file

@ -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())

View file

@ -1 +0,0 @@
__path__ = __import__('pkgutil').extend_path(__path__, __name__)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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"],

View file

@ -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:

View file

@ -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,

View file

@ -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)

View file

@ -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

View file

@ -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..")

View file

@ -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

View file

@ -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