mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 08:24:53 +01:00
Merge branch 'develop' into enhancement/OP-6458-ue_building_shot_structure_from_db
This commit is contained in:
commit
c6fe13d7fb
643 changed files with 8631 additions and 17132 deletions
28
.github/workflows/issue_to_clickup_trigger.yml
vendored
Normal file
28
.github/workflows/issue_to_clickup_trigger.yml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
name: Sync Issues to ClickUp [trigger]
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
issue-number:
|
||||||
|
required: true
|
||||||
|
issues:
|
||||||
|
types: [labeled]
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
call-ci-tools-issue-sync:
|
||||||
|
if: github.event.inputs.issue-number != '' || github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'backlog')
|
||||||
|
uses: ynput/ci-tools/.github/workflows/issue_to_clickup_ref.yml@main
|
||||||
|
with:
|
||||||
|
# issue number should be taken either from inputs or from the event
|
||||||
|
issue-number: ${{ github.event.inputs.issue-number || github.event.issue.number }}
|
||||||
|
repo-owner: ${{ github.event.repository.owner.login }}
|
||||||
|
repo-name: ${{ github.event.repository.name }}
|
||||||
|
secrets:
|
||||||
|
token: ${{ secrets.YNPUT_BOT_TOKEN }}
|
||||||
|
cu_api_key: ${{ secrets.CLICKUP_API_KEY }}
|
||||||
|
cu_team_id: ${{ secrets.CLICKUP_TEAM_ID }}
|
||||||
|
cu_folder_id: ${{ secrets.CLICKUP_FOLDER_ID }}
|
||||||
|
cu_list_id: ${{ secrets.CLICKUP_LIST_ID }}
|
||||||
|
cu_field_domain_id: ${{ secrets.CLICKUP_DOMAIN_FIELD_ID }}
|
||||||
|
cu_field_type_id: ${{ secrets.CLICKUP_ISSUETYPE_FIELD_ID }}
|
||||||
|
|
@ -15,13 +15,9 @@ from abc import ABCMeta, abstractmethod
|
||||||
import six
|
import six
|
||||||
import appdirs
|
import appdirs
|
||||||
|
|
||||||
from ayon_core.lib import Logger
|
from ayon_core.lib import Logger, is_dev_mode_enabled
|
||||||
from ayon_core.client import get_ayon_server_api_connection
|
from ayon_core.client import get_ayon_server_api_connection
|
||||||
from ayon_core.settings import get_system_settings
|
from ayon_core.settings import get_studio_settings
|
||||||
from ayon_core.settings.ayon_settings import (
|
|
||||||
is_dev_mode_enabled,
|
|
||||||
get_ayon_settings,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .interfaces import (
|
from .interfaces import (
|
||||||
IPluginPaths,
|
IPluginPaths,
|
||||||
|
|
@ -648,7 +644,6 @@ class AddonsManager:
|
||||||
|
|
||||||
def __init__(self, settings=None, initialize=True):
|
def __init__(self, settings=None, initialize=True):
|
||||||
self._settings = settings
|
self._settings = settings
|
||||||
self._system_settings = None
|
|
||||||
|
|
||||||
self._addons = []
|
self._addons = []
|
||||||
self._addons_by_id = {}
|
self._addons_by_id = {}
|
||||||
|
|
@ -738,14 +733,9 @@ class AddonsManager:
|
||||||
# Prepare settings for addons
|
# Prepare settings for addons
|
||||||
settings = self._settings
|
settings = self._settings
|
||||||
if settings is None:
|
if settings is None:
|
||||||
settings = get_ayon_settings()
|
settings = get_studio_settings()
|
||||||
|
|
||||||
# OpenPype settings
|
modules_settings = {}
|
||||||
system_settings = self._system_settings
|
|
||||||
if system_settings is None:
|
|
||||||
system_settings = get_system_settings()
|
|
||||||
|
|
||||||
modules_settings = system_settings["modules"]
|
|
||||||
|
|
||||||
report = {}
|
report = {}
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ class Commands:
|
||||||
context = get_global_context()
|
context = get_global_context()
|
||||||
env = get_app_environments_for_context(
|
env = get_app_environments_for_context(
|
||||||
context["project_name"],
|
context["project_name"],
|
||||||
context["asset_name"],
|
context["folder_path"],
|
||||||
context["task_name"],
|
context["task_name"],
|
||||||
app_full_name,
|
app_full_name,
|
||||||
launch_type=LaunchTypes.farm_publish,
|
launch_type=LaunchTypes.farm_publish,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ class AddLastWorkfileToLaunchArgs(PreLaunchHook):
|
||||||
"tvpaint",
|
"tvpaint",
|
||||||
"substancepainter",
|
"substancepainter",
|
||||||
"aftereffects",
|
"aftereffects",
|
||||||
"wrap"
|
"wrap",
|
||||||
|
"openrv"
|
||||||
}
|
}
|
||||||
launch_types = {LaunchTypes.local}
|
launch_types = {LaunchTypes.local}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ class CopyTemplateWorkfile(PreLaunchHook):
|
||||||
self.log.info("Last workfile does not exist.")
|
self.log.info("Last workfile does not exist.")
|
||||||
|
|
||||||
project_name = self.data["project_name"]
|
project_name = self.data["project_name"]
|
||||||
asset_name = self.data["asset_name"]
|
asset_name = self.data["folder_path"]
|
||||||
task_name = self.data["task_name"]
|
task_name = self.data["task_name"]
|
||||||
host_name = self.application.host_name
|
host_name = self.application.host_name
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class GlobalHostDataHook(PreLaunchHook):
|
||||||
app = self.launch_context.application
|
app = self.launch_context.application
|
||||||
temp_data = EnvironmentPrepData({
|
temp_data = EnvironmentPrepData({
|
||||||
"project_name": self.data["project_name"],
|
"project_name": self.data["project_name"],
|
||||||
"asset_name": self.data["asset_name"],
|
"folder_path": self.data["folder_path"],
|
||||||
"task_name": self.data["task_name"],
|
"task_name": self.data["task_name"],
|
||||||
|
|
||||||
"app": app,
|
"app": app,
|
||||||
|
|
@ -66,7 +66,7 @@ class GlobalHostDataHook(PreLaunchHook):
|
||||||
project_doc = get_project(project_name)
|
project_doc = get_project(project_name)
|
||||||
self.data["project_doc"] = project_doc
|
self.data["project_doc"] = project_doc
|
||||||
|
|
||||||
asset_name = self.data.get("asset_name")
|
asset_name = self.data.get("folder_path")
|
||||||
if not asset_name:
|
if not asset_name:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"Asset name was not set. Skipping asset document query."
|
"Asset name was not set. Skipping asset document query."
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ class OCIOEnvHook(PreLaunchHook):
|
||||||
"nuke",
|
"nuke",
|
||||||
"hiero",
|
"hiero",
|
||||||
"resolve",
|
"resolve",
|
||||||
|
"openrv"
|
||||||
}
|
}
|
||||||
launch_types = set()
|
launch_types = set()
|
||||||
|
|
||||||
|
|
@ -27,10 +28,10 @@ class OCIOEnvHook(PreLaunchHook):
|
||||||
|
|
||||||
template_data = get_template_data_with_names(
|
template_data = get_template_data_with_names(
|
||||||
project_name=self.data["project_name"],
|
project_name=self.data["project_name"],
|
||||||
asset_name=self.data["asset_name"],
|
asset_name=self.data["folder_path"],
|
||||||
task_name=self.data["task_name"],
|
task_name=self.data["task_name"],
|
||||||
host_name=self.host_name,
|
host_name=self.host_name,
|
||||||
system_settings=self.data["system_settings"]
|
settings=self.data["project_settings"]
|
||||||
)
|
)
|
||||||
|
|
||||||
config_data = get_imageio_config(
|
config_data = get_imageio_config(
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,10 @@ class HostDirmap(object):
|
||||||
exclude_locals=False,
|
exclude_locals=False,
|
||||||
cached=False)
|
cached=False)
|
||||||
|
|
||||||
|
# TODO implement
|
||||||
|
# Dirmap is dependent on 'get_site_local_overrides' which
|
||||||
|
# is not implemented in AYON. The mapping should be received
|
||||||
|
# from sitesync addon.
|
||||||
active_overrides = get_site_local_overrides(
|
active_overrides = get_site_local_overrides(
|
||||||
project_name, active_site)
|
project_name, active_site)
|
||||||
remote_overrides = get_site_local_overrides(
|
remote_overrides = get_site_local_overrides(
|
||||||
|
|
|
||||||
|
|
@ -134,12 +134,12 @@ class HostBase(object):
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
|
Dict[str, Union[str, None]]: Context with 3 keys 'project_name',
|
||||||
'asset_name' and 'task_name'. All of them can be 'None'.
|
'folder_path' and 'task_name'. All of them can be 'None'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"project_name": self.get_current_project_name(),
|
"project_name": self.get_current_project_name(),
|
||||||
"asset_name": self.get_current_asset_name(),
|
"folder_path": self.get_current_asset_name(),
|
||||||
"task_name": self.get_current_task_name()
|
"task_name": self.get_current_task_name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,7 +161,7 @@ class HostBase(object):
|
||||||
# Use current context to fill the context title
|
# Use current context to fill the context title
|
||||||
current_context = self.get_current_context()
|
current_context = self.get_current_context()
|
||||||
project_name = current_context["project_name"]
|
project_name = current_context["project_name"]
|
||||||
asset_name = current_context["asset_name"]
|
asset_name = current_context["folder_path"]
|
||||||
task_name = current_context["task_name"]
|
task_name = current_context["task_name"]
|
||||||
items = []
|
items = []
|
||||||
if project_name:
|
if project_name:
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ def set_settings(frames, resolution, comp_ids=None, print_msg=True):
|
||||||
current_context = get_current_context()
|
current_context = get_current_context()
|
||||||
|
|
||||||
asset_doc = get_asset_by_name(current_context["project_name"],
|
asset_doc = get_asset_by_name(current_context["project_name"],
|
||||||
current_context["asset_name"])
|
current_context["folder_path"])
|
||||||
settings = get_asset_settings(asset_doc)
|
settings = get_asset_settings(asset_doc)
|
||||||
|
|
||||||
msg = ''
|
msg = ''
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ from ayon_core.pipeline import (
|
||||||
register_loader_plugin_path,
|
register_loader_plugin_path,
|
||||||
register_creator_plugin_path,
|
register_creator_plugin_path,
|
||||||
AVALON_CONTAINER_ID,
|
AVALON_CONTAINER_ID,
|
||||||
|
AVALON_INSTANCE_ID,
|
||||||
|
AYON_INSTANCE_ID,
|
||||||
)
|
)
|
||||||
from ayon_core.hosts.aftereffects.api.workfile_template_builder import (
|
from ayon_core.hosts.aftereffects.api.workfile_template_builder import (
|
||||||
AEPlaceholderLoadPlugin,
|
AEPlaceholderLoadPlugin,
|
||||||
|
|
@ -142,7 +144,9 @@ class AfterEffectsHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
||||||
layers_meta = stub.get_metadata()
|
layers_meta = stub.get_metadata()
|
||||||
|
|
||||||
for instance in layers_meta:
|
for instance in layers_meta:
|
||||||
if instance.get("id") == "pyblish.avalon.instance":
|
if instance.get("id") in {
|
||||||
|
AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||||
|
}:
|
||||||
instances.append(instance)
|
instances.append(instance)
|
||||||
return instances
|
return instances
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from ayon_core.pipeline import (
|
||||||
from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances
|
from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances
|
||||||
from ayon_core.hosts.aftereffects.api.lib import set_settings
|
from ayon_core.hosts.aftereffects.api.lib import set_settings
|
||||||
from ayon_core.lib import prepare_template_data
|
from ayon_core.lib import prepare_template_data
|
||||||
from ayon_core.pipeline.create import SUBSET_NAME_ALLOWED_SYMBOLS
|
from ayon_core.pipeline.create import PRODUCT_NAME_ALLOWED_SYMBOLS
|
||||||
|
|
||||||
|
|
||||||
class RenderCreator(Creator):
|
class RenderCreator(Creator):
|
||||||
|
|
@ -22,7 +22,7 @@ class RenderCreator(Creator):
|
||||||
"""
|
"""
|
||||||
identifier = "render"
|
identifier = "render"
|
||||||
label = "Render"
|
label = "Render"
|
||||||
family = "render"
|
product_type = "render"
|
||||||
description = "Render creator"
|
description = "Render creator"
|
||||||
|
|
||||||
create_allow_context_change = True
|
create_allow_context_change = True
|
||||||
|
|
@ -31,7 +31,7 @@ class RenderCreator(Creator):
|
||||||
mark_for_review = True
|
mark_for_review = True
|
||||||
force_setting_values = True
|
force_setting_values = True
|
||||||
|
|
||||||
def create(self, subset_name_from_ui, data, pre_create_data):
|
def create(self, product_name, data, pre_create_data):
|
||||||
stub = api.get_stub() # only after After Effects is up
|
stub = api.get_stub() # only after After Effects is up
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -58,33 +58,37 @@ class RenderCreator(Creator):
|
||||||
len(comps) > 1)
|
len(comps) > 1)
|
||||||
for comp in comps:
|
for comp in comps:
|
||||||
composition_name = re.sub(
|
composition_name = re.sub(
|
||||||
"[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS),
|
"[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS),
|
||||||
"",
|
"",
|
||||||
comp.name
|
comp.name
|
||||||
)
|
)
|
||||||
if use_composition_name:
|
if use_composition_name:
|
||||||
if "{composition}" not in subset_name_from_ui.lower():
|
if "{composition}" not in product_name.lower():
|
||||||
subset_name_from_ui += "{Composition}"
|
product_name += "{Composition}"
|
||||||
|
|
||||||
dynamic_fill = prepare_template_data({"composition":
|
dynamic_fill = prepare_template_data({"composition":
|
||||||
composition_name})
|
composition_name})
|
||||||
subset_name = subset_name_from_ui.format(**dynamic_fill)
|
comp_product_name = product_name.format(**dynamic_fill)
|
||||||
data["composition_name"] = composition_name
|
data["composition_name"] = composition_name
|
||||||
else:
|
else:
|
||||||
subset_name = subset_name_from_ui
|
comp_product_name = re.sub(
|
||||||
subset_name = re.sub(r"\{composition\}", '', subset_name,
|
r"\{composition\}",
|
||||||
flags=re.IGNORECASE)
|
"",
|
||||||
|
product_name,
|
||||||
|
flags=re.IGNORECASE
|
||||||
|
)
|
||||||
|
|
||||||
for inst in self.create_context.instances:
|
for inst in self.create_context.instances:
|
||||||
if subset_name == inst.subset_name:
|
if comp_product_name == inst.product_name:
|
||||||
raise CreatorError("{} already exists".format(
|
raise CreatorError("{} already exists".format(
|
||||||
inst.subset_name))
|
inst.product_name))
|
||||||
|
|
||||||
data["members"] = [comp.id]
|
data["members"] = [comp.id]
|
||||||
data["orig_comp_name"] = composition_name
|
data["orig_comp_name"] = composition_name
|
||||||
|
|
||||||
new_instance = CreatedInstance(self.family, subset_name, data,
|
new_instance = CreatedInstance(
|
||||||
self)
|
self.product_type, comp_product_name, data, self
|
||||||
|
)
|
||||||
if "farm" in pre_create_data:
|
if "farm" in pre_create_data:
|
||||||
use_farm = pre_create_data["farm"]
|
use_farm = pre_create_data["farm"]
|
||||||
new_instance.creator_attributes["farm"] = use_farm
|
new_instance.creator_attributes["farm"] = use_farm
|
||||||
|
|
@ -96,7 +100,7 @@ class RenderCreator(Creator):
|
||||||
new_instance.data_to_store())
|
new_instance.data_to_store())
|
||||||
self._add_instance_to_context(new_instance)
|
self._add_instance_to_context(new_instance)
|
||||||
|
|
||||||
stub.rename_item(comp.id, subset_name)
|
stub.rename_item(comp.id, comp_product_name)
|
||||||
if self.force_setting_values:
|
if self.force_setting_values:
|
||||||
set_settings(True, True, [comp.id], print_msg=False)
|
set_settings(True, True, [comp.id], print_msg=False)
|
||||||
|
|
||||||
|
|
@ -107,7 +111,7 @@ class RenderCreator(Creator):
|
||||||
"selected by default.",
|
"selected by default.",
|
||||||
default=True, label="Use selection"),
|
default=True, label="Use selection"),
|
||||||
BoolDef("use_composition_name",
|
BoolDef("use_composition_name",
|
||||||
label="Use composition name in subset"),
|
label="Use composition name in product"),
|
||||||
UISeparatorDef(),
|
UISeparatorDef(),
|
||||||
BoolDef("farm", label="Render on farm"),
|
BoolDef("farm", label="Render on farm"),
|
||||||
BoolDef(
|
BoolDef(
|
||||||
|
|
@ -133,9 +137,14 @@ class RenderCreator(Creator):
|
||||||
|
|
||||||
def collect_instances(self):
|
def collect_instances(self):
|
||||||
for instance_data in cache_and_get_instances(self):
|
for instance_data in cache_and_get_instances(self):
|
||||||
# legacy instances have family=='render' or 'renderLocal', use them
|
# legacy instances have product_type=='render' or 'renderLocal', use them
|
||||||
creator_id = (instance_data.get("creator_identifier") or
|
creator_id = instance_data.get("creator_identifier")
|
||||||
instance_data.get("family", '').replace("Local", ''))
|
if not creator_id:
|
||||||
|
# NOTE this is for backwards compatibility but probably can be
|
||||||
|
# removed
|
||||||
|
creator_id = instance_data.get("family", "")
|
||||||
|
creator_id = creator_id.replace("Local", "")
|
||||||
|
|
||||||
if creator_id == self.identifier:
|
if creator_id == self.identifier:
|
||||||
instance_data = self._handle_legacy(instance_data)
|
instance_data = self._handle_legacy(instance_data)
|
||||||
instance = CreatedInstance.from_existing(
|
instance = CreatedInstance.from_existing(
|
||||||
|
|
@ -147,10 +156,10 @@ class RenderCreator(Creator):
|
||||||
for created_inst, _changes in update_list:
|
for created_inst, _changes in update_list:
|
||||||
api.get_stub().imprint(created_inst.get("instance_id"),
|
api.get_stub().imprint(created_inst.get("instance_id"),
|
||||||
created_inst.data_to_store())
|
created_inst.data_to_store())
|
||||||
subset_change = _changes.get("subset")
|
name_change = _changes.get("productName")
|
||||||
if subset_change:
|
if name_change:
|
||||||
api.get_stub().rename_item(created_inst.data["members"][0],
|
api.get_stub().rename_item(created_inst.data["members"][0],
|
||||||
subset_change.new_value)
|
name_change.new_value)
|
||||||
|
|
||||||
def remove_instances(self, instances):
|
def remove_instances(self, instances):
|
||||||
"""Removes metadata and renames to original comp name if available."""
|
"""Removes metadata and renames to original comp name if available."""
|
||||||
|
|
@ -183,33 +192,34 @@ class RenderCreator(Creator):
|
||||||
def get_detail_description(self):
|
def get_detail_description(self):
|
||||||
return """Creator for Render instances
|
return """Creator for Render instances
|
||||||
|
|
||||||
Main publishable item in AfterEffects will be of `render` family.
|
Main publishable item in AfterEffects will be of `render` product type.
|
||||||
Result of this item (instance) is picture sequence or video that could
|
Result of this item (instance) is picture sequence or video that could
|
||||||
be a final delivery product or loaded and used in another DCCs.
|
be a final delivery product or loaded and used in another DCCs.
|
||||||
|
|
||||||
Select single composition and create instance of 'render' family or
|
Select single composition and create instance of 'render' product type
|
||||||
turn off 'Use selection' to create instance for all compositions.
|
or turn off 'Use selection' to create instance for all compositions.
|
||||||
|
|
||||||
'Use composition name in subset' allows to explicitly add composition
|
'Use composition name in product' allows to explicitly add composition
|
||||||
name into created subset name.
|
name into created product name.
|
||||||
|
|
||||||
Position of composition name could be set in
|
Position of composition name could be set in
|
||||||
`project_settings/global/tools/creator/subset_name_profiles` with some
|
`project_settings/global/tools/creator/product_name_profiles` with
|
||||||
form of '{composition}' placeholder.
|
some form of '{composition}' placeholder.
|
||||||
|
|
||||||
Composition name will be used implicitly if multiple composition should
|
Composition name will be used implicitly if multiple composition should
|
||||||
be handled at same time.
|
be handled at same time.
|
||||||
|
|
||||||
If {composition} placeholder is not us 'subset_name_profiles'
|
If {composition} placeholder is not us 'product_name_profiles'
|
||||||
composition name will be capitalized and set at the end of subset name
|
composition name will be capitalized and set at the end of
|
||||||
if necessary.
|
product name if necessary.
|
||||||
|
|
||||||
If composition name should be used, it will be cleaned up of characters
|
If composition name should be used, it will be cleaned up of characters
|
||||||
that would cause an issue in published file names.
|
that would cause an issue in published file names.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_dynamic_data(self, variant, task_name, asset_doc,
|
def get_dynamic_data(
|
||||||
project_name, host_name, instance):
|
self, project_name, asset_doc, task_name, variant, host_name, instance
|
||||||
|
):
|
||||||
dynamic_data = {}
|
dynamic_data = {}
|
||||||
if instance is not None:
|
if instance is not None:
|
||||||
composition_name = instance.get("composition_name")
|
composition_name = instance.get("composition_name")
|
||||||
|
|
@ -234,9 +244,9 @@ class RenderCreator(Creator):
|
||||||
instance_data["task"] = self.create_context.get_current_task_name()
|
instance_data["task"] = self.create_context.get_current_task_name()
|
||||||
|
|
||||||
if not instance_data.get("creator_attributes"):
|
if not instance_data.get("creator_attributes"):
|
||||||
is_old_farm = instance_data["family"] != "renderLocal"
|
is_old_farm = instance_data.get("family") != "renderLocal"
|
||||||
instance_data["creator_attributes"] = {"farm": is_old_farm}
|
instance_data["creator_attributes"] = {"farm": is_old_farm}
|
||||||
instance_data["family"] = self.family
|
instance_data["productType"] = self.product_type
|
||||||
|
|
||||||
if instance_data["creator_attributes"].get("mark_for_review") is None:
|
if instance_data["creator_attributes"].get("mark_for_review") is None:
|
||||||
instance_data["creator_attributes"]["mark_for_review"] = True
|
instance_data["creator_attributes"]["mark_for_review"] = True
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ from ayon_core.hosts.aftereffects.api.pipeline import cache_and_get_instances
|
||||||
|
|
||||||
class AEWorkfileCreator(AutoCreator):
|
class AEWorkfileCreator(AutoCreator):
|
||||||
identifier = "workfile"
|
identifier = "workfile"
|
||||||
family = "workfile"
|
product_type = "workfile"
|
||||||
|
|
||||||
default_variant = "Main"
|
default_variant = "Main"
|
||||||
|
|
||||||
|
|
@ -20,9 +20,9 @@ class AEWorkfileCreator(AutoCreator):
|
||||||
for instance_data in cache_and_get_instances(self):
|
for instance_data in cache_and_get_instances(self):
|
||||||
creator_id = instance_data.get("creator_identifier")
|
creator_id = instance_data.get("creator_identifier")
|
||||||
if creator_id == self.identifier:
|
if creator_id == self.identifier:
|
||||||
subset_name = instance_data["subset"]
|
product_name = instance_data["productName"]
|
||||||
instance = CreatedInstance(
|
instance = CreatedInstance(
|
||||||
self.family, subset_name, instance_data, self
|
self.product_type, product_name, instance_data, self
|
||||||
)
|
)
|
||||||
self._add_instance_to_context(instance)
|
self._add_instance_to_context(instance)
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ class AEWorkfileCreator(AutoCreator):
|
||||||
def create(self, options=None):
|
def create(self, options=None):
|
||||||
existing_instance = None
|
existing_instance = None
|
||||||
for instance in self.create_context.instances:
|
for instance in self.create_context.instances:
|
||||||
if instance.family == self.family:
|
if instance.product_type == self.product_type:
|
||||||
existing_instance = instance
|
existing_instance = instance
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -49,9 +49,12 @@ class AEWorkfileCreator(AutoCreator):
|
||||||
|
|
||||||
if existing_instance is None:
|
if existing_instance is None:
|
||||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||||
subset_name = self.get_subset_name(
|
product_name = self.get_product_name(
|
||||||
self.default_variant, task_name, asset_doc,
|
project_name,
|
||||||
project_name, host_name
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
self.default_variant,
|
||||||
|
host_name,
|
||||||
)
|
)
|
||||||
data = {
|
data = {
|
||||||
"folderPath": asset_name,
|
"folderPath": asset_name,
|
||||||
|
|
@ -59,12 +62,16 @@ class AEWorkfileCreator(AutoCreator):
|
||||||
"variant": self.default_variant,
|
"variant": self.default_variant,
|
||||||
}
|
}
|
||||||
data.update(self.get_dynamic_data(
|
data.update(self.get_dynamic_data(
|
||||||
self.default_variant, task_name, asset_doc,
|
project_name,
|
||||||
project_name, host_name, None
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
self.default_variant,
|
||||||
|
host_name,
|
||||||
|
None,
|
||||||
))
|
))
|
||||||
|
|
||||||
new_instance = CreatedInstance(
|
new_instance = CreatedInstance(
|
||||||
self.family, subset_name, data, self
|
self.product_type, product_name, data, self
|
||||||
)
|
)
|
||||||
self._add_instance_to_context(new_instance)
|
self._add_instance_to_context(new_instance)
|
||||||
|
|
||||||
|
|
@ -76,10 +83,13 @@ class AEWorkfileCreator(AutoCreator):
|
||||||
or existing_instance["task"] != task_name
|
or existing_instance["task"] != task_name
|
||||||
):
|
):
|
||||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||||
subset_name = self.get_subset_name(
|
product_name = self.get_product_name(
|
||||||
self.default_variant, task_name, asset_doc,
|
project_name,
|
||||||
project_name, host_name
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
self.default_variant,
|
||||||
|
host_name,
|
||||||
)
|
)
|
||||||
existing_instance["folderPath"] = asset_name
|
existing_instance["folderPath"] = asset_name
|
||||||
existing_instance["task"] = task_name
|
existing_instance["task"] = task_name
|
||||||
existing_instance["subset"] = subset_name
|
existing_instance["productName"] = product_name
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from ayon_core.hosts.aftereffects.api.lib import (
|
||||||
|
|
||||||
class BackgroundLoader(api.AfterEffectsLoader):
|
class BackgroundLoader(api.AfterEffectsLoader):
|
||||||
"""
|
"""
|
||||||
Load images from Background family
|
Load images from Background product type
|
||||||
Creates for each background separate folder with all imported images
|
Creates for each background separate folder with all imported images
|
||||||
from background json AND automatically created composition with layers,
|
from background json AND automatically created composition with layers,
|
||||||
each layer for separate image.
|
each layer for separate image.
|
||||||
|
|
@ -56,16 +56,21 @@ class BackgroundLoader(api.AfterEffectsLoader):
|
||||||
self.__class__.__name__
|
self.__class__.__name__
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
""" Switch asset or change version """
|
""" Switch asset or change version """
|
||||||
stub = self.get_stub()
|
stub = self.get_stub()
|
||||||
context = representation.get("context", {})
|
asset_doc = context["asset"]
|
||||||
|
subset_doc = context["subset"]
|
||||||
|
repre_doc = context["representation"]
|
||||||
|
|
||||||
|
folder_name = asset_doc["name"]
|
||||||
|
product_name = subset_doc["name"]
|
||||||
_ = container.pop("layer")
|
_ = container.pop("layer")
|
||||||
|
|
||||||
# without iterator number (_001, 002...)
|
# without iterator number (_001, 002...)
|
||||||
namespace_from_container = re.sub(r'_\d{3}$', '',
|
namespace_from_container = re.sub(r'_\d{3}$', '',
|
||||||
container["namespace"])
|
container["namespace"])
|
||||||
comp_name = "{}_{}".format(context["asset"], context["subset"])
|
comp_name = "{}_{}".format(folder_name, product_name)
|
||||||
|
|
||||||
# switching assets
|
# switching assets
|
||||||
if namespace_from_container != comp_name:
|
if namespace_from_container != comp_name:
|
||||||
|
|
@ -73,11 +78,11 @@ class BackgroundLoader(api.AfterEffectsLoader):
|
||||||
existing_items = [layer.name for layer in items]
|
existing_items = [layer.name for layer in items]
|
||||||
comp_name = get_unique_layer_name(
|
comp_name = get_unique_layer_name(
|
||||||
existing_items,
|
existing_items,
|
||||||
"{}_{}".format(context["asset"], context["subset"]))
|
"{}_{}".format(folder_name, product_name))
|
||||||
else: # switching version - keep same name
|
else: # switching version - keep same name
|
||||||
comp_name = container["namespace"]
|
comp_name = container["namespace"]
|
||||||
|
|
||||||
path = get_representation_path(representation)
|
path = get_representation_path(repre_doc)
|
||||||
|
|
||||||
layers = get_background_layers(path)
|
layers = get_background_layers(path)
|
||||||
comp = stub.reload_background(container["members"][1],
|
comp = stub.reload_background(container["members"][1],
|
||||||
|
|
@ -85,8 +90,8 @@ class BackgroundLoader(api.AfterEffectsLoader):
|
||||||
layers)
|
layers)
|
||||||
|
|
||||||
# update container
|
# update container
|
||||||
container["representation"] = str(representation["_id"])
|
container["representation"] = str(repre_doc["_id"])
|
||||||
container["name"] = context["subset"]
|
container["name"] = product_name
|
||||||
container["namespace"] = comp_name
|
container["namespace"] = comp_name
|
||||||
container["members"] = comp.members
|
container["members"] = comp.members
|
||||||
|
|
||||||
|
|
@ -104,5 +109,5 @@ class BackgroundLoader(api.AfterEffectsLoader):
|
||||||
stub.imprint(layer.id, {})
|
stub.imprint(layer.id, {})
|
||||||
stub.delete_item(layer.id)
|
stub.delete_item(layer.id)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
|
||||||
|
|
@ -64,31 +64,36 @@ class FileLoader(api.AfterEffectsLoader):
|
||||||
self.__class__.__name__
|
self.__class__.__name__
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
""" Switch asset or change version """
|
""" Switch asset or change version """
|
||||||
stub = self.get_stub()
|
stub = self.get_stub()
|
||||||
layer = container.pop("layer")
|
layer = container.pop("layer")
|
||||||
|
|
||||||
context = representation.get("context", {})
|
asset_doc = context["asset"]
|
||||||
|
subset_doc = context["subset"]
|
||||||
|
repre_doc = context["representation"]
|
||||||
|
|
||||||
|
folder_name = asset_doc["name"]
|
||||||
|
product_name = subset_doc["name"]
|
||||||
|
|
||||||
namespace_from_container = re.sub(r'_\d{3}$', '',
|
namespace_from_container = re.sub(r'_\d{3}$', '',
|
||||||
container["namespace"])
|
container["namespace"])
|
||||||
layer_name = "{}_{}".format(context["asset"], context["subset"])
|
layer_name = "{}_{}".format(folder_name, product_name)
|
||||||
# switching assets
|
# switching assets
|
||||||
if namespace_from_container != layer_name:
|
if namespace_from_container != layer_name:
|
||||||
layers = stub.get_items(comps=True)
|
layers = stub.get_items(comps=True)
|
||||||
existing_layers = [layer.name for layer in layers]
|
existing_layers = [layer.name for layer in layers]
|
||||||
layer_name = get_unique_layer_name(
|
layer_name = get_unique_layer_name(
|
||||||
existing_layers,
|
existing_layers,
|
||||||
"{}_{}".format(context["asset"], context["subset"]))
|
"{}_{}".format(folder_name, product_name))
|
||||||
else: # switching version - keep same name
|
else: # switching version - keep same name
|
||||||
layer_name = container["namespace"]
|
layer_name = container["namespace"]
|
||||||
path = get_representation_path(representation)
|
path = get_representation_path(repre_doc)
|
||||||
# with aftereffects.maintained_selection(): # TODO
|
# with aftereffects.maintained_selection(): # TODO
|
||||||
stub.replace_item(layer.id, path, stub.LOADED_ICON + layer_name)
|
stub.replace_item(layer.id, path, stub.LOADED_ICON + layer_name)
|
||||||
stub.imprint(
|
stub.imprint(
|
||||||
layer.id, {"representation": str(representation["_id"]),
|
layer.id, {"representation": str(repre_doc["_id"]),
|
||||||
"name": context["subset"],
|
"name": product_name,
|
||||||
"namespace": layer_name}
|
"namespace": layer_name}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -103,5 +108,5 @@ class FileLoader(api.AfterEffectsLoader):
|
||||||
stub.imprint(layer.id, {})
|
stub.imprint(layer.id, {})
|
||||||
stub.delete_item(layer.id)
|
stub.delete_item(layer.id)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,8 @@ class CollectAERender(publish.AbstractCollectRender):
|
||||||
if not inst.data.get("active", True):
|
if not inst.data.get("active", True):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
family = inst.data["family"]
|
product_type = inst.data["productType"]
|
||||||
if family not in ["render", "renderLocal"]: # legacy
|
if product_type not in ["render", "renderLocal"]: # legacy
|
||||||
continue
|
continue
|
||||||
|
|
||||||
comp_id = int(inst.data["members"][0])
|
comp_id = int(inst.data["members"][0])
|
||||||
|
|
@ -81,29 +81,32 @@ class CollectAERender(publish.AbstractCollectRender):
|
||||||
fps = comp_info.frameRate
|
fps = comp_info.frameRate
|
||||||
# TODO add resolution when supported by extension
|
# TODO add resolution when supported by extension
|
||||||
|
|
||||||
task_name = inst.data.get("task") # legacy
|
task_name = inst.data.get("task")
|
||||||
|
|
||||||
render_q = CollectAERender.get_stub().get_render_info(comp_id)
|
render_q = CollectAERender.get_stub().get_render_info(comp_id)
|
||||||
if not render_q:
|
if not render_q:
|
||||||
raise ValueError("No file extension set in Render Queue")
|
raise ValueError("No file extension set in Render Queue")
|
||||||
render_item = render_q[0]
|
render_item = render_q[0]
|
||||||
|
|
||||||
|
product_type = "render"
|
||||||
instance_families = inst.data.get("families", [])
|
instance_families = inst.data.get("families", [])
|
||||||
subset_name = inst.data["subset"]
|
instance_families.append(product_type)
|
||||||
|
product_name = inst.data["productName"]
|
||||||
instance = AERenderInstance(
|
instance = AERenderInstance(
|
||||||
family="render",
|
productType=product_type,
|
||||||
|
family=product_type,
|
||||||
families=instance_families,
|
families=instance_families,
|
||||||
version=version,
|
version=version,
|
||||||
time="",
|
time="",
|
||||||
source=current_file,
|
source=current_file,
|
||||||
label="{} - {}".format(subset_name, family),
|
label="{} - {}".format(product_name, product_type),
|
||||||
subset=subset_name,
|
productName=product_name,
|
||||||
asset=inst.data["asset"],
|
folderPath=inst.data["folderPath"],
|
||||||
task=task_name,
|
task=task_name,
|
||||||
attachTo=False,
|
attachTo=False,
|
||||||
setMembers='',
|
setMembers='',
|
||||||
publish=True,
|
publish=True,
|
||||||
name=subset_name,
|
name=product_name,
|
||||||
resolutionWidth=render_item.width,
|
resolutionWidth=render_item.width,
|
||||||
resolutionHeight=render_item.height,
|
resolutionHeight=render_item.height,
|
||||||
pixelAspect=1,
|
pixelAspect=1,
|
||||||
|
|
@ -175,8 +178,8 @@ class CollectAERender(publish.AbstractCollectRender):
|
||||||
version_str = "v{:03d}".format(render_instance.version)
|
version_str = "v{:03d}".format(render_instance.version)
|
||||||
if "#" not in file_name: # single frame (mov)W
|
if "#" not in file_name: # single frame (mov)W
|
||||||
path = os.path.join(base_dir, "{}_{}_{}.{}".format(
|
path = os.path.join(base_dir, "{}_{}_{}.{}".format(
|
||||||
render_instance.asset,
|
render_instance.folderPath,
|
||||||
render_instance.subset,
|
render_instance.productName,
|
||||||
version_str,
|
version_str,
|
||||||
ext
|
ext
|
||||||
))
|
))
|
||||||
|
|
@ -184,8 +187,8 @@ class CollectAERender(publish.AbstractCollectRender):
|
||||||
else:
|
else:
|
||||||
for frame in range(start, end + 1):
|
for frame in range(start, end + 1):
|
||||||
path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format(
|
path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format(
|
||||||
render_instance.asset,
|
render_instance.folderPath,
|
||||||
render_instance.subset,
|
render_instance.productName,
|
||||||
version_str,
|
version_str,
|
||||||
str(frame).zfill(self.padding_width),
|
str(frame).zfill(self.padding_width),
|
||||||
ext
|
ext
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ Requires:
|
||||||
None
|
None
|
||||||
|
|
||||||
Provides:
|
Provides:
|
||||||
instance -> family ("review")
|
instance -> families ("review")
|
||||||
"""
|
"""
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,6 @@ import os
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from ayon_core.client import get_asset_name_identifier
|
|
||||||
from ayon_core.pipeline.create import get_subset_name
|
|
||||||
|
|
||||||
|
|
||||||
class CollectWorkfile(pyblish.api.ContextPlugin):
|
class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||||
""" Adds the AE render instances """
|
""" Adds the AE render instances """
|
||||||
|
|
@ -15,86 +12,24 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||||
default_variant = "Main"
|
default_variant = "Main"
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
existing_instance = None
|
workfile_instance = None
|
||||||
for instance in context:
|
for instance in context:
|
||||||
if instance.data["family"] == "workfile":
|
if instance.data["productType"] == "workfile":
|
||||||
self.log.debug("Workfile instance found, won't create new")
|
self.log.debug("Workfile instance found")
|
||||||
existing_instance = instance
|
workfile_instance = instance
|
||||||
break
|
break
|
||||||
|
|
||||||
current_file = context.data["currentFile"]
|
current_file = context.data["currentFile"]
|
||||||
staging_dir = os.path.dirname(current_file)
|
staging_dir = os.path.dirname(current_file)
|
||||||
scene_file = os.path.basename(current_file)
|
scene_file = os.path.basename(current_file)
|
||||||
if existing_instance is None: # old publish
|
if workfile_instance is None:
|
||||||
instance = self._get_new_instance(context, scene_file)
|
self.log.debug("Workfile instance not found. Skipping")
|
||||||
else:
|
return
|
||||||
instance = existing_instance
|
|
||||||
|
|
||||||
# creating representation
|
# creating representation
|
||||||
representation = {
|
workfile_instance.data["representations"].append({
|
||||||
'name': 'aep',
|
"name": "aep",
|
||||||
'ext': 'aep',
|
"ext": "aep",
|
||||||
'files': scene_file,
|
"files": scene_file,
|
||||||
"stagingDir": staging_dir,
|
"stagingDir": staging_dir,
|
||||||
}
|
|
||||||
|
|
||||||
if not instance.data.get("representations"):
|
|
||||||
instance.data["representations"] = []
|
|
||||||
instance.data["representations"].append(representation)
|
|
||||||
|
|
||||||
instance.data["publish"] = instance.data["active"] # for DL
|
|
||||||
|
|
||||||
def _get_new_instance(self, context, scene_file):
|
|
||||||
task = context.data["task"]
|
|
||||||
version = context.data["version"]
|
|
||||||
asset_entity = context.data["assetEntity"]
|
|
||||||
project_entity = context.data["projectEntity"]
|
|
||||||
|
|
||||||
asset_name = get_asset_name_identifier(asset_entity)
|
|
||||||
|
|
||||||
instance_data = {
|
|
||||||
"active": True,
|
|
||||||
"asset": asset_name,
|
|
||||||
"task": task,
|
|
||||||
"frameStart": context.data['frameStart'],
|
|
||||||
"frameEnd": context.data['frameEnd'],
|
|
||||||
"handleStart": context.data['handleStart'],
|
|
||||||
"handleEnd": context.data['handleEnd'],
|
|
||||||
"fps": asset_entity["data"]["fps"],
|
|
||||||
"resolutionWidth": asset_entity["data"].get(
|
|
||||||
"resolutionWidth",
|
|
||||||
project_entity["data"]["resolutionWidth"]),
|
|
||||||
"resolutionHeight": asset_entity["data"].get(
|
|
||||||
"resolutionHeight",
|
|
||||||
project_entity["data"]["resolutionHeight"]),
|
|
||||||
"pixelAspect": 1,
|
|
||||||
"step": 1,
|
|
||||||
"version": version
|
|
||||||
}
|
|
||||||
|
|
||||||
# workfile instance
|
|
||||||
family = "workfile"
|
|
||||||
subset = get_subset_name(
|
|
||||||
family,
|
|
||||||
self.default_variant,
|
|
||||||
context.data["anatomyData"]["task"]["name"],
|
|
||||||
context.data["assetEntity"],
|
|
||||||
context.data["anatomyData"]["project"]["name"],
|
|
||||||
host_name=context.data["hostName"],
|
|
||||||
project_settings=context.data["project_settings"]
|
|
||||||
)
|
|
||||||
# Create instance
|
|
||||||
instance = context.create_instance(subset)
|
|
||||||
|
|
||||||
# creating instance data
|
|
||||||
instance.data.update({
|
|
||||||
"subset": subset,
|
|
||||||
"label": scene_file,
|
|
||||||
"family": family,
|
|
||||||
"families": [family],
|
|
||||||
"representations": list()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
instance.data.update(instance_data)
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
<error id="main">
|
<error id="main">
|
||||||
<title>Subset context</title>
|
<title>Subset context</title>
|
||||||
<description>
|
<description>
|
||||||
## Invalid subset context
|
## Invalid product context
|
||||||
|
|
||||||
Context of the given subset doesn't match your current scene.
|
Context of the given product doesn't match your current scene.
|
||||||
|
|
||||||
### How to repair?
|
### How to repair?
|
||||||
|
|
||||||
|
|
@ -15,7 +15,7 @@ You can fix this with "repair" button on the right and refresh Publish at the bo
|
||||||
### __Detailed Info__ (optional)
|
### __Detailed Info__ (optional)
|
||||||
|
|
||||||
This might happen if you are reuse old workfile and open it in different context.
|
This might happen if you are reuse old workfile and open it in different context.
|
||||||
(Eg. you created subset "renderCompositingDefault" from asset "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing subset for "Robot" asset stayed in the workfile.)
|
(Eg. you created product name "renderCompositingDefault" from folder "Robot' in "your_project_Robot_compositing.aep", now you opened this workfile in a context "Sloth" but existing product for "Robot" asset stayed in the workfile.)
|
||||||
</detail>
|
</detail>
|
||||||
</error>
|
</error>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import json
|
|
||||||
import pyblish.api
|
|
||||||
from ayon_core.hosts.aftereffects.api import AfterEffectsHost
|
|
||||||
|
|
||||||
|
|
||||||
class PreCollectRender(pyblish.api.ContextPlugin):
|
|
||||||
"""
|
|
||||||
Checks if render instance is of old type, adds to families to both
|
|
||||||
existing collectors work same way.
|
|
||||||
|
|
||||||
Could be removed in the future when no one uses old publish.
|
|
||||||
"""
|
|
||||||
|
|
||||||
label = "PreCollect Render"
|
|
||||||
order = pyblish.api.CollectorOrder + 0.400
|
|
||||||
hosts = ["aftereffects"]
|
|
||||||
|
|
||||||
family_remapping = {
|
|
||||||
"render": ("render.farm", "farm"), # (family, label)
|
|
||||||
"renderLocal": ("render.local", "local")
|
|
||||||
}
|
|
||||||
|
|
||||||
def process(self, context):
|
|
||||||
if context.data.get("newPublishing"):
|
|
||||||
self.log.debug("Not applicable for New Publisher, skip")
|
|
||||||
return
|
|
||||||
|
|
||||||
for inst in AfterEffectsHost().list_instances():
|
|
||||||
if inst.get("creator_attributes"):
|
|
||||||
raise ValueError("Instance created in New publisher, "
|
|
||||||
"cannot be published in Pyblish.\n"
|
|
||||||
"Please publish in New Publisher "
|
|
||||||
"or recreate instances with legacy Creators")
|
|
||||||
|
|
||||||
if inst["family"] not in self.family_remapping.keys():
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not inst["members"]:
|
|
||||||
raise ValueError("Couldn't find id, unable to publish. " +
|
|
||||||
"Please recreate instance.")
|
|
||||||
|
|
||||||
instance = context.create_instance(inst["subset"])
|
|
||||||
inst["families"] = [self.family_remapping[inst["family"]][0]]
|
|
||||||
instance.data.update(inst)
|
|
||||||
|
|
||||||
self._debug_log(instance)
|
|
||||||
|
|
||||||
def _debug_log(self, instance):
|
|
||||||
def _default_json(value):
|
|
||||||
return str(value)
|
|
||||||
|
|
||||||
self.log.info(
|
|
||||||
json.dumps(instance.data, indent=4, default=_default_json)
|
|
||||||
)
|
|
||||||
|
|
@ -30,7 +30,7 @@ class ValidateInstanceAssetRepair(pyblish.api.Action):
|
||||||
for instance in instances:
|
for instance in instances:
|
||||||
data = stub.read(instance[0])
|
data = stub.read(instance[0])
|
||||||
|
|
||||||
data["asset"] = get_current_asset_name()
|
data["folderPath"] = get_current_asset_name()
|
||||||
stub.imprint(instance[0].instance_id, data)
|
stub.imprint(instance[0].instance_id, data)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin):
|
||||||
order = ValidateContentsOrder
|
order = ValidateContentsOrder
|
||||||
|
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
instance_asset = instance.data["asset"]
|
instance_asset = instance.data["folderPath"]
|
||||||
current_asset = get_current_asset_name()
|
current_asset = get_current_asset_name()
|
||||||
msg = (
|
msg = (
|
||||||
f"Instance asset {instance_asset} is not the same "
|
f"Instance asset {instance_asset} is not the same "
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ from ayon_core.pipeline import (
|
||||||
deregister_loader_plugin_path,
|
deregister_loader_plugin_path,
|
||||||
deregister_creator_plugin_path,
|
deregister_creator_plugin_path,
|
||||||
AVALON_CONTAINER_ID,
|
AVALON_CONTAINER_ID,
|
||||||
|
AYON_CONTAINER_ID,
|
||||||
)
|
)
|
||||||
from ayon_core.lib import (
|
from ayon_core.lib import (
|
||||||
Logger,
|
Logger,
|
||||||
|
|
@ -563,8 +564,9 @@ def ls() -> Iterator:
|
||||||
called containers.
|
called containers.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for container in lib.lsattr("id", AVALON_CONTAINER_ID):
|
for id_type in {AYON_CONTAINER_ID, AVALON_CONTAINER_ID}:
|
||||||
yield parse_container(container)
|
for container in lib.lsattr("id", id_type):
|
||||||
|
yield parse_container(container)
|
||||||
|
|
||||||
|
|
||||||
def publish():
|
def publish():
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ from ayon_core.pipeline import (
|
||||||
Creator,
|
Creator,
|
||||||
CreatedInstance,
|
CreatedInstance,
|
||||||
LoaderPlugin,
|
LoaderPlugin,
|
||||||
|
AVALON_INSTANCE_ID,
|
||||||
|
AYON_INSTANCE_ID,
|
||||||
)
|
)
|
||||||
from ayon_core.lib import BoolDef
|
from ayon_core.lib import BoolDef
|
||||||
|
|
||||||
|
|
@ -28,13 +30,13 @@ VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx"]
|
||||||
|
|
||||||
|
|
||||||
def prepare_scene_name(
|
def prepare_scene_name(
|
||||||
asset: str, subset: str, namespace: Optional[str] = None
|
folder_name: str, product_name: str, namespace: Optional[str] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Return a consistent name for an asset."""
|
"""Return a consistent name for an asset."""
|
||||||
name = f"{asset}"
|
name = f"{folder_name}"
|
||||||
if namespace:
|
if namespace:
|
||||||
name = f"{name}_{namespace}"
|
name = f"{name}_{namespace}"
|
||||||
name = f"{name}_{subset}"
|
name = f"{name}_{product_name}"
|
||||||
|
|
||||||
# Blender name for a collection or object cannot be longer than 63
|
# Blender name for a collection or object cannot be longer than 63
|
||||||
# characters. If the name is longer, it will raise an error.
|
# characters. If the name is longer, it will raise an error.
|
||||||
|
|
@ -45,7 +47,7 @@ def prepare_scene_name(
|
||||||
|
|
||||||
|
|
||||||
def get_unique_number(
|
def get_unique_number(
|
||||||
asset: str, subset: str
|
folder_name: str, product_name: str
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Return a unique number based on the asset name."""
|
"""Return a unique number based on the asset name."""
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
|
|
@ -62,10 +64,10 @@ def get_unique_number(
|
||||||
if c.get(AVALON_PROPERTY)}
|
if c.get(AVALON_PROPERTY)}
|
||||||
container_names = obj_group_names.union(coll_group_names)
|
container_names = obj_group_names.union(coll_group_names)
|
||||||
count = 1
|
count = 1
|
||||||
name = f"{asset}_{count:0>2}_{subset}"
|
name = f"{folder_name}_{count:0>2}_{product_name}"
|
||||||
while name in container_names:
|
while name in container_names:
|
||||||
count += 1
|
count += 1
|
||||||
name = f"{asset}_{count:0>2}_{subset}"
|
name = f"{folder_name}_{count:0>2}_{product_name}"
|
||||||
return f"{count:0>2}"
|
return f"{count:0>2}"
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -159,24 +161,22 @@ class BaseCreator(Creator):
|
||||||
create_as_asset_group = False
|
create_as_asset_group = False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def cache_subsets(shared_data):
|
def cache_instance_data(shared_data):
|
||||||
"""Cache instances for Creators shared data.
|
"""Cache instances for Creators shared data.
|
||||||
|
|
||||||
Create `blender_cached_subsets` key when needed in shared data and
|
Create `blender_cached_instances` key when needed in shared data and
|
||||||
fill it with all collected instances from the scene under its
|
fill it with all collected instances from the scene under its
|
||||||
respective creator identifiers.
|
respective creator identifiers.
|
||||||
|
|
||||||
If legacy instances are detected in the scene, create
|
If legacy instances are detected in the scene, create
|
||||||
`blender_cached_legacy_subsets` key and fill it with
|
`blender_cached_legacy_instances` key and fill it with
|
||||||
all legacy subsets from this family as a value. # key or value?
|
all legacy products from this family as a value. # key or value?
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
shared_data(Dict[str, Any]): Shared data.
|
shared_data(Dict[str, Any]): Shared data.
|
||||||
|
|
||||||
Return:
|
|
||||||
Dict[str, Any]: Shared data with cached subsets.
|
|
||||||
"""
|
"""
|
||||||
if not shared_data.get('blender_cached_subsets'):
|
if not shared_data.get('blender_cached_instances'):
|
||||||
cache = {}
|
cache = {}
|
||||||
cache_legacy = {}
|
cache_legacy = {}
|
||||||
|
|
||||||
|
|
@ -193,7 +193,9 @@ class BaseCreator(Creator):
|
||||||
if not avalon_prop:
|
if not avalon_prop:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if avalon_prop.get('id') != 'pyblish.avalon.instance':
|
if avalon_prop.get('id') not in {
|
||||||
|
AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||||
|
}:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
creator_id = avalon_prop.get('creator_identifier')
|
creator_id = avalon_prop.get('creator_identifier')
|
||||||
|
|
@ -206,19 +208,19 @@ class BaseCreator(Creator):
|
||||||
# Legacy creator instance
|
# Legacy creator instance
|
||||||
cache_legacy.setdefault(family, []).append(obj_or_col)
|
cache_legacy.setdefault(family, []).append(obj_or_col)
|
||||||
|
|
||||||
shared_data["blender_cached_subsets"] = cache
|
shared_data["blender_cached_instances"] = cache
|
||||||
shared_data["blender_cached_legacy_subsets"] = cache_legacy
|
shared_data["blender_cached_legacy_instances"] = cache_legacy
|
||||||
|
|
||||||
return shared_data
|
return shared_data
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
"""Override abstract method from Creator.
|
"""Override abstract method from Creator.
|
||||||
Create new instance and store it.
|
Create new instance and store it.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
subset_name(str): Subset name of created instance.
|
product_name(str): Subset name of created instance.
|
||||||
instance_data(dict): Instance base data.
|
instance_data(dict): Instance base data.
|
||||||
pre_create_data(dict): Data based on pre creation attributes.
|
pre_create_data(dict): Data based on pre creation attributes.
|
||||||
Those may affect how creator works.
|
Those may affect how creator works.
|
||||||
|
|
@ -232,7 +234,7 @@ class BaseCreator(Creator):
|
||||||
# Create asset group
|
# Create asset group
|
||||||
asset_name = instance_data["folderPath"].split("/")[-1]
|
asset_name = instance_data["folderPath"].split("/")[-1]
|
||||||
|
|
||||||
name = prepare_scene_name(asset_name, subset_name)
|
name = prepare_scene_name(asset_name, product_name)
|
||||||
if self.create_as_asset_group:
|
if self.create_as_asset_group:
|
||||||
# Create instance as empty
|
# Create instance as empty
|
||||||
instance_node = bpy.data.objects.new(name=name, object_data=None)
|
instance_node = bpy.data.objects.new(name=name, object_data=None)
|
||||||
|
|
@ -243,10 +245,10 @@ class BaseCreator(Creator):
|
||||||
instance_node = bpy.data.collections.new(name=name)
|
instance_node = bpy.data.collections.new(name=name)
|
||||||
instances.children.link(instance_node)
|
instances.children.link(instance_node)
|
||||||
|
|
||||||
self.set_instance_data(subset_name, instance_data)
|
self.set_instance_data(product_name, instance_data)
|
||||||
|
|
||||||
instance = CreatedInstance(
|
instance = CreatedInstance(
|
||||||
self.family, subset_name, instance_data, self
|
self.product_type, product_name, instance_data, self
|
||||||
)
|
)
|
||||||
instance.transient_data["instance_node"] = instance_node
|
instance.transient_data["instance_node"] = instance_node
|
||||||
self._add_instance_to_context(instance)
|
self._add_instance_to_context(instance)
|
||||||
|
|
@ -259,18 +261,18 @@ class BaseCreator(Creator):
|
||||||
"""Override abstract method from BaseCreator.
|
"""Override abstract method from BaseCreator.
|
||||||
Collect existing instances related to this creator plugin."""
|
Collect existing instances related to this creator plugin."""
|
||||||
|
|
||||||
# Cache subsets in shared data
|
# Cache instances in shared data
|
||||||
self.cache_subsets(self.collection_shared_data)
|
self.cache_instance_data(self.collection_shared_data)
|
||||||
|
|
||||||
# Get cached subsets
|
# Get cached instances
|
||||||
cached_subsets = self.collection_shared_data.get(
|
cached_instances = self.collection_shared_data.get(
|
||||||
"blender_cached_subsets"
|
"blender_cached_instances"
|
||||||
)
|
)
|
||||||
if not cached_subsets:
|
if not cached_instances:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Process only instances that were created by this creator
|
# Process only instances that were created by this creator
|
||||||
for instance_node in cached_subsets.get(self.identifier, []):
|
for instance_node in cached_instances.get(self.identifier, []):
|
||||||
property = instance_node.get(AVALON_PROPERTY)
|
property = instance_node.get(AVALON_PROPERTY)
|
||||||
# Create instance object from existing data
|
# Create instance object from existing data
|
||||||
instance = CreatedInstance.from_existing(
|
instance = CreatedInstance.from_existing(
|
||||||
|
|
@ -302,16 +304,17 @@ class BaseCreator(Creator):
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Rename the instance node in the scene if subset or asset changed.
|
# Rename the instance node in the scene if product
|
||||||
|
# or folder changed.
|
||||||
# Do not rename the instance if the family is workfile, as the
|
# Do not rename the instance if the family is workfile, as the
|
||||||
# workfile instance is included in the AVALON_CONTAINER collection.
|
# workfile instance is included in the AVALON_CONTAINER collection.
|
||||||
if (
|
if (
|
||||||
"subset" in changes.changed_keys
|
"productName" in changes.changed_keys
|
||||||
or "folderPath" in changes.changed_keys
|
or "folderPath" in changes.changed_keys
|
||||||
) and created_instance.family != "workfile":
|
) and created_instance.product_type != "workfile":
|
||||||
asset_name = data["folderPath"].split("/")[-1]
|
asset_name = data["folderPath"].split("/")[-1]
|
||||||
name = prepare_scene_name(
|
name = prepare_scene_name(
|
||||||
asset=asset_name, subset=data["subset"]
|
asset_name, data["productName"]
|
||||||
)
|
)
|
||||||
node.name = name
|
node.name = name
|
||||||
|
|
||||||
|
|
@ -337,13 +340,13 @@ class BaseCreator(Creator):
|
||||||
|
|
||||||
def set_instance_data(
|
def set_instance_data(
|
||||||
self,
|
self,
|
||||||
subset_name: str,
|
product_name: str,
|
||||||
instance_data: dict
|
instance_data: dict
|
||||||
):
|
):
|
||||||
"""Fill instance data with required items.
|
"""Fill instance data with required items.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
subset_name(str): Subset name of created instance.
|
product_name(str): Subset name of created instance.
|
||||||
instance_data(dict): Instance base data.
|
instance_data(dict): Instance base data.
|
||||||
instance_node(bpy.types.ID): Instance node in blender scene.
|
instance_node(bpy.types.ID): Instance node in blender scene.
|
||||||
"""
|
"""
|
||||||
|
|
@ -352,9 +355,9 @@ class BaseCreator(Creator):
|
||||||
|
|
||||||
instance_data.update(
|
instance_data.update(
|
||||||
{
|
{
|
||||||
"id": "pyblish.avalon.instance",
|
"id": AVALON_INSTANCE_ID,
|
||||||
"creator_identifier": self.identifier,
|
"creator_identifier": self.identifier,
|
||||||
"subset": subset_name,
|
"productName": product_name,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -462,14 +465,14 @@ class AssetLoader(LoaderPlugin):
|
||||||
filepath = self.filepath_from_context(context)
|
filepath = self.filepath_from_context(context)
|
||||||
assert Path(filepath).exists(), f"{filepath} doesn't exist."
|
assert Path(filepath).exists(), f"{filepath} doesn't exist."
|
||||||
|
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
unique_number = get_unique_number(
|
unique_number = get_unique_number(
|
||||||
asset, subset
|
folder_name, product_name
|
||||||
)
|
)
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
name = name or prepare_scene_name(
|
name = name or prepare_scene_name(
|
||||||
asset, subset, unique_number
|
folder_name, product_name, unique_number
|
||||||
)
|
)
|
||||||
|
|
||||||
nodes = self.process_asset(
|
nodes = self.process_asset(
|
||||||
|
|
@ -495,21 +498,21 @@ class AssetLoader(LoaderPlugin):
|
||||||
# loader=self.__class__.__name__,
|
# loader=self.__class__.__name__,
|
||||||
# )
|
# )
|
||||||
|
|
||||||
# asset = context["asset"]["name"]
|
# folder_name = context["asset"]["name"]
|
||||||
# subset = context["subset"]["name"]
|
# product_name = context["subset"]["name"]
|
||||||
# instance_name = prepare_scene_name(
|
# instance_name = prepare_scene_name(
|
||||||
# asset, subset, unique_number
|
# folder_name, product_name, unique_number
|
||||||
# ) + '_CON'
|
# ) + '_CON'
|
||||||
|
|
||||||
# return self._get_instance_collection(instance_name, nodes)
|
# return self._get_instance_collection(instance_name, nodes)
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""Must be implemented by a sub-class"""
|
"""Must be implemented by a sub-class"""
|
||||||
raise NotImplementedError("Must be implemented by a sub-class")
|
raise NotImplementedError("Must be implemented by a sub-class")
|
||||||
|
|
||||||
def update(self, container: Dict, representation: Dict):
|
def update(self, container: Dict, context: Dict):
|
||||||
""" Run the update on Blender main thread"""
|
""" Run the update on Blender main thread"""
|
||||||
mti = MainThreadItem(self.exec_update, container, representation)
|
mti = MainThreadItem(self.exec_update, container, context)
|
||||||
execute_in_main_thread(mti)
|
execute_in_main_thread(mti)
|
||||||
|
|
||||||
def exec_remove(self, container: Dict) -> bool:
|
def exec_remove(self, container: Dict) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,22 @@ def get_multilayer(settings):
|
||||||
["multilayer_exr"])
|
["multilayer_exr"])
|
||||||
|
|
||||||
|
|
||||||
|
def get_renderer(settings):
|
||||||
|
"""Get renderer from blender settings."""
|
||||||
|
|
||||||
|
return (settings["blender"]
|
||||||
|
["RenderSettings"]
|
||||||
|
["renderer"])
|
||||||
|
|
||||||
|
|
||||||
|
def get_compositing(settings):
|
||||||
|
"""Get compositing from blender settings."""
|
||||||
|
|
||||||
|
return (settings["blender"]
|
||||||
|
["RenderSettings"]
|
||||||
|
["compositing"])
|
||||||
|
|
||||||
|
|
||||||
def get_render_product(output_path, name, aov_sep):
|
def get_render_product(output_path, name, aov_sep):
|
||||||
"""
|
"""
|
||||||
Generate the path to the render product. Blender interprets the `#`
|
Generate the path to the render product. Blender interprets the `#`
|
||||||
|
|
@ -91,66 +107,120 @@ def set_render_format(ext, multilayer):
|
||||||
image_settings.file_format = "TIFF"
|
image_settings.file_format = "TIFF"
|
||||||
|
|
||||||
|
|
||||||
def set_render_passes(settings):
|
def set_render_passes(settings, renderer):
|
||||||
aov_list = (settings["blender"]
|
aov_list = set(settings["blender"]["RenderSettings"]["aov_list"])
|
||||||
["RenderSettings"]
|
custom_passes = settings["blender"]["RenderSettings"]["custom_passes"]
|
||||||
["aov_list"])
|
|
||||||
|
|
||||||
custom_passes = (settings["blender"]
|
|
||||||
["RenderSettings"]
|
|
||||||
["custom_passes"])
|
|
||||||
|
|
||||||
|
# Common passes for both renderers
|
||||||
vl = bpy.context.view_layer
|
vl = bpy.context.view_layer
|
||||||
|
|
||||||
|
# Data Passes
|
||||||
vl.use_pass_combined = "combined" in aov_list
|
vl.use_pass_combined = "combined" in aov_list
|
||||||
vl.use_pass_z = "z" in aov_list
|
vl.use_pass_z = "z" in aov_list
|
||||||
vl.use_pass_mist = "mist" in aov_list
|
vl.use_pass_mist = "mist" in aov_list
|
||||||
vl.use_pass_normal = "normal" in aov_list
|
vl.use_pass_normal = "normal" in aov_list
|
||||||
|
|
||||||
|
# Light Passes
|
||||||
vl.use_pass_diffuse_direct = "diffuse_light" in aov_list
|
vl.use_pass_diffuse_direct = "diffuse_light" in aov_list
|
||||||
vl.use_pass_diffuse_color = "diffuse_color" in aov_list
|
vl.use_pass_diffuse_color = "diffuse_color" in aov_list
|
||||||
vl.use_pass_glossy_direct = "specular_light" in aov_list
|
vl.use_pass_glossy_direct = "specular_light" in aov_list
|
||||||
vl.use_pass_glossy_color = "specular_color" in aov_list
|
vl.use_pass_glossy_color = "specular_color" in aov_list
|
||||||
vl.eevee.use_pass_volume_direct = "volume_light" in aov_list
|
|
||||||
vl.use_pass_emit = "emission" in aov_list
|
vl.use_pass_emit = "emission" in aov_list
|
||||||
vl.use_pass_environment = "environment" in aov_list
|
vl.use_pass_environment = "environment" in aov_list
|
||||||
vl.use_pass_shadow = "shadow" in aov_list
|
|
||||||
vl.use_pass_ambient_occlusion = "ao" in aov_list
|
vl.use_pass_ambient_occlusion = "ao" in aov_list
|
||||||
|
|
||||||
cycles = vl.cycles
|
# Cryptomatte Passes
|
||||||
|
vl.use_pass_cryptomatte_object = "cryptomatte_object" in aov_list
|
||||||
|
vl.use_pass_cryptomatte_material = "cryptomatte_material" in aov_list
|
||||||
|
vl.use_pass_cryptomatte_asset = "cryptomatte_asset" in aov_list
|
||||||
|
|
||||||
cycles.denoising_store_passes = "denoising" in aov_list
|
if renderer == "BLENDER_EEVEE":
|
||||||
cycles.use_pass_volume_direct = "volume_direct" in aov_list
|
# Eevee exclusive passes
|
||||||
cycles.use_pass_volume_indirect = "volume_indirect" in aov_list
|
eevee = vl.eevee
|
||||||
|
|
||||||
|
# Light Passes
|
||||||
|
vl.use_pass_shadow = "shadow" in aov_list
|
||||||
|
eevee.use_pass_volume_direct = "volume_light" in aov_list
|
||||||
|
|
||||||
|
# Effects Passes
|
||||||
|
eevee.use_pass_bloom = "bloom" in aov_list
|
||||||
|
eevee.use_pass_transparent = "transparent" in aov_list
|
||||||
|
|
||||||
|
# Cryptomatte Passes
|
||||||
|
vl.use_pass_cryptomatte_accurate = "cryptomatte_accurate" in aov_list
|
||||||
|
elif renderer == "CYCLES":
|
||||||
|
# Cycles exclusive passes
|
||||||
|
cycles = vl.cycles
|
||||||
|
|
||||||
|
# Data Passes
|
||||||
|
vl.use_pass_position = "position" in aov_list
|
||||||
|
vl.use_pass_vector = "vector" in aov_list
|
||||||
|
vl.use_pass_uv = "uv" in aov_list
|
||||||
|
cycles.denoising_store_passes = "denoising" in aov_list
|
||||||
|
vl.use_pass_object_index = "object_index" in aov_list
|
||||||
|
vl.use_pass_material_index = "material_index" in aov_list
|
||||||
|
cycles.pass_debug_sample_count = "sample_count" in aov_list
|
||||||
|
|
||||||
|
# Light Passes
|
||||||
|
vl.use_pass_diffuse_indirect = "diffuse_indirect" in aov_list
|
||||||
|
vl.use_pass_glossy_indirect = "specular_indirect" in aov_list
|
||||||
|
vl.use_pass_transmission_direct = "transmission_direct" in aov_list
|
||||||
|
vl.use_pass_transmission_indirect = "transmission_indirect" in aov_list
|
||||||
|
vl.use_pass_transmission_color = "transmission_color" in aov_list
|
||||||
|
cycles.use_pass_volume_direct = "volume_light" in aov_list
|
||||||
|
cycles.use_pass_volume_indirect = "volume_indirect" in aov_list
|
||||||
|
cycles.use_pass_shadow_catcher = "shadow" in aov_list
|
||||||
|
|
||||||
aovs_names = [aov.name for aov in vl.aovs]
|
aovs_names = [aov.name for aov in vl.aovs]
|
||||||
for cp in custom_passes:
|
for cp in custom_passes:
|
||||||
cp_name = cp[0]
|
cp_name = cp["attribute"]
|
||||||
if cp_name not in aovs_names:
|
if cp_name not in aovs_names:
|
||||||
aov = vl.aovs.add()
|
aov = vl.aovs.add()
|
||||||
aov.name = cp_name
|
aov.name = cp_name
|
||||||
else:
|
else:
|
||||||
aov = vl.aovs[cp_name]
|
aov = vl.aovs[cp_name]
|
||||||
aov.type = cp[1].get("type", "VALUE")
|
aov.type = cp["value"]
|
||||||
|
|
||||||
return aov_list, custom_passes
|
return list(aov_list), custom_passes
|
||||||
|
|
||||||
|
|
||||||
def set_node_tree(output_path, name, aov_sep, ext, multilayer):
|
def _create_aov_slot(name, aov_sep, slots, rpass_name, multi_exr, output_path):
|
||||||
|
filename = f"{name}{aov_sep}{rpass_name}.####"
|
||||||
|
slot = slots.new(rpass_name if multi_exr else filename)
|
||||||
|
filepath = str(output_path / filename.lstrip("/"))
|
||||||
|
|
||||||
|
return slot, filepath
|
||||||
|
|
||||||
|
|
||||||
|
def set_node_tree(
|
||||||
|
output_path, render_product, name, aov_sep, ext, multilayer, compositing
|
||||||
|
):
|
||||||
# Set the scene to use the compositor node tree to render
|
# Set the scene to use the compositor node tree to render
|
||||||
bpy.context.scene.use_nodes = True
|
bpy.context.scene.use_nodes = True
|
||||||
|
|
||||||
tree = bpy.context.scene.node_tree
|
tree = bpy.context.scene.node_tree
|
||||||
|
|
||||||
# Get the Render Layers node
|
comp_layer_type = "CompositorNodeRLayers"
|
||||||
rl_node = None
|
output_type = "CompositorNodeOutputFile"
|
||||||
|
compositor_type = "CompositorNodeComposite"
|
||||||
|
|
||||||
|
# Get the Render Layer, Composite and the previous output nodes
|
||||||
|
render_layer_node = None
|
||||||
|
composite_node = None
|
||||||
|
old_output_node = None
|
||||||
for node in tree.nodes:
|
for node in tree.nodes:
|
||||||
if node.bl_idname == "CompositorNodeRLayers":
|
if node.bl_idname == comp_layer_type:
|
||||||
rl_node = node
|
render_layer_node = node
|
||||||
|
elif node.bl_idname == compositor_type:
|
||||||
|
composite_node = node
|
||||||
|
elif node.bl_idname == output_type and "AYON" in node.name:
|
||||||
|
old_output_node = node
|
||||||
|
if render_layer_node and composite_node and old_output_node:
|
||||||
break
|
break
|
||||||
|
|
||||||
# If there's not a Render Layers node, we create it
|
# If there's not a Render Layers node, we create it
|
||||||
if not rl_node:
|
if not render_layer_node:
|
||||||
rl_node = tree.nodes.new("CompositorNodeRLayers")
|
render_layer_node = tree.nodes.new(comp_layer_type)
|
||||||
|
|
||||||
# Get the enabled output sockets, that are the active passes for the
|
# Get the enabled output sockets, that are the active passes for the
|
||||||
# render.
|
# render.
|
||||||
|
|
@ -158,48 +228,81 @@ def set_node_tree(output_path, name, aov_sep, ext, multilayer):
|
||||||
exclude_sockets = ["Image", "Alpha", "Noisy Image"]
|
exclude_sockets = ["Image", "Alpha", "Noisy Image"]
|
||||||
passes = [
|
passes = [
|
||||||
socket
|
socket
|
||||||
for socket in rl_node.outputs
|
for socket in render_layer_node.outputs
|
||||||
if socket.enabled and socket.name not in exclude_sockets
|
if socket.enabled and socket.name not in exclude_sockets
|
||||||
]
|
]
|
||||||
|
|
||||||
# Remove all output nodes
|
|
||||||
for node in tree.nodes:
|
|
||||||
if node.bl_idname == "CompositorNodeOutputFile":
|
|
||||||
tree.nodes.remove(node)
|
|
||||||
|
|
||||||
# Create a new output node
|
# Create a new output node
|
||||||
output = tree.nodes.new("CompositorNodeOutputFile")
|
output = tree.nodes.new(output_type)
|
||||||
|
|
||||||
image_settings = bpy.context.scene.render.image_settings
|
image_settings = bpy.context.scene.render.image_settings
|
||||||
output.format.file_format = image_settings.file_format
|
output.format.file_format = image_settings.file_format
|
||||||
|
|
||||||
|
slots = None
|
||||||
|
|
||||||
# In case of a multilayer exr, we don't need to use the output node,
|
# In case of a multilayer exr, we don't need to use the output node,
|
||||||
# because the blender render already outputs a multilayer exr.
|
# because the blender render already outputs a multilayer exr.
|
||||||
if ext == "exr" and multilayer:
|
multi_exr = ext == "exr" and multilayer
|
||||||
output.layer_slots.clear()
|
slots = output.layer_slots if multi_exr else output.file_slots
|
||||||
return []
|
output.base_path = render_product if multi_exr else str(output_path)
|
||||||
|
|
||||||
output.file_slots.clear()
|
slots.clear()
|
||||||
output.base_path = str(output_path)
|
|
||||||
|
|
||||||
aov_file_products = []
|
aov_file_products = []
|
||||||
|
|
||||||
|
old_links = {
|
||||||
|
link.from_socket.name: link for link in tree.links
|
||||||
|
if link.to_node == old_output_node}
|
||||||
|
|
||||||
|
# Create a new socket for the beauty output
|
||||||
|
pass_name = "rgba" if multi_exr else "beauty"
|
||||||
|
slot, _ = _create_aov_slot(
|
||||||
|
name, aov_sep, slots, pass_name, multi_exr, output_path)
|
||||||
|
tree.links.new(render_layer_node.outputs["Image"], slot)
|
||||||
|
|
||||||
|
if compositing:
|
||||||
|
# Create a new socket for the composite output
|
||||||
|
pass_name = "composite"
|
||||||
|
comp_socket, filepath = _create_aov_slot(
|
||||||
|
name, aov_sep, slots, pass_name, multi_exr, output_path)
|
||||||
|
aov_file_products.append(("Composite", filepath))
|
||||||
|
|
||||||
# For each active render pass, we add a new socket to the output node
|
# For each active render pass, we add a new socket to the output node
|
||||||
# and link it
|
# and link it
|
||||||
for render_pass in passes:
|
for rpass in passes:
|
||||||
filepath = f"{name}{aov_sep}{render_pass.name}.####"
|
slot, filepath = _create_aov_slot(
|
||||||
|
name, aov_sep, slots, rpass.name, multi_exr, output_path)
|
||||||
|
aov_file_products.append((rpass.name, filepath))
|
||||||
|
|
||||||
output.file_slots.new(filepath)
|
# If the rpass was not connected with the old output node, we connect
|
||||||
|
# it with the new one.
|
||||||
|
if not old_links.get(rpass.name):
|
||||||
|
tree.links.new(rpass, slot)
|
||||||
|
|
||||||
filename = str(output_path / filepath.lstrip("/"))
|
for link in list(old_links.values()):
|
||||||
|
# Check if the socket is still available in the new output node.
|
||||||
|
socket = output.inputs.get(link.to_socket.name)
|
||||||
|
# If it is, we connect it with the new output node.
|
||||||
|
if socket:
|
||||||
|
tree.links.new(link.from_socket, socket)
|
||||||
|
# Then, we remove the old link.
|
||||||
|
tree.links.remove(link)
|
||||||
|
|
||||||
aov_file_products.append((render_pass.name, filename))
|
# If there's a composite node, we connect its input with the new output
|
||||||
|
if compositing and composite_node:
|
||||||
|
for link in tree.links:
|
||||||
|
if link.to_node == composite_node:
|
||||||
|
tree.links.new(link.from_socket, comp_socket)
|
||||||
|
break
|
||||||
|
|
||||||
node_input = output.inputs[-1]
|
if old_output_node:
|
||||||
|
output.location = old_output_node.location
|
||||||
|
tree.nodes.remove(old_output_node)
|
||||||
|
|
||||||
tree.links.new(render_pass, node_input)
|
output.name = "AYON File Output"
|
||||||
|
output.label = "AYON File Output"
|
||||||
|
|
||||||
return aov_file_products
|
return [] if multi_exr else aov_file_products
|
||||||
|
|
||||||
|
|
||||||
def imprint_render_settings(node, data):
|
def imprint_render_settings(node, data):
|
||||||
|
|
@ -228,17 +331,23 @@ def prepare_rendering(asset_group):
|
||||||
aov_sep = get_aov_separator(settings)
|
aov_sep = get_aov_separator(settings)
|
||||||
ext = get_image_format(settings)
|
ext = get_image_format(settings)
|
||||||
multilayer = get_multilayer(settings)
|
multilayer = get_multilayer(settings)
|
||||||
|
renderer = get_renderer(settings)
|
||||||
|
compositing = get_compositing(settings)
|
||||||
|
|
||||||
set_render_format(ext, multilayer)
|
set_render_format(ext, multilayer)
|
||||||
aov_list, custom_passes = set_render_passes(settings)
|
bpy.context.scene.render.engine = renderer
|
||||||
|
aov_list, custom_passes = set_render_passes(settings, renderer)
|
||||||
|
|
||||||
output_path = Path.joinpath(dirpath, render_folder, file_name)
|
output_path = Path.joinpath(dirpath, render_folder, file_name)
|
||||||
|
|
||||||
render_product = get_render_product(output_path, name, aov_sep)
|
render_product = get_render_product(output_path, name, aov_sep)
|
||||||
aov_file_product = set_node_tree(
|
aov_file_product = set_node_tree(
|
||||||
output_path, name, aov_sep, ext, multilayer)
|
output_path, render_product, name, aov_sep,
|
||||||
|
ext, multilayer, compositing)
|
||||||
|
|
||||||
bpy.context.scene.render.filepath = render_product
|
# Clear the render filepath, so that the output is handled only by the
|
||||||
|
# output node in the compositor.
|
||||||
|
bpy.context.scene.render.filepath = ""
|
||||||
|
|
||||||
render_settings = {
|
render_settings = {
|
||||||
"render_folder": render_folder,
|
"render_folder": render_folder,
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""Converter for legacy Houdini subsets."""
|
"""Converter for legacy Houdini products."""
|
||||||
from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin
|
from ayon_core.pipeline.create.creator_plugins import SubsetConvertorPlugin
|
||||||
from ayon_core.hosts.blender.api.lib import imprint
|
from ayon_core.hosts.blender.api.lib import imprint
|
||||||
|
|
||||||
|
|
||||||
class BlenderLegacyConvertor(SubsetConvertorPlugin):
|
class BlenderLegacyConvertor(SubsetConvertorPlugin):
|
||||||
"""Find and convert any legacy subsets in the scene.
|
"""Find and convert any legacy products in the scene.
|
||||||
|
|
||||||
This Converter will find all legacy subsets in the scene and will
|
This Converter will find all legacy products in the scene and will
|
||||||
transform them to the current system. Since the old subsets doesn't
|
transform them to the current system. Since the old products doesn't
|
||||||
retain any information about their original creators, the only mapping
|
retain any information about their original creators, the only mapping
|
||||||
we can do is based on their families.
|
we can do is based on their product types.
|
||||||
|
|
||||||
Its limitation is that you can have multiple creators creating subset
|
Its limitation is that you can have multiple creators creating product
|
||||||
of the same family and there is no way to handle it. This code should
|
of the same product type and there is no way to handle it. This code
|
||||||
nevertheless cover all creators that came with OpenPype.
|
should nevertheless cover all creators that came with OpenPype.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
identifier = "io.openpype.creators.blender.legacy"
|
identifier = "io.openpype.creators.blender.legacy"
|
||||||
family_to_id = {
|
product_type_to_id = {
|
||||||
"action": "io.openpype.creators.blender.action",
|
"action": "io.openpype.creators.blender.action",
|
||||||
"camera": "io.openpype.creators.blender.camera",
|
"camera": "io.openpype.creators.blender.camera",
|
||||||
"animation": "io.openpype.creators.blender.animation",
|
"animation": "io.openpype.creators.blender.animation",
|
||||||
|
|
@ -33,42 +33,42 @@ class BlenderLegacyConvertor(SubsetConvertorPlugin):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(BlenderLegacyConvertor, self).__init__(*args, **kwargs)
|
super(BlenderLegacyConvertor, self).__init__(*args, **kwargs)
|
||||||
self.legacy_subsets = {}
|
self.legacy_instances = {}
|
||||||
|
|
||||||
def find_instances(self):
|
def find_instances(self):
|
||||||
"""Find legacy subsets in the scene.
|
"""Find legacy products in the scene.
|
||||||
|
|
||||||
Legacy subsets are the ones that doesn't have `creator_identifier`
|
Legacy products are the ones that doesn't have `creator_identifier`
|
||||||
parameter on them.
|
parameter on them.
|
||||||
|
|
||||||
This is using cached entries done in
|
This is using cached entries done in
|
||||||
:py:meth:`~BaseCreator.cache_subsets()`
|
:py:meth:`~BaseCreator.cache_instance_data()`
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.legacy_subsets = self.collection_shared_data.get(
|
self.legacy_instances = self.collection_shared_data.get(
|
||||||
"blender_cached_legacy_subsets")
|
"blender_cached_legacy_instances")
|
||||||
if not self.legacy_subsets:
|
if not self.legacy_instances:
|
||||||
return
|
return
|
||||||
self.add_convertor_item(
|
self.add_convertor_item(
|
||||||
"Found {} incompatible subset{}".format(
|
"Found {} incompatible product{}".format(
|
||||||
len(self.legacy_subsets),
|
len(self.legacy_instances),
|
||||||
"s" if len(self.legacy_subsets) > 1 else ""
|
"s" if len(self.legacy_instances) > 1 else ""
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def convert(self):
|
def convert(self):
|
||||||
"""Convert all legacy subsets to current.
|
"""Convert all legacy products to current.
|
||||||
|
|
||||||
It is enough to add `creator_identifier` and `instance_node`.
|
It is enough to add `creator_identifier` and `instance_node`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not self.legacy_subsets:
|
if not self.legacy_instances:
|
||||||
return
|
return
|
||||||
|
|
||||||
for family, instance_nodes in self.legacy_subsets.items():
|
for product_type, instance_nodes in self.legacy_instances.items():
|
||||||
if family in self.family_to_id:
|
if product_type in self.product_type_to_id:
|
||||||
for instance_node in instance_nodes:
|
for instance_node in instance_nodes:
|
||||||
creator_identifier = self.family_to_id[family]
|
creator_identifier = self.product_type_to_id[product_type]
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Converting {} to {}".format(instance_node.name,
|
"Converting {} to {}".format(instance_node.name,
|
||||||
creator_identifier)
|
creator_identifier)
|
||||||
|
|
|
||||||
|
|
@ -10,19 +10,21 @@ class CreateAction(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.action"
|
identifier = "io.openpype.creators.blender.action"
|
||||||
label = "Action"
|
label = "Action"
|
||||||
family = "action"
|
product_type = "action"
|
||||||
icon = "male"
|
icon = "male"
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
# Run parent create method
|
# Run parent create method
|
||||||
collection = super().create(
|
collection = super().create(
|
||||||
subset_name, instance_data, pre_create_data
|
product_name, instance_data, pre_create_data
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get instance name
|
# Get instance name
|
||||||
name = plugin.prepare_scene_name(instance_data["asset"], subset_name)
|
name = plugin.prepare_scene_name(
|
||||||
|
instance_data["folderPath"], product_name
|
||||||
|
)
|
||||||
|
|
||||||
if pre_create_data.get("use_selection"):
|
if pre_create_data.get("use_selection"):
|
||||||
for obj in lib.get_selection():
|
for obj in lib.get_selection():
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ class CreateAnimation(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.animation"
|
identifier = "io.openpype.creators.blender.animation"
|
||||||
label = "Animation"
|
label = "Animation"
|
||||||
family = "animation"
|
product_type = "animation"
|
||||||
icon = "male"
|
icon = "male"
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
# Run parent create method
|
# Run parent create method
|
||||||
collection = super().create(
|
collection = super().create(
|
||||||
subset_name, instance_data, pre_create_data
|
product_name, instance_data, pre_create_data
|
||||||
)
|
)
|
||||||
|
|
||||||
if pre_create_data.get("use_selection"):
|
if pre_create_data.get("use_selection"):
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,16 @@ class CreateBlendScene(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.blendscene"
|
identifier = "io.openpype.creators.blender.blendscene"
|
||||||
label = "Blender Scene"
|
label = "Blender Scene"
|
||||||
family = "blendScene"
|
product_type = "blendScene"
|
||||||
icon = "cubes"
|
icon = "cubes"
|
||||||
|
|
||||||
maintain_selection = False
|
maintain_selection = False
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
|
|
||||||
instance_node = super().create(subset_name,
|
instance_node = super().create(product_name,
|
||||||
instance_data,
|
instance_data,
|
||||||
pre_create_data)
|
pre_create_data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,16 +11,16 @@ class CreateCamera(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.camera"
|
identifier = "io.openpype.creators.blender.camera"
|
||||||
label = "Camera"
|
label = "Camera"
|
||||||
family = "camera"
|
product_type = "camera"
|
||||||
icon = "video-camera"
|
icon = "video-camera"
|
||||||
|
|
||||||
create_as_asset_group = True
|
create_as_asset_group = True
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
|
|
||||||
asset_group = super().create(subset_name,
|
asset_group = super().create(product_name,
|
||||||
instance_data,
|
instance_data,
|
||||||
pre_create_data)
|
pre_create_data)
|
||||||
|
|
||||||
|
|
@ -30,8 +30,8 @@ class CreateCamera(plugin.BaseCreator):
|
||||||
obj.parent = asset_group
|
obj.parent = asset_group
|
||||||
else:
|
else:
|
||||||
plugin.deselect_all()
|
plugin.deselect_all()
|
||||||
camera = bpy.data.cameras.new(subset_name)
|
camera = bpy.data.cameras.new(product_name)
|
||||||
camera_obj = bpy.data.objects.new(subset_name, camera)
|
camera_obj = bpy.data.objects.new(product_name, camera)
|
||||||
|
|
||||||
instances = bpy.data.collections.get(AVALON_INSTANCES)
|
instances = bpy.data.collections.get(AVALON_INSTANCES)
|
||||||
instances.objects.link(camera_obj)
|
instances.objects.link(camera_obj)
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,16 @@ class CreateLayout(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.layout"
|
identifier = "io.openpype.creators.blender.layout"
|
||||||
label = "Layout"
|
label = "Layout"
|
||||||
family = "layout"
|
product_type = "layout"
|
||||||
icon = "cubes"
|
icon = "cubes"
|
||||||
|
|
||||||
create_as_asset_group = True
|
create_as_asset_group = True
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
|
|
||||||
asset_group = super().create(subset_name,
|
asset_group = super().create(product_name,
|
||||||
instance_data,
|
instance_data,
|
||||||
pre_create_data)
|
pre_create_data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,15 @@ class CreateModel(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.model"
|
identifier = "io.openpype.creators.blender.model"
|
||||||
label = "Model"
|
label = "Model"
|
||||||
family = "model"
|
product_type = "model"
|
||||||
icon = "cube"
|
icon = "cube"
|
||||||
|
|
||||||
create_as_asset_group = True
|
create_as_asset_group = True
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
asset_group = super().create(subset_name,
|
asset_group = super().create(product_name,
|
||||||
instance_data,
|
instance_data,
|
||||||
pre_create_data)
|
pre_create_data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ class CreatePointcache(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.pointcache"
|
identifier = "io.openpype.creators.blender.pointcache"
|
||||||
label = "Point Cache"
|
label = "Point Cache"
|
||||||
family = "pointcache"
|
product_type = "pointcache"
|
||||||
icon = "gears"
|
icon = "gears"
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
# Run parent create method
|
# Run parent create method
|
||||||
collection = super().create(
|
collection = super().create(
|
||||||
subset_name, instance_data, pre_create_data
|
product_name, instance_data, pre_create_data
|
||||||
)
|
)
|
||||||
|
|
||||||
if pre_create_data.get("use_selection"):
|
if pre_create_data.get("use_selection"):
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
"""Create render."""
|
"""Create render."""
|
||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
|
from ayon_core.lib import version_up
|
||||||
from ayon_core.hosts.blender.api import plugin
|
from ayon_core.hosts.blender.api import plugin
|
||||||
from ayon_core.hosts.blender.api.render_lib import prepare_rendering
|
from ayon_core.hosts.blender.api.render_lib import prepare_rendering
|
||||||
|
from ayon_core.hosts.blender.api.workio import save_file
|
||||||
|
|
||||||
|
|
||||||
class CreateRenderlayer(plugin.BaseCreator):
|
class CreateRenderlayer(plugin.BaseCreator):
|
||||||
|
|
@ -10,16 +12,16 @@ class CreateRenderlayer(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.render"
|
identifier = "io.openpype.creators.blender.render"
|
||||||
label = "Render"
|
label = "Render"
|
||||||
family = "render"
|
product_type = "render"
|
||||||
icon = "eye"
|
icon = "eye"
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
# Run parent create method
|
# Run parent create method
|
||||||
collection = super().create(
|
collection = super().create(
|
||||||
subset_name, instance_data, pre_create_data
|
product_name, instance_data, pre_create_data
|
||||||
)
|
)
|
||||||
|
|
||||||
prepare_rendering(collection)
|
prepare_rendering(collection)
|
||||||
|
|
@ -37,6 +39,7 @@ class CreateRenderlayer(plugin.BaseCreator):
|
||||||
# settings. Even the validator to check that the file is saved will
|
# settings. Even the validator to check that the file is saved will
|
||||||
# detect the file as saved, even if it isn't. The only solution for
|
# detect the file as saved, even if it isn't. The only solution for
|
||||||
# now it is to force the file to be saved.
|
# now it is to force the file to be saved.
|
||||||
bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath)
|
filepath = version_up(bpy.data.filepath)
|
||||||
|
save_file(filepath, copy=False)
|
||||||
|
|
||||||
return collection
|
return collection
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ class CreateReview(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.review"
|
identifier = "io.openpype.creators.blender.review"
|
||||||
label = "Review"
|
label = "Review"
|
||||||
family = "review"
|
product_type = "review"
|
||||||
icon = "video-camera"
|
icon = "video-camera"
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
# Run parent create method
|
# Run parent create method
|
||||||
collection = super().create(
|
collection = super().create(
|
||||||
subset_name, instance_data, pre_create_data
|
product_name, instance_data, pre_create_data
|
||||||
)
|
)
|
||||||
|
|
||||||
if pre_create_data.get("use_selection"):
|
if pre_create_data.get("use_selection"):
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,15 @@ class CreateRig(plugin.BaseCreator):
|
||||||
|
|
||||||
identifier = "io.openpype.creators.blender.rig"
|
identifier = "io.openpype.creators.blender.rig"
|
||||||
label = "Rig"
|
label = "Rig"
|
||||||
family = "rig"
|
product_type = "rig"
|
||||||
icon = "wheelchair"
|
icon = "wheelchair"
|
||||||
|
|
||||||
create_as_asset_group = True
|
create_as_asset_group = True
|
||||||
|
|
||||||
def create(
|
def create(
|
||||||
self, subset_name: str, instance_data: dict, pre_create_data: dict
|
self, product_name: str, instance_data: dict, pre_create_data: dict
|
||||||
):
|
):
|
||||||
asset_group = super().create(subset_name,
|
asset_group = super().create(product_name,
|
||||||
instance_data,
|
instance_data,
|
||||||
pre_create_data)
|
pre_create_data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
||||||
"""
|
"""
|
||||||
identifier = "io.openpype.creators.blender.workfile"
|
identifier = "io.openpype.creators.blender.workfile"
|
||||||
label = "Workfile"
|
label = "Workfile"
|
||||||
family = "workfile"
|
product_type = "workfile"
|
||||||
icon = "fa5.file"
|
icon = "fa5.file"
|
||||||
|
|
||||||
def create(self):
|
def create(self):
|
||||||
|
|
@ -43,8 +43,12 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
||||||
|
|
||||||
if not workfile_instance:
|
if not workfile_instance:
|
||||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||||
subset_name = self.get_subset_name(
|
product_name = self.get_product_name(
|
||||||
task_name, task_name, asset_doc, project_name, host_name
|
project_name,
|
||||||
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
task_name,
|
||||||
|
host_name,
|
||||||
)
|
)
|
||||||
data = {
|
data = {
|
||||||
"folderPath": asset_name,
|
"folderPath": asset_name,
|
||||||
|
|
@ -53,17 +57,17 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
||||||
}
|
}
|
||||||
data.update(
|
data.update(
|
||||||
self.get_dynamic_data(
|
self.get_dynamic_data(
|
||||||
task_name,
|
|
||||||
task_name,
|
|
||||||
asset_doc,
|
|
||||||
project_name,
|
project_name,
|
||||||
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
task_name,
|
||||||
host_name,
|
host_name,
|
||||||
workfile_instance,
|
workfile_instance,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.log.info("Auto-creating workfile instance...")
|
self.log.info("Auto-creating workfile instance...")
|
||||||
workfile_instance = CreatedInstance(
|
workfile_instance = CreatedInstance(
|
||||||
self.family, subset_name, data, self
|
self.product_type, product_name, data, self
|
||||||
)
|
)
|
||||||
self._add_instance_to_context(workfile_instance)
|
self._add_instance_to_context(workfile_instance)
|
||||||
|
|
||||||
|
|
@ -73,13 +77,17 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
||||||
):
|
):
|
||||||
# Update instance context if it's different
|
# Update instance context if it's different
|
||||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||||
subset_name = self.get_subset_name(
|
product_name = self.get_product_name(
|
||||||
task_name, task_name, asset_doc, project_name, host_name
|
project_name,
|
||||||
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
self.default_variant,
|
||||||
|
host_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
workfile_instance["folderPath"] = asset_name
|
workfile_instance["folderPath"] = asset_name
|
||||||
workfile_instance["task"] = task_name
|
workfile_instance["task"] = task_name
|
||||||
workfile_instance["subset"] = subset_name
|
workfile_instance["productName"] = product_name
|
||||||
|
|
||||||
instance_node = bpy.data.collections.get(AVALON_CONTAINERS)
|
instance_node = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not instance_node:
|
if not instance_node:
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ from ayon_core.hosts.blender.api import plugin
|
||||||
|
|
||||||
|
|
||||||
def append_workfile(context, fname, do_import):
|
def append_workfile(context, fname, do_import):
|
||||||
asset = context['asset']['name']
|
folder_name = context['asset']['name']
|
||||||
subset = context['subset']['name']
|
product_name = context['subset']['name']
|
||||||
|
|
||||||
group_name = plugin.prepare_scene_name(asset, subset)
|
group_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
|
|
||||||
# We need to preserve the original names of the scenes, otherwise,
|
# We need to preserve the original names of the scenes, otherwise,
|
||||||
# if there are duplicate names in the current workfile, the imported
|
# if there are duplicate names in the current workfile, the imported
|
||||||
|
|
|
||||||
|
|
@ -134,13 +134,15 @@ class CacheModelLoader(plugin.AssetLoader):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
containers = bpy.data.collections.get(AVALON_CONTAINERS)
|
containers = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not containers:
|
if not containers:
|
||||||
|
|
@ -159,6 +161,7 @@ class CacheModelLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
self._link_objects(objects, asset_group, containers, asset_group)
|
self._link_objects(objects, asset_group, containers, asset_group)
|
||||||
|
|
||||||
|
product_type = context["subset"]["data"]["family"]
|
||||||
asset_group[AVALON_PROPERTY] = {
|
asset_group[AVALON_PROPERTY] = {
|
||||||
"schema": "openpype:container-2.0",
|
"schema": "openpype:container-2.0",
|
||||||
"id": AVALON_CONTAINER_ID,
|
"id": AVALON_CONTAINER_ID,
|
||||||
|
|
@ -169,14 +172,14 @@ class CacheModelLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": product_type,
|
||||||
"objectName": group_name
|
"objectName": group_name
|
||||||
}
|
}
|
||||||
|
|
||||||
self[:] = objects
|
self[:] = objects
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""Update the loaded asset.
|
"""Update the loaded asset.
|
||||||
|
|
||||||
This will remove all objects of the current collection, load the new
|
This will remove all objects of the current collection, load the new
|
||||||
|
|
@ -188,15 +191,16 @@ class CacheModelLoader(plugin.AssetLoader):
|
||||||
Warning:
|
Warning:
|
||||||
No nested collections are supported at the moment!
|
No nested collections are supported at the moment!
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
object_name = container["objectName"]
|
object_name = container["objectName"]
|
||||||
asset_group = bpy.data.objects.get(object_name)
|
asset_group = bpy.data.objects.get(object_name)
|
||||||
libpath = Path(get_representation_path(representation))
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
extension = libpath.suffix.lower()
|
extension = libpath.suffix.lower()
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
|
|
@ -241,7 +245,7 @@ class CacheModelLoader(plugin.AssetLoader):
|
||||||
asset_group.matrix_basis = mat
|
asset_group.matrix_basis = mat
|
||||||
|
|
||||||
metadata["libpath"] = str(libpath)
|
metadata["libpath"] = str(libpath)
|
||||||
metadata["representation"] = str(representation["_id"])
|
metadata["representation"] = str(repre_doc["_id"])
|
||||||
|
|
||||||
def exec_remove(self, container: Dict) -> bool:
|
def exec_remove(self, container: Dict) -> bool:
|
||||||
"""Remove an existing container from a Blender scene.
|
"""Remove an existing container from a Blender scene.
|
||||||
|
|
|
||||||
|
|
@ -44,11 +44,11 @@ class BlendActionLoader(plugin.AssetLoader):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
lib_container = plugin.prepare_scene_name(asset, subset)
|
lib_container = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
container_name = plugin.prepare_scene_name(
|
container_name = plugin.prepare_scene_name(
|
||||||
asset, subset, namespace
|
folder_name, product_name, namespace
|
||||||
)
|
)
|
||||||
|
|
||||||
container = bpy.data.collections.new(lib_container)
|
container = bpy.data.collections.new(lib_container)
|
||||||
|
|
@ -114,7 +114,7 @@ class BlendActionLoader(plugin.AssetLoader):
|
||||||
self[:] = nodes
|
self[:] = nodes
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def update(self, container: Dict, representation: Dict):
|
def update(self, container: Dict, context: Dict):
|
||||||
"""Update the loaded asset.
|
"""Update the loaded asset.
|
||||||
|
|
||||||
This will remove all objects of the current collection, load the new
|
This will remove all objects of the current collection, load the new
|
||||||
|
|
@ -126,18 +126,18 @@ class BlendActionLoader(plugin.AssetLoader):
|
||||||
Warning:
|
Warning:
|
||||||
No nested collections are supported at the moment!
|
No nested collections are supported at the moment!
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
collection = bpy.data.collections.get(
|
collection = bpy.data.collections.get(
|
||||||
container["objectName"]
|
container["objectName"]
|
||||||
)
|
)
|
||||||
|
|
||||||
libpath = Path(get_representation_path(representation))
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
extension = libpath.suffix.lower()
|
extension = libpath.suffix.lower()
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert collection, (
|
assert collection, (
|
||||||
|
|
@ -241,7 +241,7 @@ class BlendActionLoader(plugin.AssetLoader):
|
||||||
# Save the list of objects in the metadata container
|
# Save the list of objects in the metadata container
|
||||||
collection_metadata["objects"] = objects_list
|
collection_metadata["objects"] = objects_list
|
||||||
collection_metadata["libpath"] = str(libpath)
|
collection_metadata["libpath"] = str(libpath)
|
||||||
collection_metadata["representation"] = str(representation["_id"])
|
collection_metadata["representation"] = str(repre_doc["_id"])
|
||||||
|
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
bpy.ops.object.select_all(action='DESELECT')
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,13 +39,15 @@ class AudioLoader(plugin.AssetLoader):
|
||||||
options: Additional settings dictionary
|
options: Additional settings dictionary
|
||||||
"""
|
"""
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not avalon_container:
|
if not avalon_container:
|
||||||
|
|
@ -85,7 +87,7 @@ class AudioLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": context["subset"]["data"]["family"],
|
||||||
"objectName": group_name,
|
"objectName": group_name,
|
||||||
"audio": audio
|
"audio": audio
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +96,7 @@ class AudioLoader(plugin.AssetLoader):
|
||||||
self[:] = objects
|
self[:] = objects
|
||||||
return [objects]
|
return [objects]
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""Update an audio strip in the sequence editor.
|
"""Update an audio strip in the sequence editor.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
|
|
@ -103,14 +105,15 @@ class AudioLoader(plugin.AssetLoader):
|
||||||
representation (openpype:representation-1.0): Representation to
|
representation (openpype:representation-1.0): Representation to
|
||||||
update, from `host.ls()`.
|
update, from `host.ls()`.
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
object_name = container["objectName"]
|
object_name = container["objectName"]
|
||||||
asset_group = bpy.data.objects.get(object_name)
|
asset_group = bpy.data.objects.get(object_name)
|
||||||
libpath = Path(get_representation_path(representation))
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
|
|
@ -173,8 +176,8 @@ class AudioLoader(plugin.AssetLoader):
|
||||||
window_manager.windows[-1].screen.areas[0].type = old_type
|
window_manager.windows[-1].screen.areas[0].type = old_type
|
||||||
|
|
||||||
metadata["libpath"] = str(libpath)
|
metadata["libpath"] = str(libpath)
|
||||||
metadata["representation"] = str(representation["_id"])
|
metadata["representation"] = str(repre_doc["_id"])
|
||||||
metadata["parent"] = str(representation["parent"])
|
metadata["parent"] = str(repre_doc["parent"])
|
||||||
metadata["audio"] = new_audio
|
metadata["audio"] = new_audio
|
||||||
|
|
||||||
def exec_remove(self, container: Dict) -> bool:
|
def exec_remove(self, container: Dict) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -127,20 +127,22 @@ class BlendLoader(plugin.AssetLoader):
|
||||||
options: Additional settings dictionary
|
options: Additional settings dictionary
|
||||||
"""
|
"""
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
family = context["representation"]["context"]["family"]
|
product_type = context["subset"]["data"]["family"]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
family = "model"
|
product_type = "model"
|
||||||
|
|
||||||
representation = str(context["representation"]["_id"])
|
representation = str(context["representation"]["_id"])
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not avalon_container:
|
if not avalon_container:
|
||||||
|
|
@ -149,8 +151,8 @@ class BlendLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
container, members = self._process_data(libpath, group_name)
|
container, members = self._process_data(libpath, group_name)
|
||||||
|
|
||||||
if family == "layout":
|
if product_type == "layout":
|
||||||
self._post_process_layout(container, asset, representation)
|
self._post_process_layout(container, folder_name, representation)
|
||||||
|
|
||||||
avalon_container.objects.link(container)
|
avalon_container.objects.link(container)
|
||||||
|
|
||||||
|
|
@ -164,7 +166,7 @@ class BlendLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": context["subset"]["data"]["family"],
|
||||||
"objectName": group_name,
|
"objectName": group_name,
|
||||||
"members": members,
|
"members": members,
|
||||||
}
|
}
|
||||||
|
|
@ -179,13 +181,14 @@ class BlendLoader(plugin.AssetLoader):
|
||||||
self[:] = objects
|
self[:] = objects
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""
|
"""
|
||||||
Update the loaded asset.
|
Update the loaded asset.
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
group_name = container["objectName"]
|
group_name = container["objectName"]
|
||||||
asset_group = bpy.data.objects.get(group_name)
|
asset_group = bpy.data.objects.get(group_name)
|
||||||
libpath = Path(get_representation_path(representation)).as_posix()
|
libpath = Path(get_representation_path(repre_doc)).as_posix()
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
f"The asset is not loaded: {container['objectName']}"
|
f"The asset is not loaded: {container['objectName']}"
|
||||||
|
|
@ -232,8 +235,8 @@ class BlendLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
new_data = {
|
new_data = {
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"representation": str(representation["_id"]),
|
"representation": str(repre_doc["_id"]),
|
||||||
"parent": str(representation["parent"]),
|
"parent": str(repre_doc["parent"]),
|
||||||
"members": members,
|
"members": members,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ class BlendSceneLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _process_data(self, libpath, group_name, family):
|
def _process_data(self, libpath, group_name, product_type):
|
||||||
# Append all the data from the .blend file
|
# Append all the data from the .blend file
|
||||||
with bpy.data.libraries.load(
|
with bpy.data.libraries.load(
|
||||||
libpath, link=False, relative=False
|
libpath, link=False, relative=False
|
||||||
|
|
@ -82,25 +82,29 @@ class BlendSceneLoader(plugin.AssetLoader):
|
||||||
options: Additional settings dictionary
|
options: Additional settings dictionary
|
||||||
"""
|
"""
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
family = context["representation"]["context"]["family"]
|
product_type = context["subset"]["data"]["family"]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
family = "model"
|
product_type = "model"
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not avalon_container:
|
if not avalon_container:
|
||||||
avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS)
|
||||||
bpy.context.scene.collection.children.link(avalon_container)
|
bpy.context.scene.collection.children.link(avalon_container)
|
||||||
|
|
||||||
container, members = self._process_data(libpath, group_name, family)
|
container, members = self._process_data(
|
||||||
|
libpath, group_name, product_type
|
||||||
|
)
|
||||||
|
|
||||||
avalon_container.children.link(container)
|
avalon_container.children.link(container)
|
||||||
|
|
||||||
|
|
@ -114,7 +118,7 @@ class BlendSceneLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": context["subset"]["data"]["family"],
|
||||||
"objectName": group_name,
|
"objectName": group_name,
|
||||||
"members": members,
|
"members": members,
|
||||||
}
|
}
|
||||||
|
|
@ -129,13 +133,14 @@ class BlendSceneLoader(plugin.AssetLoader):
|
||||||
self[:] = objects
|
self[:] = objects
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""
|
"""
|
||||||
Update the loaded asset.
|
Update the loaded asset.
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
group_name = container["objectName"]
|
group_name = container["objectName"]
|
||||||
asset_group = bpy.data.collections.get(group_name)
|
asset_group = bpy.data.collections.get(group_name)
|
||||||
libpath = Path(get_representation_path(representation)).as_posix()
|
libpath = Path(get_representation_path(repre_doc)).as_posix()
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
f"The asset is not loaded: {container['objectName']}"
|
f"The asset is not loaded: {container['objectName']}"
|
||||||
|
|
@ -167,8 +172,12 @@ class BlendSceneLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
self.exec_remove(container)
|
self.exec_remove(container)
|
||||||
|
|
||||||
family = container["family"]
|
product_type = container.get("productType")
|
||||||
asset_group, members = self._process_data(libpath, group_name, family)
|
if product_type is None:
|
||||||
|
product_type = container["family"]
|
||||||
|
asset_group, members = self._process_data(
|
||||||
|
libpath, group_name, product_type
|
||||||
|
)
|
||||||
|
|
||||||
for member in members:
|
for member in members:
|
||||||
if member.name in collection_parents:
|
if member.name in collection_parents:
|
||||||
|
|
@ -193,8 +202,8 @@ class BlendSceneLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
new_data = {
|
new_data = {
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"representation": str(representation["_id"]),
|
"representation": str(repre_doc["_id"]),
|
||||||
"parent": str(representation["parent"]),
|
"parent": str(repre_doc["parent"]),
|
||||||
"members": members,
|
"members": members,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,13 +84,15 @@ class AbcCameraLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
|
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not avalon_container:
|
if not avalon_container:
|
||||||
|
|
@ -121,14 +123,14 @@ class AbcCameraLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": context["subset"]["data"]["family"],
|
||||||
"objectName": group_name,
|
"objectName": group_name,
|
||||||
}
|
}
|
||||||
|
|
||||||
self[:] = objects
|
self[:] = objects
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""Update the loaded asset.
|
"""Update the loaded asset.
|
||||||
|
|
||||||
This will remove all objects of the current collection, load the new
|
This will remove all objects of the current collection, load the new
|
||||||
|
|
@ -140,15 +142,16 @@ class AbcCameraLoader(plugin.AssetLoader):
|
||||||
Warning:
|
Warning:
|
||||||
No nested collections are supported at the moment!
|
No nested collections are supported at the moment!
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
object_name = container["objectName"]
|
object_name = container["objectName"]
|
||||||
asset_group = bpy.data.objects.get(object_name)
|
asset_group = bpy.data.objects.get(object_name)
|
||||||
libpath = Path(get_representation_path(representation))
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
extension = libpath.suffix.lower()
|
extension = libpath.suffix.lower()
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
|
|
@ -183,7 +186,7 @@ class AbcCameraLoader(plugin.AssetLoader):
|
||||||
asset_group.matrix_basis = mat
|
asset_group.matrix_basis = mat
|
||||||
|
|
||||||
metadata["libpath"] = str(libpath)
|
metadata["libpath"] = str(libpath)
|
||||||
metadata["representation"] = str(representation["_id"])
|
metadata["representation"] = str(repre_doc["_id"])
|
||||||
|
|
||||||
def exec_remove(self, container: Dict) -> bool:
|
def exec_remove(self, container: Dict) -> bool:
|
||||||
"""Remove an existing container from a Blender scene.
|
"""Remove an existing container from a Blender scene.
|
||||||
|
|
|
||||||
|
|
@ -87,13 +87,15 @@ class FbxCameraLoader(plugin.AssetLoader):
|
||||||
options: Additional settings dictionary
|
options: Additional settings dictionary
|
||||||
"""
|
"""
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not avalon_container:
|
if not avalon_container:
|
||||||
|
|
@ -124,14 +126,14 @@ class FbxCameraLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": context["subset"]["data"]["family"],
|
||||||
"objectName": group_name
|
"objectName": group_name
|
||||||
}
|
}
|
||||||
|
|
||||||
self[:] = objects
|
self[:] = objects
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""Update the loaded asset.
|
"""Update the loaded asset.
|
||||||
|
|
||||||
This will remove all objects of the current collection, load the new
|
This will remove all objects of the current collection, load the new
|
||||||
|
|
@ -143,15 +145,16 @@ class FbxCameraLoader(plugin.AssetLoader):
|
||||||
Warning:
|
Warning:
|
||||||
No nested collections are supported at the moment!
|
No nested collections are supported at the moment!
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
object_name = container["objectName"]
|
object_name = container["objectName"]
|
||||||
asset_group = bpy.data.objects.get(object_name)
|
asset_group = bpy.data.objects.get(object_name)
|
||||||
libpath = Path(get_representation_path(representation))
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
extension = libpath.suffix.lower()
|
extension = libpath.suffix.lower()
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
|
|
@ -193,7 +196,7 @@ class FbxCameraLoader(plugin.AssetLoader):
|
||||||
asset_group.matrix_basis = mat
|
asset_group.matrix_basis = mat
|
||||||
|
|
||||||
metadata["libpath"] = str(libpath)
|
metadata["libpath"] = str(libpath)
|
||||||
metadata["representation"] = str(representation["_id"])
|
metadata["representation"] = str(repre_doc["_id"])
|
||||||
|
|
||||||
def exec_remove(self, container: Dict) -> bool:
|
def exec_remove(self, container: Dict) -> bool:
|
||||||
"""Remove an existing container from a Blender scene.
|
"""Remove an existing container from a Blender scene.
|
||||||
|
|
|
||||||
|
|
@ -131,13 +131,15 @@ class FbxModelLoader(plugin.AssetLoader):
|
||||||
options: Additional settings dictionary
|
options: Additional settings dictionary
|
||||||
"""
|
"""
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not avalon_container:
|
if not avalon_container:
|
||||||
|
|
@ -168,14 +170,14 @@ class FbxModelLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": context["subset"]["data"]["family"],
|
||||||
"objectName": group_name
|
"objectName": group_name
|
||||||
}
|
}
|
||||||
|
|
||||||
self[:] = objects
|
self[:] = objects
|
||||||
return objects
|
return objects
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""Update the loaded asset.
|
"""Update the loaded asset.
|
||||||
|
|
||||||
This will remove all objects of the current collection, load the new
|
This will remove all objects of the current collection, load the new
|
||||||
|
|
@ -187,15 +189,16 @@ class FbxModelLoader(plugin.AssetLoader):
|
||||||
Warning:
|
Warning:
|
||||||
No nested collections are supported at the moment!
|
No nested collections are supported at the moment!
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
object_name = container["objectName"]
|
object_name = container["objectName"]
|
||||||
asset_group = bpy.data.objects.get(object_name)
|
asset_group = bpy.data.objects.get(object_name)
|
||||||
libpath = Path(get_representation_path(representation))
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
extension = libpath.suffix.lower()
|
extension = libpath.suffix.lower()
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
|
|
@ -248,7 +251,7 @@ class FbxModelLoader(plugin.AssetLoader):
|
||||||
asset_group.matrix_basis = mat
|
asset_group.matrix_basis = mat
|
||||||
|
|
||||||
metadata["libpath"] = str(libpath)
|
metadata["libpath"] = str(libpath)
|
||||||
metadata["representation"] = str(representation["_id"])
|
metadata["representation"] = str(repre_doc["_id"])
|
||||||
|
|
||||||
def exec_remove(self, container: Dict) -> bool:
|
def exec_remove(self, container: Dict) -> bool:
|
||||||
"""Remove an existing container from a Blender scene.
|
"""Remove an existing container from a Blender scene.
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,11 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
if anim_collection:
|
if anim_collection:
|
||||||
bpy.data.collections.remove(anim_collection)
|
bpy.data.collections.remove(anim_collection)
|
||||||
|
|
||||||
def _get_loader(self, loaders, family):
|
def _get_loader(self, loaders, product_type):
|
||||||
name = ""
|
name = ""
|
||||||
if family == 'rig':
|
if product_type == 'rig':
|
||||||
name = "BlendRigLoader"
|
name = "BlendRigLoader"
|
||||||
elif family == 'model':
|
elif product_type == 'model':
|
||||||
name = "BlendModelLoader"
|
name = "BlendModelLoader"
|
||||||
|
|
||||||
if name == "":
|
if name == "":
|
||||||
|
|
@ -76,10 +76,12 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
for element in data:
|
for element in data:
|
||||||
reference = element.get('reference')
|
reference = element.get('reference')
|
||||||
family = element.get('family')
|
product_type = element.get("product_type")
|
||||||
|
if product_type is None:
|
||||||
|
product_type = element.get("family")
|
||||||
|
|
||||||
loaders = loaders_from_representation(all_loaders, reference)
|
loaders = loaders_from_representation(all_loaders, reference)
|
||||||
loader = self._get_loader(loaders, family)
|
loader = self._get_loader(loaders, product_type)
|
||||||
|
|
||||||
if not loader:
|
if not loader:
|
||||||
continue
|
continue
|
||||||
|
|
@ -95,7 +97,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
'parent': asset_group,
|
'parent': asset_group,
|
||||||
'transform': element.get('transform'),
|
'transform': element.get('transform'),
|
||||||
'action': action,
|
'action': action,
|
||||||
'create_animation': True if family == 'rig' else False,
|
'create_animation': True if product_type == 'rig' else False,
|
||||||
'animation_asset': asset
|
'animation_asset': asset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,7 +129,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
# legacy_create(
|
# legacy_create(
|
||||||
# creator_plugin,
|
# creator_plugin,
|
||||||
# name="camera",
|
# name="camera",
|
||||||
# # name=f"{unique_number}_{subset}_animation",
|
# # name=f"{unique_number}_{product[name]}_animation",
|
||||||
# asset=asset,
|
# asset=asset,
|
||||||
# options={"useSelection": False}
|
# options={"useSelection": False}
|
||||||
# # data={"dependencies": str(context["representation"]["_id"])}
|
# # data={"dependencies": str(context["representation"]["_id"])}
|
||||||
|
|
@ -146,13 +148,15 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
options: Additional settings dictionary
|
options: Additional settings dictionary
|
||||||
"""
|
"""
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||||
unique_number = plugin.get_unique_number(asset, subset)
|
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
group_name = plugin.prepare_scene_name(
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
folder_name, product_name, unique_number
|
||||||
|
)
|
||||||
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
|
|
||||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||||
if not avalon_container:
|
if not avalon_container:
|
||||||
|
|
@ -177,14 +181,14 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
"libpath": libpath,
|
"libpath": libpath,
|
||||||
"asset_name": asset_name,
|
"asset_name": asset_name,
|
||||||
"parent": str(context["representation"]["parent"]),
|
"parent": str(context["representation"]["parent"]),
|
||||||
"family": context["representation"]["context"]["family"],
|
"productType": context["subset"]["data"]["family"],
|
||||||
"objectName": group_name
|
"objectName": group_name
|
||||||
}
|
}
|
||||||
|
|
||||||
self[:] = asset_group.children
|
self[:] = asset_group.children
|
||||||
return asset_group.children
|
return asset_group.children
|
||||||
|
|
||||||
def exec_update(self, container: Dict, representation: Dict):
|
def exec_update(self, container: Dict, context: Dict):
|
||||||
"""Update the loaded asset.
|
"""Update the loaded asset.
|
||||||
|
|
||||||
This will remove all objects of the current collection, load the new
|
This will remove all objects of the current collection, load the new
|
||||||
|
|
@ -193,15 +197,16 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
will not be removed, only unlinked. Normally this should not be the
|
will not be removed, only unlinked. Normally this should not be the
|
||||||
case though.
|
case though.
|
||||||
"""
|
"""
|
||||||
|
repre_doc = context["representation"]
|
||||||
object_name = container["objectName"]
|
object_name = container["objectName"]
|
||||||
asset_group = bpy.data.objects.get(object_name)
|
asset_group = bpy.data.objects.get(object_name)
|
||||||
libpath = Path(get_representation_path(representation))
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
extension = libpath.suffix.lower()
|
extension = libpath.suffix.lower()
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert asset_group, (
|
assert asset_group, (
|
||||||
|
|
@ -239,7 +244,10 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
|
|
||||||
for obj in asset_group.children:
|
for obj in asset_group.children:
|
||||||
obj_meta = obj.get(AVALON_PROPERTY)
|
obj_meta = obj.get(AVALON_PROPERTY)
|
||||||
if obj_meta.get('family') == 'rig':
|
product_type = obj_meta.get("productType")
|
||||||
|
if product_type is None:
|
||||||
|
product_type = obj_meta.get("family")
|
||||||
|
if product_type == "rig":
|
||||||
rig = None
|
rig = None
|
||||||
for child in obj.children:
|
for child in obj.children:
|
||||||
if child.type == 'ARMATURE':
|
if child.type == 'ARMATURE':
|
||||||
|
|
@ -262,7 +270,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
||||||
asset_group.matrix_basis = mat
|
asset_group.matrix_basis = mat
|
||||||
|
|
||||||
metadata["libpath"] = str(libpath)
|
metadata["libpath"] = str(libpath)
|
||||||
metadata["representation"] = str(representation["_id"])
|
metadata["representation"] = str(repre_doc["_id"])
|
||||||
|
|
||||||
def exec_remove(self, container: Dict) -> bool:
|
def exec_remove(self, container: Dict) -> bool:
|
||||||
"""Remove an existing container from a Blender scene.
|
"""Remove an existing container from a Blender scene.
|
||||||
|
|
|
||||||
|
|
@ -93,18 +93,18 @@ class BlendLookLoader(plugin.AssetLoader):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
libpath = self.filepath_from_context(context)
|
libpath = self.filepath_from_context(context)
|
||||||
asset = context["asset"]["name"]
|
folder_name = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
lib_container = plugin.prepare_scene_name(
|
lib_container = plugin.prepare_scene_name(
|
||||||
asset, subset
|
folder_name, product_name
|
||||||
)
|
)
|
||||||
unique_number = plugin.get_unique_number(
|
unique_number = plugin.get_unique_number(
|
||||||
asset, subset
|
folder_name, product_name
|
||||||
)
|
)
|
||||||
namespace = namespace or f"{asset}_{unique_number}"
|
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||||
container_name = plugin.prepare_scene_name(
|
container_name = plugin.prepare_scene_name(
|
||||||
asset, subset, unique_number
|
folder_name, product_name, unique_number
|
||||||
)
|
)
|
||||||
|
|
||||||
container = bpy.data.collections.new(lib_container)
|
container = bpy.data.collections.new(lib_container)
|
||||||
|
|
@ -131,22 +131,23 @@ class BlendLookLoader(plugin.AssetLoader):
|
||||||
metadata["materials"] = materials
|
metadata["materials"] = materials
|
||||||
|
|
||||||
metadata["parent"] = str(context["representation"]["parent"])
|
metadata["parent"] = str(context["representation"]["parent"])
|
||||||
metadata["family"] = context["representation"]["context"]["family"]
|
metadata["product_type"] = context["subset"]["data"]["family"]
|
||||||
|
|
||||||
nodes = list(container.objects)
|
nodes = list(container.objects)
|
||||||
nodes.append(container)
|
nodes.append(container)
|
||||||
self[:] = nodes
|
self[:] = nodes
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
def update(self, container: Dict, representation: Dict):
|
def update(self, container: Dict, context: Dict):
|
||||||
collection = bpy.data.collections.get(container["objectName"])
|
collection = bpy.data.collections.get(container["objectName"])
|
||||||
libpath = Path(get_representation_path(representation))
|
repre_doc = context["representation"]
|
||||||
|
libpath = Path(get_representation_path(repre_doc))
|
||||||
extension = libpath.suffix.lower()
|
extension = libpath.suffix.lower()
|
||||||
|
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Container: %s\nRepresentation: %s",
|
"Container: %s\nRepresentation: %s",
|
||||||
pformat(container, indent=2),
|
pformat(container, indent=2),
|
||||||
pformat(representation, indent=2),
|
pformat(repre_doc, indent=2),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert collection, (
|
assert collection, (
|
||||||
|
|
@ -201,7 +202,7 @@ class BlendLookLoader(plugin.AssetLoader):
|
||||||
collection_metadata["objects"] = objects
|
collection_metadata["objects"] = objects
|
||||||
collection_metadata["materials"] = materials
|
collection_metadata["materials"] = materials
|
||||||
collection_metadata["libpath"] = str(libpath)
|
collection_metadata["libpath"] = str(libpath)
|
||||||
collection_metadata["representation"] = str(representation["_id"])
|
collection_metadata["representation"] = str(repre_doc["_id"])
|
||||||
|
|
||||||
def remove(self, container: Dict) -> bool:
|
def remove(self, container: Dict) -> bool:
|
||||||
collection = bpy.data.collections.get(container["objectName"])
|
collection = bpy.data.collections.get(container["objectName"])
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class CollectBlenderInstanceData(pyblish.api.InstancePlugin):
|
||||||
members.extend(instance_node.children)
|
members.extend(instance_node.children)
|
||||||
|
|
||||||
# Special case for animation instances, include armatures
|
# Special case for animation instances, include armatures
|
||||||
if instance.data["family"] == "animation":
|
if instance.data["productType"] == "animation":
|
||||||
for obj in instance_node.objects:
|
for obj in instance_node.objects:
|
||||||
if obj.type == 'EMPTY' and obj.get(AVALON_PROPERTY):
|
if obj.type == 'EMPTY' and obj.get(AVALON_PROPERTY):
|
||||||
members.extend(
|
members.extend(
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
|
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
filename = f"{instance_name}.abc"
|
filename = f"{instance_name}.abc"
|
||||||
filepath = os.path.join(stagingdir, filename)
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ class ExtractAnimationABC(
|
||||||
|
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
filename = f"{instance_name}.abc"
|
filename = f"{instance_name}.abc"
|
||||||
|
|
||||||
filepath = os.path.join(stagingdir, filename)
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
|
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
filename = f"{instance_name}.blend"
|
filename = f"{instance_name}.blend"
|
||||||
filepath = os.path.join(stagingdir, filename)
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ class ExtractBlendAnimation(
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
|
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
filename = f"{instance_name}.blend"
|
filename = f"{instance_name}.blend"
|
||||||
filepath = os.path.join(stagingdir, filename)
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
|
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
filename = f"{instance_name}.abc"
|
filename = f"{instance_name}.abc"
|
||||||
filepath = os.path.join(stagingdir, filename)
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
|
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
filename = f"{instance_name}.fbx"
|
filename = f"{instance_name}.fbx"
|
||||||
filepath = os.path.join(stagingdir, filename)
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
|
|
||||||
# Define extract output file path
|
# Define extract output file path
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
filename = f"{instance_name}.fbx"
|
filename = f"{instance_name}.fbx"
|
||||||
filepath = os.path.join(stagingdir, filename)
|
filepath = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,9 +145,9 @@ class ExtractAnimationFBX(
|
||||||
|
|
||||||
root.select_set(True)
|
root.select_set(True)
|
||||||
armature.select_set(True)
|
armature.select_set(True)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
fbx_filename = f"{instance_name}_{armature.name}.fbx"
|
fbx_filename = f"{instance_name}_{armature.name}.fbx"
|
||||||
filepath = os.path.join(stagingdir, fbx_filename)
|
filepath = os.path.join(stagingdir, fbx_filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
|
|
||||||
fbx_count = 0
|
fbx_count = 0
|
||||||
|
|
||||||
project_name = instance.context.data["projectEntity"]["name"]
|
project_name = instance.context.data["projectName"]
|
||||||
for asset in asset_group.children:
|
for asset in asset_group.children:
|
||||||
metadata = asset.get(AVALON_PROPERTY)
|
metadata = asset.get(AVALON_PROPERTY)
|
||||||
if not metadata:
|
if not metadata:
|
||||||
|
|
@ -147,7 +147,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
version_id = metadata["parent"]
|
version_id = metadata["parent"]
|
||||||
family = metadata["family"]
|
product_type = metadata.get("product_type")
|
||||||
|
if product_type is None:
|
||||||
|
product_type = metadata["family"]
|
||||||
|
|
||||||
self.log.debug("Parent: {}".format(version_id))
|
self.log.debug("Parent: {}".format(version_id))
|
||||||
# Get blend reference
|
# Get blend reference
|
||||||
|
|
@ -179,7 +181,8 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
json_element["reference_fbx"] = str(fbx_id)
|
json_element["reference_fbx"] = str(fbx_id)
|
||||||
if abc_id:
|
if abc_id:
|
||||||
json_element["reference_abc"] = str(abc_id)
|
json_element["reference_abc"] = str(abc_id)
|
||||||
json_element["family"] = family
|
|
||||||
|
json_element["product_type"] = product_type
|
||||||
json_element["instance_name"] = asset.name
|
json_element["instance_name"] = asset.name
|
||||||
json_element["asset_name"] = metadata["asset_name"]
|
json_element["asset_name"] = metadata["asset_name"]
|
||||||
json_element["file_path"] = metadata["libpath"]
|
json_element["file_path"] = metadata["libpath"]
|
||||||
|
|
@ -215,7 +218,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
]
|
]
|
||||||
|
|
||||||
# Extract the animation as well
|
# Extract the animation as well
|
||||||
if family == "rig":
|
if product_type == "rig":
|
||||||
f, n = self._export_animation(
|
f, n = self._export_animation(
|
||||||
asset, instance, stagingdir, fbx_count)
|
asset, instance, stagingdir, fbx_count)
|
||||||
if f:
|
if f:
|
||||||
|
|
@ -225,9 +228,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
|
|
||||||
json_data.append(json_element)
|
json_data.append(json_element)
|
||||||
|
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
instance_name = f"{asset_name}_{subset}"
|
instance_name = f"{folder_name}_{product_name}"
|
||||||
json_filename = f"{instance_name}.json"
|
json_filename = f"{instance_name}.json"
|
||||||
|
|
||||||
json_path = os.path.join(stagingdir, json_filename)
|
json_path = os.path.join(stagingdir, json_filename)
|
||||||
|
|
|
||||||
|
|
@ -55,9 +55,9 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
||||||
|
|
||||||
# get output path
|
# get output path
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
filename = f"{asset_name}_{subset}"
|
filename = f"{folder_name}_{product_name}"
|
||||||
|
|
||||||
path = os.path.join(stagingdir, filename)
|
path = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,9 +32,9 @@ class ExtractThumbnail(publish.Extractor):
|
||||||
return
|
return
|
||||||
|
|
||||||
stagingdir = self.staging_dir(instance)
|
stagingdir = self.staging_dir(instance)
|
||||||
asset_name = instance.data["assetEntity"]["name"]
|
folder_name = instance.data["assetEntity"]["name"]
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
filename = f"{asset_name}_{subset}"
|
filename = f"{folder_name}_{product_name}"
|
||||||
|
|
||||||
path = os.path.join(stagingdir, filename)
|
path = os.path.join(stagingdir, filename)
|
||||||
|
|
||||||
|
|
@ -42,11 +42,11 @@ class ExtractThumbnail(publish.Extractor):
|
||||||
|
|
||||||
camera = instance.data.get("review_camera", "AUTO")
|
camera = instance.data.get("review_camera", "AUTO")
|
||||||
start = instance.data.get("frameStart", bpy.context.scene.frame_start)
|
start = instance.data.get("frameStart", bpy.context.scene.frame_start)
|
||||||
family = instance.data.get("family")
|
product_type = instance.data["productType"]
|
||||||
isolate = instance.data("isolate", None)
|
isolate = instance.data("isolate", None)
|
||||||
|
|
||||||
presets = json.loads(self.presets)
|
presets = json.loads(self.presets)
|
||||||
preset = presets.get(family, {})
|
preset = presets.get(product_type, {})
|
||||||
|
|
||||||
preset.update({
|
preset.update({
|
||||||
"camera": camera,
|
"camera": camera,
|
||||||
|
|
|
||||||
|
|
@ -28,25 +28,26 @@ class IntegrateAnimation(
|
||||||
# Update the json file for the setdress to add the published
|
# Update the json file for the setdress to add the published
|
||||||
# representations of the animations
|
# representations of the animations
|
||||||
for json_dict in data:
|
for json_dict in data:
|
||||||
|
json_product_name = json_dict["productName"]
|
||||||
i = None
|
i = None
|
||||||
for elem in instance.context:
|
for elem in instance.context:
|
||||||
if elem.data.get('subset') == json_dict['subset']:
|
if elem.data["productName"] == json_product_name:
|
||||||
i = elem
|
i = elem
|
||||||
break
|
break
|
||||||
if not i:
|
if not i:
|
||||||
continue
|
continue
|
||||||
rep = None
|
rep = None
|
||||||
pub_repr = i.data.get('published_representations')
|
pub_repr = i.data["published_representations"]
|
||||||
for elem in pub_repr:
|
for elem in pub_repr:
|
||||||
if pub_repr.get(elem).get('representation').get('name') == "fbx":
|
if pub_repr[elem]["representation"]["name"] == "fbx":
|
||||||
rep = pub_repr.get(elem)
|
rep = pub_repr[elem]
|
||||||
break
|
break
|
||||||
if not rep:
|
if not rep:
|
||||||
continue
|
continue
|
||||||
obj_id = rep.get('representation').get('_id')
|
obj_id = rep["representation"]["_id"]
|
||||||
|
|
||||||
if obj_id:
|
if obj_id:
|
||||||
json_dict['_id'] = str(obj_id)
|
json_dict["representation_id"] = str(obj_id)
|
||||||
|
|
||||||
with open(json_path, "w") as file:
|
with open(json_path, "w") as file:
|
||||||
json.dump(data, fp=file, indent=2)
|
json.dump(data, fp=file, indent=2)
|
||||||
|
|
|
||||||
|
|
@ -28,15 +28,27 @@ class ValidateDeadlinePublish(pyblish.api.InstancePlugin,
|
||||||
def process(self, instance):
|
def process(self, instance):
|
||||||
if not self.is_active(instance.data):
|
if not self.is_active(instance.data):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
tree = bpy.context.scene.node_tree
|
||||||
|
output_type = "CompositorNodeOutputFile"
|
||||||
|
output_node = None
|
||||||
|
# Remove all output nodes that inlcude "AYON" in the name.
|
||||||
|
# There should be only one.
|
||||||
|
for node in tree.nodes:
|
||||||
|
if node.bl_idname == output_type and "AYON" in node.name:
|
||||||
|
output_node = node
|
||||||
|
break
|
||||||
|
if not output_node:
|
||||||
|
raise PublishValidationError(
|
||||||
|
"No output node found in the compositor tree."
|
||||||
|
)
|
||||||
filepath = bpy.data.filepath
|
filepath = bpy.data.filepath
|
||||||
file = os.path.basename(filepath)
|
file = os.path.basename(filepath)
|
||||||
filename, ext = os.path.splitext(file)
|
filename, ext = os.path.splitext(file)
|
||||||
if filename not in bpy.context.scene.render.filepath:
|
if filename not in output_node.base_path:
|
||||||
raise PublishValidationError(
|
raise PublishValidationError(
|
||||||
"Render output folder "
|
"Render output folder doesn't match the blender scene name! "
|
||||||
"doesn't match the blender scene name! "
|
"Use Repair action to fix the folder file path."
|
||||||
"Use Repair action to "
|
|
||||||
"fix the folder file path."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
|
||||||
hosts = ["blender"]
|
hosts = ["blender"]
|
||||||
label = "Validate File Saved"
|
label = "Validate File Saved"
|
||||||
optional = False
|
optional = False
|
||||||
|
# TODO rename to 'exclude_product_types'
|
||||||
exclude_families = []
|
exclude_families = []
|
||||||
actions = [SaveWorkfileAction]
|
actions = [SaveWorkfileAction]
|
||||||
|
|
||||||
|
|
@ -41,8 +42,8 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
|
||||||
|
|
||||||
# Do not validate workfile has unsaved changes if only instances
|
# Do not validate workfile has unsaved changes if only instances
|
||||||
# present of families that should be excluded
|
# present of families that should be excluded
|
||||||
families = {
|
product_types = {
|
||||||
instance.data["family"] for instance in context
|
instance.data["productType"] for instance in context
|
||||||
# Consider only enabled instances
|
# Consider only enabled instances
|
||||||
if instance.data.get("publish", True)
|
if instance.data.get("publish", True)
|
||||||
and instance.data.get("active", True)
|
and instance.data.get("active", True)
|
||||||
|
|
@ -52,7 +53,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
|
||||||
return any(family in exclude_family
|
return any(family in exclude_family
|
||||||
for exclude_family in self.exclude_families)
|
for exclude_family in self.exclude_families)
|
||||||
|
|
||||||
if all(is_excluded(family) for family in families):
|
if all(is_excluded(product_type) for product_type in product_types):
|
||||||
self.log.debug("Only excluded families found, skipping workfile "
|
self.log.debug("Only excluded families found, skipping workfile "
|
||||||
"unsaved changes validation..")
|
"unsaved changes validation..")
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
|
||||||
asset_name = get_asset_name_identifier(asset_entity)
|
asset_name = get_asset_name_identifier(asset_entity)
|
||||||
|
|
||||||
shared_instance_data = {
|
shared_instance_data = {
|
||||||
"asset": asset_name,
|
"folderPath": asset_name,
|
||||||
"frameStart": asset_entity["data"]["frameStart"],
|
"frameStart": asset_entity["data"]["frameStart"],
|
||||||
"frameEnd": asset_entity["data"]["frameEnd"],
|
"frameEnd": asset_entity["data"]["frameEnd"],
|
||||||
"handleStart": asset_entity["data"]["handleStart"],
|
"handleStart": asset_entity["data"]["handleStart"],
|
||||||
|
|
@ -46,17 +46,18 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
|
||||||
shared_instance_data.update(celaction_kwargs)
|
shared_instance_data.update(celaction_kwargs)
|
||||||
|
|
||||||
# workfile instance
|
# workfile instance
|
||||||
family = "workfile"
|
product_type = "workfile"
|
||||||
subset = family + task.capitalize()
|
product_name = product_type + task.capitalize()
|
||||||
# Create instance
|
# Create instance
|
||||||
instance = context.create_instance(subset)
|
instance = context.create_instance(product_name)
|
||||||
|
|
||||||
# creating instance data
|
# creating instance data
|
||||||
instance.data.update({
|
instance.data.update({
|
||||||
"subset": subset,
|
|
||||||
"label": scene_file,
|
"label": scene_file,
|
||||||
"family": family,
|
"productName": product_name,
|
||||||
"families": [],
|
"productType": product_type,
|
||||||
|
"family": product_type,
|
||||||
|
"families": [product_type],
|
||||||
"representations": []
|
"representations": []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -76,17 +77,19 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
|
||||||
self.log.info('Publishing Celaction workfile')
|
self.log.info('Publishing Celaction workfile')
|
||||||
|
|
||||||
# render instance
|
# render instance
|
||||||
subset = f"render{task}Main"
|
product_name = f"render{task}Main"
|
||||||
instance = context.create_instance(name=subset)
|
product_type = "render.farm"
|
||||||
|
instance = context.create_instance(name=product_name)
|
||||||
# getting instance state
|
# getting instance state
|
||||||
instance.data["publish"] = True
|
instance.data["publish"] = True
|
||||||
|
|
||||||
# add assetEntity data into instance
|
# add assetEntity data into instance
|
||||||
instance.data.update({
|
instance.data.update({
|
||||||
"label": "{} - farm".format(subset),
|
"label": "{} - farm".format(product_name),
|
||||||
"family": "render.farm",
|
"productType": product_type,
|
||||||
"families": [],
|
"family": product_type,
|
||||||
"subset": subset
|
"families": [product_type],
|
||||||
|
"productName": product_name
|
||||||
})
|
})
|
||||||
|
|
||||||
# adding basic script data
|
# adding basic script data
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,14 @@ class CollectRenderPath(pyblish.api.InstancePlugin):
|
||||||
anatomy = instance.context.data["anatomy"]
|
anatomy = instance.context.data["anatomy"]
|
||||||
anatomy_data = copy.deepcopy(instance.data["anatomyData"])
|
anatomy_data = copy.deepcopy(instance.data["anatomyData"])
|
||||||
padding = anatomy.templates.get("frame_padding", 4)
|
padding = anatomy.templates.get("frame_padding", 4)
|
||||||
|
product_type = "render"
|
||||||
anatomy_data.update({
|
anatomy_data.update({
|
||||||
"frame": f"%0{padding}d",
|
"frame": f"%0{padding}d",
|
||||||
"family": "render",
|
"family": product_type,
|
||||||
"representation": self.output_extension,
|
"representation": self.output_extension,
|
||||||
"ext": self.output_extension
|
"ext": self.output_extension
|
||||||
})
|
})
|
||||||
|
anatomy_data["product"]["type"] = product_type
|
||||||
|
|
||||||
anatomy_filled = anatomy.format(anatomy_data)
|
anatomy_filled = anatomy.format(anatomy_data)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -147,8 +147,8 @@ def imprint(segment, data=None):
|
||||||
Examples:
|
Examples:
|
||||||
data = {
|
data = {
|
||||||
'asset': 'sq020sh0280',
|
'asset': 'sq020sh0280',
|
||||||
'family': 'render',
|
'productType': 'render',
|
||||||
'subset': 'subsetMain'
|
'productName': 'productMain'
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
data = data or {}
|
data = data or {}
|
||||||
|
|
|
||||||
|
|
@ -353,9 +353,9 @@ class PublishableClip:
|
||||||
rename_default = False
|
rename_default = False
|
||||||
hierarchy_default = "{_folder_}/{_sequence_}/{_track_}"
|
hierarchy_default = "{_folder_}/{_sequence_}/{_track_}"
|
||||||
clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}"
|
clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}"
|
||||||
subset_name_default = "[ track name ]"
|
|
||||||
review_track_default = "[ none ]"
|
review_track_default = "[ none ]"
|
||||||
subset_family_default = "plate"
|
base_product_name_default = "[ track name ]"
|
||||||
|
base_product_type_default = "plate"
|
||||||
count_from_default = 10
|
count_from_default = 10
|
||||||
count_steps_default = 10
|
count_steps_default = 10
|
||||||
vertical_sync_default = False
|
vertical_sync_default = False
|
||||||
|
|
@ -368,7 +368,7 @@ class PublishableClip:
|
||||||
|
|
||||||
def __init__(self, segment, **kwargs):
|
def __init__(self, segment, **kwargs):
|
||||||
self.rename_index = kwargs["rename_index"]
|
self.rename_index = kwargs["rename_index"]
|
||||||
self.family = kwargs["family"]
|
self.product_type = kwargs["family"]
|
||||||
self.log = kwargs["log"]
|
self.log = kwargs["log"]
|
||||||
|
|
||||||
# get main parent objects
|
# get main parent objects
|
||||||
|
|
@ -486,10 +486,10 @@ class PublishableClip:
|
||||||
"countFrom", {}).get("value") or self.count_from_default
|
"countFrom", {}).get("value") or self.count_from_default
|
||||||
self.count_steps = self.ui_inputs.get(
|
self.count_steps = self.ui_inputs.get(
|
||||||
"countSteps", {}).get("value") or self.count_steps_default
|
"countSteps", {}).get("value") or self.count_steps_default
|
||||||
self.subset_name = self.ui_inputs.get(
|
self.base_product_name = self.ui_inputs.get(
|
||||||
"subsetName", {}).get("value") or self.subset_name_default
|
"productName", {}).get("value") or self.base_product_name_default
|
||||||
self.subset_family = self.ui_inputs.get(
|
self.base_product_type = self.ui_inputs.get(
|
||||||
"subsetFamily", {}).get("value") or self.subset_family_default
|
"productType", {}).get("value") or self.base_product_type_default
|
||||||
self.vertical_sync = self.ui_inputs.get(
|
self.vertical_sync = self.ui_inputs.get(
|
||||||
"vSyncOn", {}).get("value") or self.vertical_sync_default
|
"vSyncOn", {}).get("value") or self.vertical_sync_default
|
||||||
self.driving_layer = self.ui_inputs.get(
|
self.driving_layer = self.ui_inputs.get(
|
||||||
|
|
@ -509,12 +509,14 @@ class PublishableClip:
|
||||||
or self.retimed_framerange_default
|
or self.retimed_framerange_default
|
||||||
)
|
)
|
||||||
|
|
||||||
# build subset name from layer name
|
# build product name from layer name
|
||||||
if self.subset_name == "[ track name ]":
|
if self.base_product_name == "[ track name ]":
|
||||||
self.subset_name = self.track_name
|
self.base_product_name = self.track_name
|
||||||
|
|
||||||
# create subset for publishing
|
# create product for publishing
|
||||||
self.subset = self.subset_family + self.subset_name.capitalize()
|
self.product_name = (
|
||||||
|
self.base_product_type + self.base_product_name.capitalize()
|
||||||
|
)
|
||||||
|
|
||||||
def _replace_hash_to_expression(self, name, text):
|
def _replace_hash_to_expression(self, name, text):
|
||||||
""" Replace hash with number in correct padding. """
|
""" Replace hash with number in correct padding. """
|
||||||
|
|
@ -608,14 +610,14 @@ class PublishableClip:
|
||||||
_hero_data = deepcopy(hero_data)
|
_hero_data = deepcopy(hero_data)
|
||||||
_hero_data.update({"heroTrack": False})
|
_hero_data.update({"heroTrack": False})
|
||||||
if _in <= self.clip_in and _out >= self.clip_out:
|
if _in <= self.clip_in and _out >= self.clip_out:
|
||||||
data_subset = hero_data["subset"]
|
data_product_name = hero_data["productName"]
|
||||||
# add track index in case duplicity of names in hero data
|
# add track index in case duplicity of names in hero data
|
||||||
if self.subset in data_subset:
|
if self.product_name in data_product_name:
|
||||||
_hero_data["subset"] = self.subset + str(
|
_hero_data["productName"] = self.product_name + str(
|
||||||
self.track_index)
|
self.track_index)
|
||||||
# in case track name and subset name is the same then add
|
# in case track name and product name is the same then add
|
||||||
if self.subset_name == self.track_name:
|
if self.base_product_name == self.track_name:
|
||||||
_hero_data["subset"] = self.subset
|
_hero_data["productName"] = self.product_name
|
||||||
# assign data to return hierarchy data to tag
|
# assign data to return hierarchy data to tag
|
||||||
tag_hierarchy_data = _hero_data
|
tag_hierarchy_data = _hero_data
|
||||||
break
|
break
|
||||||
|
|
@ -637,9 +639,9 @@ class PublishableClip:
|
||||||
"hierarchy": hierarchy_filled,
|
"hierarchy": hierarchy_filled,
|
||||||
"parents": self.parents,
|
"parents": self.parents,
|
||||||
"hierarchyData": hierarchy_formatting_data,
|
"hierarchyData": hierarchy_formatting_data,
|
||||||
"subset": self.subset,
|
"productName": self.product_name,
|
||||||
"family": self.subset_family,
|
"productType": self.base_product_type,
|
||||||
"families": [self.family]
|
"families": [self.base_product_type, self.product_type]
|
||||||
}
|
}
|
||||||
|
|
||||||
def _convert_to_entity(self, type, template):
|
def _convert_to_entity(self, type, template):
|
||||||
|
|
@ -704,7 +706,7 @@ class ClipLoader(LoaderPlugin):
|
||||||
_mapping = None
|
_mapping = None
|
||||||
_host_settings = None
|
_host_settings = None
|
||||||
|
|
||||||
def apply_settings(cls, project_settings, system_settings):
|
def apply_settings(cls, project_settings):
|
||||||
|
|
||||||
plugin_type_settings = (
|
plugin_type_settings = (
|
||||||
project_settings
|
project_settings
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ class CreateShotClip(opfapi.Creator):
|
||||||
"""Publishable clip"""
|
"""Publishable clip"""
|
||||||
|
|
||||||
label = "Create Publishable Clip"
|
label = "Create Publishable Clip"
|
||||||
family = "clip"
|
product_type = "clip"
|
||||||
icon = "film"
|
icon = "film"
|
||||||
defaults = ["Main"]
|
defaults = ["Main"]
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ class CreateShotClip(opfapi.Creator):
|
||||||
|
|
||||||
# open widget for plugins inputs
|
# open widget for plugins inputs
|
||||||
results_back = self.create_widget(
|
results_back = self.create_widget(
|
||||||
"Pype publish attributes creator",
|
"AYON publish attributes creator",
|
||||||
"Define sequential rename and fill hierarchy data.",
|
"Define sequential rename and fill hierarchy data.",
|
||||||
gui_inputs
|
gui_inputs
|
||||||
)
|
)
|
||||||
|
|
@ -62,7 +62,7 @@ class CreateShotClip(opfapi.Creator):
|
||||||
"log": self.log,
|
"log": self.log,
|
||||||
"ui_inputs": results_back,
|
"ui_inputs": results_back,
|
||||||
"avalon": self.data,
|
"avalon": self.data,
|
||||||
"family": self.data["family"]
|
"product_type": self.data["productType"]
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, segment in enumerate(sorted_selected_segments):
|
for i, segment in enumerate(sorted_selected_segments):
|
||||||
|
|
@ -203,19 +203,19 @@ class CreateShotClip(opfapi.Creator):
|
||||||
"target": "ui",
|
"target": "ui",
|
||||||
"order": 3,
|
"order": 3,
|
||||||
"value": {
|
"value": {
|
||||||
"subsetName": {
|
"productName": {
|
||||||
"value": ["[ track name ]", "main", "bg", "fg", "bg",
|
"value": ["[ track name ]", "main", "bg", "fg", "bg",
|
||||||
"animatic"],
|
"animatic"],
|
||||||
"type": "QComboBox",
|
"type": "QComboBox",
|
||||||
"label": "Subset Name",
|
"label": "Subset Name",
|
||||||
"target": "ui",
|
"target": "ui",
|
||||||
"toolTip": "chose subset name pattern, if [ track name ] is selected, name of track layer will be used", # noqa
|
"toolTip": "chose product name pattern, if [ track name ] is selected, name of track layer will be used", # noqa
|
||||||
"order": 0},
|
"order": 0},
|
||||||
"subsetFamily": {
|
"productType": {
|
||||||
"value": ["plate", "take"],
|
"value": ["plate", "take"],
|
||||||
"type": "QComboBox",
|
"type": "QComboBox",
|
||||||
"label": "Subset Family",
|
"label": "Subset Family",
|
||||||
"target": "ui", "toolTip": "What use of this subset is for", # noqa
|
"target": "ui", "toolTip": "What use of this product is for", # noqa
|
||||||
"order": 1},
|
"order": 1},
|
||||||
"reviewTrack": {
|
"reviewTrack": {
|
||||||
"value": ["< none >"] + gui_tracks,
|
"value": ["< none >"] + gui_tracks,
|
||||||
|
|
@ -229,7 +229,7 @@ class CreateShotClip(opfapi.Creator):
|
||||||
"type": "QCheckBox",
|
"type": "QCheckBox",
|
||||||
"label": "Include audio",
|
"label": "Include audio",
|
||||||
"target": "tag",
|
"target": "tag",
|
||||||
"toolTip": "Process subsets with corresponding audio", # noqa
|
"toolTip": "Process products with corresponding audio", # noqa
|
||||||
"order": 3},
|
"order": 3},
|
||||||
"sourceResolution": {
|
"sourceResolution": {
|
||||||
"value": False,
|
"value": False,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ from ayon_core.lib.transcoding import (
|
||||||
|
|
||||||
|
|
||||||
class LoadClip(opfapi.ClipLoader):
|
class LoadClip(opfapi.ClipLoader):
|
||||||
"""Load a subset to timeline as clip
|
"""Load a product to timeline as clip
|
||||||
|
|
||||||
Place clip to timeline on its asset origin timings collected
|
Place clip to timeline on its asset origin timings collected
|
||||||
during conforming to project
|
during conforming to project
|
||||||
|
|
@ -31,14 +31,14 @@ class LoadClip(opfapi.ClipLoader):
|
||||||
# settings
|
# settings
|
||||||
reel_group_name = "OpenPype_Reels"
|
reel_group_name = "OpenPype_Reels"
|
||||||
reel_name = "Loaded"
|
reel_name = "Loaded"
|
||||||
clip_name_template = "{asset}_{subset}<_{output}>"
|
clip_name_template = "{folder[name]}_{product[name]}<_{output}>"
|
||||||
|
|
||||||
""" Anatomy keys from version context data and dynamically added:
|
""" Anatomy keys from version context data and dynamically added:
|
||||||
- {layerName} - original layer name token
|
- {layerName} - original layer name token
|
||||||
- {layerUID} - original layer UID token
|
- {layerUID} - original layer UID token
|
||||||
- {originalBasename} - original clip name taken from file
|
- {originalBasename} - original clip name taken from file
|
||||||
"""
|
"""
|
||||||
layer_rename_template = "{asset}_{subset}<_{output}>"
|
layer_rename_template = "{folder[name]}_{product[name]}<_{output}>"
|
||||||
layer_rename_patterns = []
|
layer_rename_patterns = []
|
||||||
|
|
||||||
def load(self, context, name, namespace, options):
|
def load(self, context, name, namespace, options):
|
||||||
|
|
@ -180,27 +180,27 @@ class LoadClip(opfapi.ClipLoader):
|
||||||
# unwrapping segment from input clip
|
# unwrapping segment from input clip
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# def switch(self, container, representation):
|
# def switch(self, container, context):
|
||||||
# self.update(container, representation)
|
# self.update(container, context)
|
||||||
|
|
||||||
# def update(self, container, representation):
|
# def update(self, container, context):
|
||||||
# """ Updating previously loaded clips
|
# """ Updating previously loaded clips
|
||||||
# """
|
# """
|
||||||
|
|
||||||
# # load clip to timeline and get main variables
|
# # load clip to timeline and get main variables
|
||||||
|
# repre_doc = context['representation']
|
||||||
# name = container['name']
|
# name = container['name']
|
||||||
# namespace = container['namespace']
|
# namespace = container['namespace']
|
||||||
# track_item = phiero.get_track_items(
|
# track_item = phiero.get_track_items(
|
||||||
# track_item_name=namespace)
|
# track_item_name=namespace)
|
||||||
# version = io.find_one({
|
# version = io.find_one({
|
||||||
# "type": "version",
|
# "type": "version",
|
||||||
# "_id": representation["parent"]
|
# "_id": repre_doc["parent"]
|
||||||
# })
|
# })
|
||||||
# version_data = version.get("data", {})
|
# version_data = version.get("data", {})
|
||||||
# version_name = version.get("name", None)
|
# version_name = version.get("name", None)
|
||||||
# colorspace = version_data.get("colorspace", None)
|
# colorspace = version_data.get("colorspace", None)
|
||||||
# object_name = "{}_{}".format(name, namespace)
|
# object_name = "{}_{}".format(name, namespace)
|
||||||
# file = get_representation_path(representation).replace("\\", "/")
|
# file = get_representation_path(repre_doc).replace("\\", "/")
|
||||||
# clip = track_item.source()
|
# clip = track_item.source()
|
||||||
|
|
||||||
# # reconnect media to new path
|
# # reconnect media to new path
|
||||||
|
|
@ -225,7 +225,7 @@ class LoadClip(opfapi.ClipLoader):
|
||||||
|
|
||||||
# # add variables related to version context
|
# # add variables related to version context
|
||||||
# data_imprint.update({
|
# data_imprint.update({
|
||||||
# "representation": str(representation["_id"]),
|
# "representation": str(repre_doc["_id"]),
|
||||||
# "version": version_name,
|
# "version": version_name,
|
||||||
# "colorspace": colorspace,
|
# "colorspace": colorspace,
|
||||||
# "objectName": object_name
|
# "objectName": object_name
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from ayon_core.lib.transcoding import (
|
||||||
)
|
)
|
||||||
|
|
||||||
class LoadClipBatch(opfapi.ClipLoader):
|
class LoadClipBatch(opfapi.ClipLoader):
|
||||||
"""Load a subset to timeline as clip
|
"""Load a product to timeline as clip
|
||||||
|
|
||||||
Place clip to timeline on its asset origin timings collected
|
Place clip to timeline on its asset origin timings collected
|
||||||
during conforming to project
|
during conforming to project
|
||||||
|
|
@ -29,14 +29,14 @@ class LoadClipBatch(opfapi.ClipLoader):
|
||||||
|
|
||||||
# settings
|
# settings
|
||||||
reel_name = "OP_LoadedReel"
|
reel_name = "OP_LoadedReel"
|
||||||
clip_name_template = "{batch}_{asset}_{subset}<_{output}>"
|
clip_name_template = "{batch}_{folder[name]}_{product[name]}<_{output}>"
|
||||||
|
|
||||||
""" Anatomy keys from version context data and dynamically added:
|
""" Anatomy keys from version context data and dynamically added:
|
||||||
- {layerName} - original layer name token
|
- {layerName} - original layer name token
|
||||||
- {layerUID} - original layer UID token
|
- {layerUID} - original layer UID token
|
||||||
- {originalBasename} - original clip name taken from file
|
- {originalBasename} - original clip name taken from file
|
||||||
"""
|
"""
|
||||||
layer_rename_template = "{asset}_{subset}<_{output}>"
|
layer_rename_template = "{folder[name]}_{product[name]}<_{output}>"
|
||||||
layer_rename_patterns = []
|
layer_rename_patterns = []
|
||||||
|
|
||||||
def load(self, context, name, namespace, options):
|
def load(self, context, name, namespace, options):
|
||||||
|
|
@ -50,17 +50,8 @@ class LoadClipBatch(opfapi.ClipLoader):
|
||||||
version_name = version.get("name", None)
|
version_name = version.get("name", None)
|
||||||
colorspace = self.get_colorspace(context)
|
colorspace = self.get_colorspace(context)
|
||||||
|
|
||||||
# TODO remove '{folder[name]}' and '{product[name]}' replacement
|
clip_name_template = self.clip_name_template
|
||||||
clip_name_template = (
|
layer_rename_template = self.layer_rename_template
|
||||||
self.clip_name_template
|
|
||||||
.replace("{folder[name]}", "{asset}")
|
|
||||||
.replace("{product[name]}", "{subset}")
|
|
||||||
)
|
|
||||||
layer_rename_template = (
|
|
||||||
self.layer_rename_template
|
|
||||||
.replace("{folder[name]}", "{asset}")
|
|
||||||
.replace("{product[name]}", "{subset}")
|
|
||||||
)
|
|
||||||
# in case output is not in context replace key to representation
|
# in case output is not in context replace key to representation
|
||||||
if not context["representation"]["context"].get("output"):
|
if not context["representation"]["context"].get("output"):
|
||||||
clip_name_template = clip_name_template.replace(
|
clip_name_template = clip_name_template.replace(
|
||||||
|
|
@ -68,8 +59,22 @@ class LoadClipBatch(opfapi.ClipLoader):
|
||||||
layer_rename_template = layer_rename_template.replace(
|
layer_rename_template = layer_rename_template.replace(
|
||||||
"output", "representation")
|
"output", "representation")
|
||||||
|
|
||||||
|
asset_doc = context["asset"]
|
||||||
|
subset_doc = context["subset"]
|
||||||
formatting_data = deepcopy(context["representation"]["context"])
|
formatting_data = deepcopy(context["representation"]["context"])
|
||||||
formatting_data["batch"] = self.batch.name.get_value()
|
formatting_data["batch"] = self.batch.name.get_value()
|
||||||
|
formatting_data.update({
|
||||||
|
"asset": asset_doc["name"],
|
||||||
|
"folder": {
|
||||||
|
"name": asset_doc["name"],
|
||||||
|
},
|
||||||
|
"subset": subset_doc["name"],
|
||||||
|
"family": subset_doc["data"]["family"],
|
||||||
|
"product": {
|
||||||
|
"name": subset_doc["name"],
|
||||||
|
"type": subset_doc["data"]["family"],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
clip_name = StringTemplate(clip_name_template).format(
|
clip_name = StringTemplate(clip_name_template).format(
|
||||||
formatting_data)
|
formatting_data)
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,6 @@ class CollectTestSelection(pyblish.api.ContextPlugin):
|
||||||
|
|
||||||
opfapi.imprint(segment, {
|
opfapi.imprint(segment, {
|
||||||
'asset': segment.name.get_value(),
|
'asset': segment.name.get_value(),
|
||||||
'family': 'render',
|
'productType': 'render',
|
||||||
'subset': 'subsetMain'
|
'productName': 'productMain'
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ from types import NoneType
|
||||||
import pyblish
|
import pyblish
|
||||||
import ayon_core.hosts.flame.api as opfapi
|
import ayon_core.hosts.flame.api as opfapi
|
||||||
from ayon_core.hosts.flame.otio import flame_export
|
from ayon_core.hosts.flame.otio import flame_export
|
||||||
|
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||||
from ayon_core.pipeline.editorial import (
|
from ayon_core.pipeline.editorial import (
|
||||||
is_overlapping_otio_ranges,
|
is_overlapping_otio_ranges,
|
||||||
get_media_range_with_retimes
|
get_media_range_with_retimes
|
||||||
|
|
@ -47,7 +48,9 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
|
||||||
if not marker_data:
|
if not marker_data:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if marker_data.get("id") != "pyblish.avalon.instance":
|
if marker_data.get("id") not in {
|
||||||
|
AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||||
|
}:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.log.debug("__ segment.name: {}".format(
|
self.log.debug("__ segment.name: {}".format(
|
||||||
|
|
@ -107,24 +110,25 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
|
||||||
# add ocio_data to instance data
|
# add ocio_data to instance data
|
||||||
inst_data.update(otio_data)
|
inst_data.update(otio_data)
|
||||||
|
|
||||||
asset = marker_data["asset"]
|
folder_path = marker_data["folderPath"]
|
||||||
subset = marker_data["subset"]
|
folder_name = folder_path.rsplit("/")[-1]
|
||||||
|
product_name = marker_data["productName"]
|
||||||
|
|
||||||
# insert family into families
|
# insert product type into families
|
||||||
family = marker_data["family"]
|
product_type = marker_data["productType"]
|
||||||
families = [str(f) for f in marker_data["families"]]
|
families = [str(f) for f in marker_data["families"]]
|
||||||
families.insert(0, str(family))
|
families.insert(0, str(product_type))
|
||||||
|
|
||||||
# form label
|
# form label
|
||||||
label = asset
|
label = folder_name
|
||||||
if asset != clip_name:
|
if folder_name != clip_name:
|
||||||
label += " ({})".format(clip_name)
|
label += " ({})".format(clip_name)
|
||||||
label += " {} [{}]".format(subset, ", ".join(families))
|
label += " {} [{}]".format(product_name, ", ".join(families))
|
||||||
|
|
||||||
inst_data.update({
|
inst_data.update({
|
||||||
"name": "{}_{}".format(asset, subset),
|
"name": "{}_{}".format(folder_name, product_name),
|
||||||
"label": label,
|
"label": label,
|
||||||
"asset": asset,
|
"folderPath": folder_path,
|
||||||
"item": segment,
|
"item": segment,
|
||||||
"families": families,
|
"families": families,
|
||||||
"publish": marker_data["publish"],
|
"publish": marker_data["publish"],
|
||||||
|
|
@ -332,26 +336,28 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
|
||||||
if not hierarchy_data:
|
if not hierarchy_data:
|
||||||
return
|
return
|
||||||
|
|
||||||
asset = data["asset"]
|
folder_path = data["folderPath"]
|
||||||
subset = "shotMain"
|
folder_name = folder_path.rsplit("/")[-1]
|
||||||
|
product_name = "shotMain"
|
||||||
|
|
||||||
# insert family into families
|
# insert product type into families
|
||||||
family = "shot"
|
product_type = "shot"
|
||||||
|
|
||||||
# form label
|
# form label
|
||||||
label = asset
|
label = folder_name
|
||||||
if asset != clip_name:
|
if folder_name != clip_name:
|
||||||
label += " ({}) ".format(clip_name)
|
label += " ({}) ".format(clip_name)
|
||||||
label += " {}".format(subset)
|
label += " {}".format(product_name)
|
||||||
label += " [{}]".format(family)
|
label += " [{}]".format(product_type)
|
||||||
|
|
||||||
data.update({
|
data.update({
|
||||||
"name": "{}_{}".format(asset, subset),
|
"name": "{}_{}".format(folder_name, product_name),
|
||||||
"label": label,
|
"label": label,
|
||||||
"subset": subset,
|
"productName": product_name,
|
||||||
"asset": asset,
|
"folderPath": folder_path,
|
||||||
"family": family,
|
"productType": product_type,
|
||||||
"families": []
|
"family": product_type,
|
||||||
|
"families": [product_type]
|
||||||
})
|
})
|
||||||
|
|
||||||
instance = context.create_instance(**data)
|
instance = context.create_instance(**data)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import pyblish.api
|
||||||
from ayon_core.client import get_asset_name_identifier
|
from ayon_core.client import get_asset_name_identifier
|
||||||
import ayon_core.hosts.flame.api as opfapi
|
import ayon_core.hosts.flame.api as opfapi
|
||||||
from ayon_core.hosts.flame.otio import flame_export
|
from ayon_core.hosts.flame.otio import flame_export
|
||||||
from ayon_core.pipeline.create import get_subset_name
|
from ayon_core.pipeline.create import get_product_name
|
||||||
|
|
||||||
|
|
||||||
class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
||||||
|
|
@ -14,7 +14,7 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
||||||
|
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
# plugin defined
|
# plugin defined
|
||||||
family = "workfile"
|
product_type = "workfile"
|
||||||
variant = "otioTimeline"
|
variant = "otioTimeline"
|
||||||
|
|
||||||
# main
|
# main
|
||||||
|
|
@ -23,29 +23,30 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
||||||
project = opfapi.get_current_project()
|
project = opfapi.get_current_project()
|
||||||
sequence = opfapi.get_current_sequence(opfapi.CTX.selection)
|
sequence = opfapi.get_current_sequence(opfapi.CTX.selection)
|
||||||
|
|
||||||
# create subset name
|
# create product name
|
||||||
subset_name = get_subset_name(
|
product_name = get_product_name(
|
||||||
family,
|
|
||||||
variant,
|
|
||||||
task_name,
|
|
||||||
asset_doc,
|
|
||||||
context.data["projectName"],
|
context.data["projectName"],
|
||||||
|
asset_doc,
|
||||||
|
task_name,
|
||||||
context.data["hostName"],
|
context.data["hostName"],
|
||||||
|
product_type,
|
||||||
|
variant,
|
||||||
project_settings=context.data["project_settings"]
|
project_settings=context.data["project_settings"]
|
||||||
)
|
)
|
||||||
|
|
||||||
asset_name = get_asset_name_identifier(asset_doc)
|
folder_path = get_asset_name_identifier(asset_doc)
|
||||||
|
|
||||||
# adding otio timeline to context
|
# adding otio timeline to context
|
||||||
with opfapi.maintained_segment_selection(sequence) as selected_seg:
|
with opfapi.maintained_segment_selection(sequence) as selected_seg:
|
||||||
otio_timeline = flame_export.create_otio_timeline(sequence)
|
otio_timeline = flame_export.create_otio_timeline(sequence)
|
||||||
|
|
||||||
instance_data = {
|
instance_data = {
|
||||||
"name": subset_name,
|
"name": product_name,
|
||||||
"asset": asset_name,
|
"folderPath": folder_path,
|
||||||
"subset": subset_name,
|
"productName": product_name,
|
||||||
"family": "workfile",
|
"productType": product_type,
|
||||||
"families": []
|
"family": product_type,
|
||||||
|
"families": [product_type]
|
||||||
}
|
}
|
||||||
|
|
||||||
# create instance with workfile
|
# create instance with workfile
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ class ExtractProductResources(publish.Extractor):
|
||||||
|
|
||||||
# flame objects
|
# flame objects
|
||||||
segment = instance.data["item"]
|
segment = instance.data["item"]
|
||||||
asset_name = instance.data["asset"]
|
folder_path = instance.data["folderPath"]
|
||||||
segment_name = segment.name.get_value()
|
segment_name = segment.name.get_value()
|
||||||
clip_path = instance.data["path"]
|
clip_path = instance.data["path"]
|
||||||
sequence_clip = instance.context.data["flameSequence"]
|
sequence_clip = instance.context.data["flameSequence"]
|
||||||
|
|
@ -249,7 +249,7 @@ class ExtractProductResources(publish.Extractor):
|
||||||
out_mark = in_mark + source_duration_handles
|
out_mark = in_mark + source_duration_handles
|
||||||
exporting_clip = self.import_clip(clip_path)
|
exporting_clip = self.import_clip(clip_path)
|
||||||
exporting_clip.name.set_value("{}_{}".format(
|
exporting_clip.name.set_value("{}_{}".format(
|
||||||
asset_name, segment_name))
|
folder_path, segment_name))
|
||||||
|
|
||||||
# add xml tags modifications
|
# add xml tags modifications
|
||||||
modify_xml_data.update({
|
modify_xml_data.update({
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,8 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin):
|
||||||
))
|
))
|
||||||
|
|
||||||
# load plate to batch group
|
# load plate to batch group
|
||||||
self.log.info("Loading subset `{}` into batch `{}`".format(
|
self.log.info("Loading product `{}` into batch `{}`".format(
|
||||||
instance.data["subset"], bgroup.name.get_value()
|
instance.data["productName"], bgroup.name.get_value()
|
||||||
))
|
))
|
||||||
self._load_clip_to_context(instance, bgroup)
|
self._load_clip_to_context(instance, bgroup)
|
||||||
|
|
||||||
|
|
@ -168,10 +168,10 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin):
|
||||||
handle_start = instance.data["handleStart"]
|
handle_start = instance.data["handleStart"]
|
||||||
handle_end = instance.data["handleEnd"]
|
handle_end = instance.data["handleEnd"]
|
||||||
frame_duration = (frame_end - frame_start) + 1
|
frame_duration = (frame_end - frame_start) + 1
|
||||||
asset_name = instance.data["asset"]
|
folder_path = instance.data["folderPath"]
|
||||||
|
|
||||||
task_name = task_data["name"]
|
task_name = task_data["name"]
|
||||||
batchgroup_name = "{}_{}".format(asset_name, task_name)
|
batchgroup_name = "{}_{}".format(folder_path, task_name)
|
||||||
|
|
||||||
batch_data = {
|
batch_data = {
|
||||||
"shematic_reels": [
|
"shematic_reels": [
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ from ayon_core.lib import (
|
||||||
)
|
)
|
||||||
from ayon_core.pipeline import (
|
from ayon_core.pipeline import (
|
||||||
Creator,
|
Creator,
|
||||||
CreatedInstance
|
CreatedInstance,
|
||||||
|
AVALON_INSTANCE_ID,
|
||||||
|
AYON_INSTANCE_ID,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -31,14 +33,16 @@ class GenericCreateSaver(Creator):
|
||||||
|
|
||||||
# TODO: This should be renamed together with Nuke so it is aligned
|
# TODO: This should be renamed together with Nuke so it is aligned
|
||||||
temp_rendering_path_template = (
|
temp_rendering_path_template = (
|
||||||
"{workdir}/renders/fusion/{subset}/{subset}.{frame}.{ext}")
|
"{workdir}/renders/fusion/{product[name]}/"
|
||||||
|
"{product[name]}.{frame}.{ext}"
|
||||||
|
)
|
||||||
|
|
||||||
def create(self, subset_name, instance_data, pre_create_data):
|
def create(self, product_name, instance_data, pre_create_data):
|
||||||
self.pass_pre_attributes_to_instance(instance_data, pre_create_data)
|
self.pass_pre_attributes_to_instance(instance_data, pre_create_data)
|
||||||
|
|
||||||
instance = CreatedInstance(
|
instance = CreatedInstance(
|
||||||
family=self.family,
|
product_type=self.product_type,
|
||||||
subset_name=subset_name,
|
product_name=product_name,
|
||||||
data=instance_data,
|
data=instance_data,
|
||||||
creator=self,
|
creator=self,
|
||||||
)
|
)
|
||||||
|
|
@ -109,23 +113,23 @@ class GenericCreateSaver(Creator):
|
||||||
tool.SetData(f"openpype.{key}", value)
|
tool.SetData(f"openpype.{key}", value)
|
||||||
|
|
||||||
def _update_tool_with_data(self, tool, data):
|
def _update_tool_with_data(self, tool, data):
|
||||||
"""Update tool node name and output path based on subset data"""
|
"""Update tool node name and output path based on product data"""
|
||||||
if "subset" not in data:
|
if "productName" not in data:
|
||||||
return
|
return
|
||||||
|
|
||||||
original_subset = tool.GetData("openpype.subset")
|
original_product_name = tool.GetData("openpype.productName")
|
||||||
original_format = tool.GetData(
|
original_format = tool.GetData(
|
||||||
"openpype.creator_attributes.image_format"
|
"openpype.creator_attributes.image_format"
|
||||||
)
|
)
|
||||||
|
|
||||||
subset = data["subset"]
|
product_name = data["productName"]
|
||||||
if (
|
if (
|
||||||
original_subset != subset
|
original_product_name != product_name
|
||||||
or original_format != data["creator_attributes"]["image_format"]
|
or original_format != data["creator_attributes"]["image_format"]
|
||||||
):
|
):
|
||||||
self._configure_saver_tool(data, tool, subset)
|
self._configure_saver_tool(data, tool, product_name)
|
||||||
|
|
||||||
def _configure_saver_tool(self, data, tool, subset):
|
def _configure_saver_tool(self, data, tool, product_name):
|
||||||
formatting_data = deepcopy(data)
|
formatting_data = deepcopy(data)
|
||||||
|
|
||||||
# get frame padding from anatomy templates
|
# get frame padding from anatomy templates
|
||||||
|
|
@ -135,25 +139,39 @@ class GenericCreateSaver(Creator):
|
||||||
ext = data["creator_attributes"]["image_format"]
|
ext = data["creator_attributes"]["image_format"]
|
||||||
|
|
||||||
# Subset change detected
|
# Subset change detected
|
||||||
|
product_type = formatting_data["productType"]
|
||||||
|
f_product_name = formatting_data["productName"]
|
||||||
|
|
||||||
|
folder_path = formatting_data["folderPath"]
|
||||||
|
folder_name = folder_path.rsplit("/", 1)[-1]
|
||||||
|
|
||||||
workdir = os.path.normpath(os.getenv("AYON_WORKDIR"))
|
workdir = os.path.normpath(os.getenv("AYON_WORKDIR"))
|
||||||
formatting_data.update({
|
formatting_data.update({
|
||||||
"workdir": workdir,
|
"workdir": workdir,
|
||||||
"frame": "0" * frame_padding,
|
"frame": "0" * frame_padding,
|
||||||
"ext": ext,
|
"ext": ext,
|
||||||
"product": {
|
"product": {
|
||||||
"name": formatting_data["subset"],
|
"name": f_product_name,
|
||||||
"type": formatting_data["family"],
|
"type": product_type,
|
||||||
},
|
},
|
||||||
|
# TODO add more variants for 'folder' and 'task'
|
||||||
|
"folder": {
|
||||||
|
"name": folder_name,
|
||||||
|
},
|
||||||
|
"task": {
|
||||||
|
"name": data["task"],
|
||||||
|
},
|
||||||
|
# Backwards compatibility
|
||||||
|
"asset": folder_name,
|
||||||
|
"subset": f_product_name,
|
||||||
|
"family": product_type,
|
||||||
})
|
})
|
||||||
|
|
||||||
# build file path to render
|
# build file path to render
|
||||||
# TODO make sure the keys are available in 'formatting_data'
|
# TODO make sure the keys are available in 'formatting_data'
|
||||||
temp_rendering_path_template = (
|
temp_rendering_path_template = (
|
||||||
self.temp_rendering_path_template
|
self.temp_rendering_path_template
|
||||||
.replace("{product[name]}", "{subset}")
|
.replace("{task}", "{task[name]}")
|
||||||
.replace("{product[type]}", "{family}")
|
|
||||||
.replace("{folder[name]}", "{asset}")
|
|
||||||
.replace("{task[name]}", "{task}")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
filepath = temp_rendering_path_template.format(**formatting_data)
|
filepath = temp_rendering_path_template.format(**formatting_data)
|
||||||
|
|
@ -162,9 +180,9 @@ class GenericCreateSaver(Creator):
|
||||||
tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath))
|
tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath))
|
||||||
|
|
||||||
# Rename tool
|
# Rename tool
|
||||||
if tool.Name != subset:
|
if tool.Name != product_name:
|
||||||
print(f"Renaming {tool.Name} -> {subset}")
|
print(f"Renaming {tool.Name} -> {product_name}")
|
||||||
tool.SetAttrs({"TOOLS_Name": subset})
|
tool.SetAttrs({"TOOLS_Name": product_name})
|
||||||
|
|
||||||
def get_managed_tool_data(self, tool):
|
def get_managed_tool_data(self, tool):
|
||||||
"""Return data of the tool if it matches creator identifier"""
|
"""Return data of the tool if it matches creator identifier"""
|
||||||
|
|
@ -172,13 +190,13 @@ class GenericCreateSaver(Creator):
|
||||||
if not isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
return
|
return
|
||||||
|
|
||||||
required = {
|
if (
|
||||||
"id": "pyblish.avalon.instance",
|
data.get("creator_identifier") != self.identifier
|
||||||
"creator_identifier": self.identifier,
|
or data.get("id") not in {
|
||||||
}
|
AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||||
for key, value in required.items():
|
}
|
||||||
if key not in data or data[key] != value:
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Get active state from the actual tool state
|
# Get active state from the actual tool state
|
||||||
attrs = tool.GetAttrs()
|
attrs = tool.GetAttrs()
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ class CreateImageSaver(GenericCreateSaver):
|
||||||
identifier = "io.openpype.creators.fusion.imagesaver"
|
identifier = "io.openpype.creators.fusion.imagesaver"
|
||||||
label = "Image (saver)"
|
label = "Image (saver)"
|
||||||
name = "image"
|
name = "image"
|
||||||
family = "image"
|
product_type = "image"
|
||||||
description = "Fusion Saver to generate image"
|
description = "Fusion Saver to generate image"
|
||||||
|
|
||||||
default_frame = 0
|
default_frame = 0
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ class CreateSaver(GenericCreateSaver):
|
||||||
identifier = "io.openpype.creators.fusion.saver"
|
identifier = "io.openpype.creators.fusion.saver"
|
||||||
label = "Render (saver)"
|
label = "Render (saver)"
|
||||||
name = "render"
|
name = "render"
|
||||||
family = "render"
|
product_type = "render"
|
||||||
description = "Fusion Saver to generate image sequence"
|
description = "Fusion Saver to generate image sequence"
|
||||||
|
|
||||||
default_frame_range_option = "asset_db"
|
default_frame_range_option = "asset_db"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from ayon_core.pipeline import (
|
||||||
|
|
||||||
class FusionWorkfileCreator(AutoCreator):
|
class FusionWorkfileCreator(AutoCreator):
|
||||||
identifier = "workfile"
|
identifier = "workfile"
|
||||||
family = "workfile"
|
product_type = "workfile"
|
||||||
label = "Workfile"
|
label = "Workfile"
|
||||||
icon = "fa5.file"
|
icon = "fa5.file"
|
||||||
|
|
||||||
|
|
@ -27,9 +27,12 @@ class FusionWorkfileCreator(AutoCreator):
|
||||||
if not data:
|
if not data:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
product_name = data.get("productName")
|
||||||
|
if product_name is None:
|
||||||
|
product_name = data["subset"]
|
||||||
instance = CreatedInstance(
|
instance = CreatedInstance(
|
||||||
family=self.family,
|
product_type=self.product_type,
|
||||||
subset_name=data["subset"],
|
product_name=product_name,
|
||||||
data=data,
|
data=data,
|
||||||
creator=self
|
creator=self
|
||||||
)
|
)
|
||||||
|
|
@ -59,7 +62,7 @@ class FusionWorkfileCreator(AutoCreator):
|
||||||
|
|
||||||
existing_instance = None
|
existing_instance = None
|
||||||
for instance in self.create_context.instances:
|
for instance in self.create_context.instances:
|
||||||
if instance.family == self.family:
|
if instance.product_type == self.product_type:
|
||||||
existing_instance = instance
|
existing_instance = instance
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -75,9 +78,12 @@ class FusionWorkfileCreator(AutoCreator):
|
||||||
|
|
||||||
if existing_instance is None:
|
if existing_instance is None:
|
||||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||||
subset_name = self.get_subset_name(
|
product_name = self.get_product_name(
|
||||||
self.default_variant, task_name, asset_doc,
|
project_name,
|
||||||
project_name, host_name
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
self.default_variant,
|
||||||
|
host_name,
|
||||||
)
|
)
|
||||||
data = {
|
data = {
|
||||||
"folderPath": asset_name,
|
"folderPath": asset_name,
|
||||||
|
|
@ -85,12 +91,17 @@ class FusionWorkfileCreator(AutoCreator):
|
||||||
"variant": self.default_variant,
|
"variant": self.default_variant,
|
||||||
}
|
}
|
||||||
data.update(self.get_dynamic_data(
|
data.update(self.get_dynamic_data(
|
||||||
self.default_variant, task_name, asset_doc,
|
project_name,
|
||||||
project_name, host_name, None
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
self.default_variant,
|
||||||
|
host_name,
|
||||||
|
None
|
||||||
|
|
||||||
))
|
))
|
||||||
|
|
||||||
new_instance = CreatedInstance(
|
new_instance = CreatedInstance(
|
||||||
self.family, subset_name, data, self
|
self.product_type, product_name, data, self
|
||||||
)
|
)
|
||||||
new_instance.transient_data["comp"] = comp
|
new_instance.transient_data["comp"] = comp
|
||||||
self._add_instance_to_context(new_instance)
|
self._add_instance_to_context(new_instance)
|
||||||
|
|
@ -100,10 +111,13 @@ class FusionWorkfileCreator(AutoCreator):
|
||||||
or existing_instance["task"] != task_name
|
or existing_instance["task"] != task_name
|
||||||
):
|
):
|
||||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||||
subset_name = self.get_subset_name(
|
product_name = self.get_product_name(
|
||||||
self.default_variant, task_name, asset_doc,
|
project_name,
|
||||||
project_name, host_name
|
asset_doc,
|
||||||
|
task_name,
|
||||||
|
self.default_variant,
|
||||||
|
host_name,
|
||||||
)
|
)
|
||||||
existing_instance["folderPath"] = asset_name
|
existing_instance["folderPath"] = asset_name
|
||||||
existing_instance["task"] = task_name
|
existing_instance["task"] = task_name
|
||||||
existing_instance["subset"] = subset_name
|
existing_instance["productName"] = product_name
|
||||||
|
|
|
||||||
|
|
@ -44,23 +44,24 @@ class FusionLoadAlembicMesh(load.LoaderPlugin):
|
||||||
context=context,
|
context=context,
|
||||||
loader=self.__class__.__name__)
|
loader=self.__class__.__name__)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
"""Update Alembic path"""
|
"""Update Alembic path"""
|
||||||
|
|
||||||
tool = container["_tool"]
|
tool = container["_tool"]
|
||||||
assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
|
assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
|
||||||
comp = tool.Comp()
|
comp = tool.Comp()
|
||||||
|
|
||||||
path = get_representation_path(representation)
|
repre_doc = context["representation"]
|
||||||
|
path = get_representation_path(repre_doc)
|
||||||
|
|
||||||
with comp_lock_and_undo_chunk(comp, "Update tool"):
|
with comp_lock_and_undo_chunk(comp, "Update tool"):
|
||||||
tool["Filename"] = path
|
tool["Filename"] = path
|
||||||
|
|
||||||
# Update the imprinted representation
|
# Update the imprinted representation
|
||||||
tool.SetData("avalon.representation", str(representation["_id"]))
|
tool.SetData("avalon.representation", str(repre_doc["_id"]))
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
tool = container["_tool"]
|
tool = container["_tool"]
|
||||||
|
|
|
||||||
|
|
@ -59,23 +59,24 @@ class FusionLoadFBXMesh(load.LoaderPlugin):
|
||||||
loader=self.__class__.__name__,
|
loader=self.__class__.__name__,
|
||||||
)
|
)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
"""Update path"""
|
"""Update path"""
|
||||||
|
|
||||||
tool = container["_tool"]
|
tool = container["_tool"]
|
||||||
assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
|
assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
|
||||||
comp = tool.Comp()
|
comp = tool.Comp()
|
||||||
|
|
||||||
path = get_representation_path(representation)
|
repre_doc = context["representation"]
|
||||||
|
path = get_representation_path(repre_doc)
|
||||||
|
|
||||||
with comp_lock_and_undo_chunk(comp, "Update tool"):
|
with comp_lock_and_undo_chunk(comp, "Update tool"):
|
||||||
tool["ImportFile"] = path
|
tool["ImportFile"] = path
|
||||||
|
|
||||||
# Update the imprinted representation
|
# Update the imprinted representation
|
||||||
tool.SetData("avalon.representation", str(representation["_id"]))
|
tool.SetData("avalon.representation", str(repre_doc["_id"]))
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
tool = container["_tool"]
|
tool = container["_tool"]
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ class FusionLoadSequence(load.LoaderPlugin):
|
||||||
"render",
|
"render",
|
||||||
"plate",
|
"plate",
|
||||||
"image",
|
"image",
|
||||||
"onilne",
|
"online",
|
||||||
]
|
]
|
||||||
representations = ["*"]
|
representations = ["*"]
|
||||||
extensions = set(
|
extensions = set(
|
||||||
|
|
@ -175,10 +175,10 @@ class FusionLoadSequence(load.LoaderPlugin):
|
||||||
loader=self.__class__.__name__,
|
loader=self.__class__.__name__,
|
||||||
)
|
)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
"""Update the Loader's path
|
"""Update the Loader's path
|
||||||
|
|
||||||
Fusion automatically tries to reset some variables when changing
|
Fusion automatically tries to reset some variables when changing
|
||||||
|
|
@ -224,7 +224,8 @@ class FusionLoadSequence(load.LoaderPlugin):
|
||||||
assert tool.ID == "Loader", "Must be Loader"
|
assert tool.ID == "Loader", "Must be Loader"
|
||||||
comp = tool.Comp()
|
comp = tool.Comp()
|
||||||
|
|
||||||
context = get_representation_context(representation)
|
repre_doc = context["representation"]
|
||||||
|
context = get_representation_context(repre_doc)
|
||||||
path = self.filepath_from_context(context)
|
path = self.filepath_from_context(context)
|
||||||
|
|
||||||
# Get start frame from version data
|
# Get start frame from version data
|
||||||
|
|
@ -255,7 +256,7 @@ class FusionLoadSequence(load.LoaderPlugin):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update the imprinted representation
|
# Update the imprinted representation
|
||||||
tool.SetData("avalon.representation", str(representation["_id"]))
|
tool.SetData("avalon.representation", str(repre_doc["_id"]))
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
tool = container["_tool"]
|
tool = container["_tool"]
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,8 @@ class FusionLoadUSD(load.LoaderPlugin):
|
||||||
tool_type = "uLoader"
|
tool_type = "uLoader"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def apply_settings(cls, project_settings, system_settings):
|
def apply_settings(cls, project_settings):
|
||||||
super(FusionLoadUSD, cls).apply_settings(project_settings,
|
super(FusionLoadUSD, cls).apply_settings(project_settings)
|
||||||
system_settings)
|
|
||||||
if cls.enabled:
|
if cls.enabled:
|
||||||
# Enable only in Fusion 18.5+
|
# Enable only in Fusion 18.5+
|
||||||
fusion = get_fusion_module()
|
fusion = get_fusion_module()
|
||||||
|
|
@ -61,22 +60,23 @@ class FusionLoadUSD(load.LoaderPlugin):
|
||||||
context=context,
|
context=context,
|
||||||
loader=self.__class__.__name__)
|
loader=self.__class__.__name__)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
|
|
||||||
tool = container["_tool"]
|
tool = container["_tool"]
|
||||||
assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
|
assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
|
||||||
comp = tool.Comp()
|
comp = tool.Comp()
|
||||||
|
|
||||||
path = get_representation_path(representation)
|
repre_doc = context["representation"]
|
||||||
|
path = get_representation_path(repre_doc)
|
||||||
|
|
||||||
with comp_lock_and_undo_chunk(comp, "Update tool"):
|
with comp_lock_and_undo_chunk(comp, "Update tool"):
|
||||||
tool["Filename"] = path
|
tool["Filename"] = path
|
||||||
|
|
||||||
# Update the imprinted representation
|
# Update the imprinted representation
|
||||||
tool.SetData("avalon.representation", str(representation["_id"]))
|
tool.SetData("avalon.representation", str(repre_doc["_id"]))
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
tool = container["_tool"]
|
tool = container["_tool"]
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
|
||||||
instance.data["frame_range_source"] = frame_range_source
|
instance.data["frame_range_source"] = frame_range_source
|
||||||
|
|
||||||
# get asset frame ranges to all instances
|
# get asset frame ranges to all instances
|
||||||
# render family instances `asset_db` render target
|
# render product type instances `asset_db` render target
|
||||||
start = context.data["frameStart"]
|
start = context.data["frameStart"]
|
||||||
end = context.data["frameEnd"]
|
end = context.data["frameEnd"]
|
||||||
handle_start = context.data["handleStart"]
|
handle_start = context.data["handleStart"]
|
||||||
|
|
@ -34,7 +34,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
|
||||||
start_with_handle = start - handle_start
|
start_with_handle = start - handle_start
|
||||||
end_with_handle = end + handle_end
|
end_with_handle = end + handle_end
|
||||||
|
|
||||||
# conditions for render family instances
|
# conditions for render product type instances
|
||||||
if frame_range_source == "render_range":
|
if frame_range_source == "render_range":
|
||||||
# set comp render frame ranges
|
# set comp render frame ranges
|
||||||
start = context.data["renderFrameStart"]
|
start = context.data["renderFrameStart"]
|
||||||
|
|
@ -70,11 +70,11 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
|
||||||
end_with_handle = frame
|
end_with_handle = frame
|
||||||
|
|
||||||
# Include start and end render frame in label
|
# Include start and end render frame in label
|
||||||
subset = instance.data["subset"]
|
product_name = instance.data["productName"]
|
||||||
label = (
|
label = (
|
||||||
"{subset} ({start}-{end}) [{handle_start}-{handle_end}]"
|
"{product_name} ({start}-{end}) [{handle_start}-{handle_end}]"
|
||||||
).format(
|
).format(
|
||||||
subset=subset,
|
product_name=product_name,
|
||||||
start=int(start),
|
start=int(start),
|
||||||
end=int(end),
|
end=int(end),
|
||||||
handle_start=int(handle_start),
|
handle_start=int(handle_start),
|
||||||
|
|
|
||||||
|
|
@ -49,31 +49,32 @@ class CollectFusionRender(
|
||||||
if not inst.data.get("active", True):
|
if not inst.data.get("active", True):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
family = inst.data["family"]
|
product_type = inst.data["productType"]
|
||||||
if family not in ["render", "image"]:
|
if product_type not in ["render", "image"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
task_name = context.data["task"]
|
task_name = context.data["task"]
|
||||||
tool = inst.data["transientData"]["tool"]
|
tool = inst.data["transientData"]["tool"]
|
||||||
|
|
||||||
instance_families = inst.data.get("families", [])
|
instance_families = inst.data.get("families", [])
|
||||||
subset_name = inst.data["subset"]
|
product_name = inst.data["productName"]
|
||||||
instance = FusionRenderInstance(
|
instance = FusionRenderInstance(
|
||||||
family=family,
|
|
||||||
tool=tool,
|
tool=tool,
|
||||||
workfileComp=comp,
|
workfileComp=comp,
|
||||||
|
productType=product_type,
|
||||||
|
family=product_type,
|
||||||
families=instance_families,
|
families=instance_families,
|
||||||
version=version,
|
version=version,
|
||||||
time="",
|
time="",
|
||||||
source=current_file,
|
source=current_file,
|
||||||
label=inst.data["label"],
|
label=inst.data["label"],
|
||||||
subset=subset_name,
|
productName=product_name,
|
||||||
asset=inst.data["asset"],
|
folderPath=inst.data["folderPath"],
|
||||||
task=task_name,
|
task=task_name,
|
||||||
attachTo=False,
|
attachTo=False,
|
||||||
setMembers='',
|
setMembers='',
|
||||||
publish=True,
|
publish=True,
|
||||||
name=subset_name,
|
name=product_name,
|
||||||
resolutionWidth=comp_frame_format_prefs.get("Width"),
|
resolutionWidth=comp_frame_format_prefs.get("Width"),
|
||||||
resolutionHeight=comp_frame_format_prefs.get("Height"),
|
resolutionHeight=comp_frame_format_prefs.get("Height"),
|
||||||
pixelAspect=aspect_x / aspect_y,
|
pixelAspect=aspect_x / aspect_y,
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class FusionRenderLocal(
|
||||||
self.log.info(
|
self.log.info(
|
||||||
"Rendered '{nm}' for asset '{ast}' under the task '{tsk}'".format(
|
"Rendered '{nm}' for asset '{ast}' under the task '{tsk}'".format(
|
||||||
nm=instance.data["name"],
|
nm=instance.data["name"],
|
||||||
ast=instance.data["asset"],
|
ast=instance.data["folderPath"],
|
||||||
tsk=instance.data["task"],
|
tsk=instance.data["task"],
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from ayon_core.hosts.fusion.api.action import SelectInvalidAction
|
||||||
|
|
||||||
|
|
||||||
class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
|
class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
|
||||||
"""Ensure all instances have a unique subset name"""
|
"""Ensure all instances have a unique product name"""
|
||||||
|
|
||||||
order = pyblish.api.ValidatorOrder
|
order = pyblish.api.ValidatorOrder
|
||||||
label = "Validate Unique Subsets"
|
label = "Validate Unique Subsets"
|
||||||
|
|
@ -18,25 +18,31 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_invalid(cls, context):
|
def get_invalid(cls, context):
|
||||||
|
|
||||||
# Collect instances per subset per asset
|
# Collect instances per product per folder
|
||||||
instances_per_subset_asset = defaultdict(lambda: defaultdict(list))
|
instances_per_product_folder = defaultdict(lambda: defaultdict(list))
|
||||||
for instance in context:
|
for instance in context:
|
||||||
asset = instance.data.get("asset", context.data.get("asset"))
|
folder_path = instance.data["folderPath"]
|
||||||
subset = instance.data.get("subset", context.data.get("subset"))
|
product_name = instance.data["productName"]
|
||||||
instances_per_subset_asset[asset][subset].append(instance)
|
instances_per_product_folder[folder_path][product_name].append(
|
||||||
|
instance
|
||||||
|
)
|
||||||
|
|
||||||
# Find which asset + subset combination has more than one instance
|
# Find which asset + subset combination has more than one instance
|
||||||
# Those are considered invalid because they'd integrate to the same
|
# Those are considered invalid because they'd integrate to the same
|
||||||
# destination.
|
# destination.
|
||||||
invalid = []
|
invalid = []
|
||||||
for asset, instances_per_subset in instances_per_subset_asset.items():
|
for folder_path, instances_per_product in (
|
||||||
for subset, instances in instances_per_subset.items():
|
instances_per_product_folder.items()
|
||||||
|
):
|
||||||
|
for product_name, instances in instances_per_product.items():
|
||||||
if len(instances) > 1:
|
if len(instances) > 1:
|
||||||
cls.log.warning(
|
cls.log.warning(
|
||||||
"{asset} > {subset} used by more than "
|
(
|
||||||
"one instance: {instances}".format(
|
"{folder_path} > {product_name} used by more than "
|
||||||
asset=asset,
|
"one instance: {instances}"
|
||||||
subset=subset,
|
).format(
|
||||||
|
folder_path=folder_path,
|
||||||
|
product_name=product_name,
|
||||||
instances=instances
|
instances=instances
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
@ -50,6 +56,7 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
|
||||||
def process(self, context):
|
def process(self, context):
|
||||||
invalid = self.get_invalid(context)
|
invalid = self.get_invalid(context)
|
||||||
if invalid:
|
if invalid:
|
||||||
raise PublishValidationError("Multiple instances are set to "
|
raise PublishValidationError(
|
||||||
"the same asset > subset.",
|
"Multiple instances are set to the same folder > product.",
|
||||||
title=self.label)
|
title=self.label
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ class CreateComposite(harmony.Creator):
|
||||||
|
|
||||||
name = "compositeDefault"
|
name = "compositeDefault"
|
||||||
label = "Composite"
|
label = "Composite"
|
||||||
family = "mindbender.template"
|
product_type = "mindbender.template"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(CreateComposite, self).__init__(*args, **kwargs)
|
super(CreateComposite, self).__init__(*args, **kwargs)
|
||||||
|
|
@ -212,6 +212,7 @@ class CreateComposite(harmony.Creator):
|
||||||
|
|
||||||
The creator plugin can be configured to use other node types. For example here is a write node creator:
|
The creator plugin can be configured to use other node types. For example here is a write node creator:
|
||||||
```python
|
```python
|
||||||
|
from uuid import uuid4
|
||||||
import ayon_core.hosts.harmony.api as harmony
|
import ayon_core.hosts.harmony.api as harmony
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -220,7 +221,7 @@ class CreateRender(harmony.Creator):
|
||||||
|
|
||||||
name = "writeDefault"
|
name = "writeDefault"
|
||||||
label = "Write"
|
label = "Write"
|
||||||
family = "mindbender.imagesequence"
|
product_type = "mindbender.imagesequence"
|
||||||
node_type = "WRITE"
|
node_type = "WRITE"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
@ -242,6 +243,7 @@ class CreateRender(harmony.Creator):
|
||||||
#### Collector Plugin
|
#### Collector Plugin
|
||||||
```python
|
```python
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
from ayon_core.pipeline import AYON_INSTANCE_ID, AVALON_INSTANCE_ID
|
||||||
import ayon_core.hosts.harmony.api as harmony
|
import ayon_core.hosts.harmony.api as harmony
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -252,7 +254,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
||||||
a composite node and marked with a unique identifier;
|
a composite node and marked with a unique identifier;
|
||||||
|
|
||||||
Identifier:
|
Identifier:
|
||||||
id (str): "pyblish.avalon.instance"
|
id (str): "ayon.create.instance"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
label = "Instances"
|
label = "Instances"
|
||||||
|
|
@ -272,7 +274,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip containers.
|
# Skip containers.
|
||||||
if "container" in data["id"]:
|
if data["id"] not in {AYON_INSTANCE_ID, AVALON_INSTANCE_ID}:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
instance = context.create_instance(node.split("/")[-1])
|
instance = context.create_instance(node.split("/")[-1])
|
||||||
|
|
@ -287,6 +289,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
||||||
#### Extractor Plugin
|
#### Extractor Plugin
|
||||||
```python
|
```python
|
||||||
import os
|
import os
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
import ayon_core.hosts.harmony.api as harmony
|
import ayon_core.hosts.harmony.api as harmony
|
||||||
|
|
@ -418,6 +421,7 @@ class ExtractImage(pyblish.api.InstancePlugin):
|
||||||
#### Loader Plugin
|
#### Loader Plugin
|
||||||
```python
|
```python
|
||||||
import os
|
import os
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
import ayon_core.hosts.harmony.api as harmony
|
import ayon_core.hosts.harmony.api as harmony
|
||||||
|
|
||||||
|
|
@ -607,11 +611,12 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
self.__class__.__name__
|
self.__class__.__name__
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
node = container.pop("node")
|
node = container.pop("node")
|
||||||
|
|
||||||
|
repre_doc = context["representation"]
|
||||||
project_name = get_current_project_name()
|
project_name = get_current_project_name()
|
||||||
version = get_version_by_id(project_name, representation["parent"])
|
version = get_version_by_id(project_name, repre_doc["parent"])
|
||||||
files = []
|
files = []
|
||||||
for f in version["data"]["files"]:
|
for f in version["data"]["files"]:
|
||||||
files.append(
|
files.append(
|
||||||
|
|
@ -628,7 +633,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
)
|
)
|
||||||
|
|
||||||
harmony.imprint(
|
harmony.imprint(
|
||||||
node, {"representation": str(representation["_id"])}
|
node, {"representation": str(repre_doc["_id"])}
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
|
|
@ -644,8 +649,8 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
{"function": func, "args": [node]}
|
{"function": func, "args": [node]}
|
||||||
)
|
)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ ImageSequenceLoader.getUniqueColumnName = function(columnPrefix) {
|
||||||
* var args = [
|
* var args = [
|
||||||
* files, // Files in file sequences.
|
* files, // Files in file sequences.
|
||||||
* asset, // Asset name.
|
* asset, // Asset name.
|
||||||
* subset, // Subset name.
|
* productName, // Product name.
|
||||||
* startFrame, // Sequence starting frame.
|
* startFrame, // Sequence starting frame.
|
||||||
* groupId // Unique group ID (uuid4).
|
* groupId // Unique group ID (uuid4).
|
||||||
* ];
|
* ];
|
||||||
|
|
@ -106,7 +106,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) {
|
||||||
var doc = $.scn;
|
var doc = $.scn;
|
||||||
var files = args[0];
|
var files = args[0];
|
||||||
var asset = args[1];
|
var asset = args[1];
|
||||||
var subset = args[2];
|
var productName = args[2];
|
||||||
var startFrame = args[3];
|
var startFrame = args[3];
|
||||||
var groupId = args[4];
|
var groupId = args[4];
|
||||||
var vectorFormat = null;
|
var vectorFormat = null;
|
||||||
|
|
@ -124,7 +124,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) {
|
||||||
var num = 0;
|
var num = 0;
|
||||||
var name = '';
|
var name = '';
|
||||||
do {
|
do {
|
||||||
name = asset + '_' + (num++) + '_' + subset;
|
name = asset + '_' + (num++) + '_' + productName;
|
||||||
} while (currentGroup.getNodeByName(name) != null);
|
} while (currentGroup.getNodeByName(name) != null);
|
||||||
|
|
||||||
extension = filename.substr(pos+1).toLowerCase();
|
extension = filename.substr(pos+1).toLowerCase();
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ var TemplateLoader = function() {};
|
||||||
* var args = [
|
* var args = [
|
||||||
* templatePath, // Path to tpl file.
|
* templatePath, // Path to tpl file.
|
||||||
* assetName, // Asset name.
|
* assetName, // Asset name.
|
||||||
* subsetName, // Subset name.
|
* productName, // Product name.
|
||||||
* groupId // unique ID (uuid4)
|
* groupId // unique ID (uuid4)
|
||||||
* ];
|
* ];
|
||||||
*/
|
*/
|
||||||
|
|
@ -39,7 +39,7 @@ TemplateLoader.prototype.loadContainer = function(args) {
|
||||||
var doc = $.scn;
|
var doc = $.scn;
|
||||||
var templatePath = args[0];
|
var templatePath = args[0];
|
||||||
var assetName = args[1];
|
var assetName = args[1];
|
||||||
var subset = args[2];
|
var productName = args[2];
|
||||||
var groupId = args[3];
|
var groupId = args[3];
|
||||||
|
|
||||||
// Get the current group
|
// Get the current group
|
||||||
|
|
@ -62,7 +62,7 @@ TemplateLoader.prototype.loadContainer = function(args) {
|
||||||
var num = 0;
|
var num = 0;
|
||||||
var containerGroupName = '';
|
var containerGroupName = '';
|
||||||
do {
|
do {
|
||||||
containerGroupName = assetName + '_' + (num++) + '_' + subset;
|
containerGroupName = assetName + '_' + (num++) + '_' + productName;
|
||||||
} while (currentGroup.getNodeByName(containerGroupName) != null);
|
} while (currentGroup.getNodeByName(containerGroupName) != null);
|
||||||
|
|
||||||
// import the template
|
// import the template
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ class CreateFarmRender(plugin.Creator):
|
||||||
|
|
||||||
name = "renderDefault"
|
name = "renderDefault"
|
||||||
label = "Render on Farm"
|
label = "Render on Farm"
|
||||||
family = "renderFarm"
|
product_type = "renderFarm"
|
||||||
node_type = "WRITE"
|
node_type = "WRITE"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ class CreateRender(plugin.Creator):
|
||||||
|
|
||||||
name = "renderDefault"
|
name = "renderDefault"
|
||||||
label = "Render"
|
label = "Render"
|
||||||
family = "render"
|
product_type = "render"
|
||||||
node_type = "WRITE"
|
node_type = "WRITE"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ class CreateTemplate(plugin.Creator):
|
||||||
|
|
||||||
name = "templateDefault"
|
name = "templateDefault"
|
||||||
label = "Template"
|
label = "Template"
|
||||||
family = "harmony.template"
|
product_type = "harmony.template"
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(CreateTemplate, self).__init__(*args, **kwargs)
|
super(CreateTemplate, self).__init__(*args, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -45,17 +45,17 @@ class ImportAudioLoader(load.LoaderPlugin):
|
||||||
{"function": func, "args": [context["subset"]["name"], wav_file]}
|
{"function": func, "args": [context["subset"]["name"], wav_file]}
|
||||||
)
|
)
|
||||||
|
|
||||||
subset_name = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
return harmony.containerise(
|
return harmony.containerise(
|
||||||
subset_name,
|
product_name,
|
||||||
namespace,
|
namespace,
|
||||||
subset_name,
|
product_name,
|
||||||
context,
|
context,
|
||||||
self.__class__.__name__
|
self.__class__.__name__
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,7 @@ class BackgroundLoader(load.LoaderPlugin):
|
||||||
|
|
||||||
bg_folder = os.path.dirname(path)
|
bg_folder = os.path.dirname(path)
|
||||||
|
|
||||||
subset_name = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
# read_node_name += "_{}".format(uuid.uuid4())
|
# read_node_name += "_{}".format(uuid.uuid4())
|
||||||
container_nodes = []
|
container_nodes = []
|
||||||
|
|
||||||
|
|
@ -272,16 +272,17 @@ class BackgroundLoader(load.LoaderPlugin):
|
||||||
container_nodes.append(read_node)
|
container_nodes.append(read_node)
|
||||||
|
|
||||||
return harmony.containerise(
|
return harmony.containerise(
|
||||||
subset_name,
|
product_name,
|
||||||
namespace,
|
namespace,
|
||||||
subset_name,
|
product_name,
|
||||||
context,
|
context,
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
nodes=container_nodes
|
nodes=container_nodes
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
path = get_representation_path(representation)
|
repre_doc = context["representation"]
|
||||||
|
path = get_representation_path(repre_doc)
|
||||||
with open(path) as json_file:
|
with open(path) as json_file:
|
||||||
data = json.load(json_file)
|
data = json.load(json_file)
|
||||||
|
|
||||||
|
|
@ -301,7 +302,7 @@ class BackgroundLoader(load.LoaderPlugin):
|
||||||
|
|
||||||
print(container)
|
print(container)
|
||||||
|
|
||||||
is_latest = is_representation_from_latest(representation)
|
is_latest = is_representation_from_latest(repre_doc)
|
||||||
for layer in sorted(layers):
|
for layer in sorted(layers):
|
||||||
file_to_import = [
|
file_to_import = [
|
||||||
os.path.join(bg_folder, layer).replace("\\", "/")
|
os.path.join(bg_folder, layer).replace("\\", "/")
|
||||||
|
|
@ -351,8 +352,11 @@ class BackgroundLoader(load.LoaderPlugin):
|
||||||
harmony.send({"function": func, "args": [node, "red"]})
|
harmony.send({"function": func, "args": [node, "red"]})
|
||||||
|
|
||||||
harmony.imprint(
|
harmony.imprint(
|
||||||
container['name'], {"representation": str(representation["_id"]),
|
container['name'],
|
||||||
"nodes": container['nodes']}
|
{
|
||||||
|
"representation": str(repre_doc["_id"]),
|
||||||
|
"nodes": container["nodes"]
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
|
|
@ -369,5 +373,5 @@ class BackgroundLoader(load.LoaderPlugin):
|
||||||
)
|
)
|
||||||
harmony.imprint(container['name'], {}, remove=True)
|
harmony.imprint(container['name'], {}, remove=True)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
files.append(fname.parent.joinpath(remainder[0]).as_posix())
|
files.append(fname.parent.joinpath(remainder[0]).as_posix())
|
||||||
|
|
||||||
asset = context["asset"]["name"]
|
asset = context["asset"]["name"]
|
||||||
subset = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
group_id = str(uuid.uuid4())
|
group_id = str(uuid.uuid4())
|
||||||
read_node = harmony.send(
|
read_node = harmony.send(
|
||||||
|
|
@ -56,7 +56,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
"args": [
|
"args": [
|
||||||
files,
|
files,
|
||||||
asset,
|
asset,
|
||||||
subset,
|
product_name,
|
||||||
1,
|
1,
|
||||||
group_id
|
group_id
|
||||||
]
|
]
|
||||||
|
|
@ -64,7 +64,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
)["result"]
|
)["result"]
|
||||||
|
|
||||||
return harmony.containerise(
|
return harmony.containerise(
|
||||||
f"{asset}_{subset}",
|
f"{asset}_{product_name}",
|
||||||
namespace,
|
namespace,
|
||||||
read_node,
|
read_node,
|
||||||
context,
|
context,
|
||||||
|
|
@ -72,18 +72,19 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
nodes=[read_node]
|
nodes=[read_node]
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
"""Update loaded containers.
|
"""Update loaded containers.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
container (dict): Container data.
|
container (dict): Container data.
|
||||||
representation (dict): Representation data.
|
context (dict): Representation context data.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self_name = self.__class__.__name__
|
self_name = self.__class__.__name__
|
||||||
node = container.get("nodes").pop()
|
node = container.get("nodes").pop()
|
||||||
|
|
||||||
path = get_representation_path(representation)
|
repre_doc = context["representation"]
|
||||||
|
path = get_representation_path(repre_doc)
|
||||||
collections, remainder = clique.assemble(
|
collections, remainder = clique.assemble(
|
||||||
os.listdir(os.path.dirname(path))
|
os.listdir(os.path.dirname(path))
|
||||||
)
|
)
|
||||||
|
|
@ -110,7 +111,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
)
|
)
|
||||||
|
|
||||||
# Colour node.
|
# Colour node.
|
||||||
if is_representation_from_latest(representation):
|
if is_representation_from_latest(repre_doc):
|
||||||
harmony.send(
|
harmony.send(
|
||||||
{
|
{
|
||||||
"function": "PypeHarmony.setColor",
|
"function": "PypeHarmony.setColor",
|
||||||
|
|
@ -124,7 +125,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
})
|
})
|
||||||
|
|
||||||
harmony.imprint(
|
harmony.imprint(
|
||||||
node, {"representation": str(representation["_id"])}
|
node, {"representation": str(repre_doc["_id"])}
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
|
|
@ -140,6 +141,6 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
||||||
)
|
)
|
||||||
harmony.imprint(node, {}, remove=True)
|
harmony.imprint(node, {}, remove=True)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
"""Switch loaded representations."""
|
"""Switch loaded representations."""
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
|
||||||
|
|
@ -26,15 +26,17 @@ class ImportPaletteLoader(load.LoaderPlugin):
|
||||||
self.__class__.__name__
|
self.__class__.__name__
|
||||||
)
|
)
|
||||||
|
|
||||||
def load_palette(self, representation):
|
def load_palette(self, context):
|
||||||
subset_name = representation["context"]["subset"]
|
subset_doc = context["subset"]
|
||||||
name = subset_name.replace("palette", "")
|
repre_doc = context["representation"]
|
||||||
|
product_name = subset_doc["name"]
|
||||||
|
name = product_name.replace("palette", "")
|
||||||
|
|
||||||
# Overwrite palette on disk.
|
# Overwrite palette on disk.
|
||||||
scene_path = harmony.send(
|
scene_path = harmony.send(
|
||||||
{"function": "scene.currentProjectPath"}
|
{"function": "scene.currentProjectPath"}
|
||||||
)["result"]
|
)["result"]
|
||||||
src = get_representation_path(representation)
|
src = get_representation_path(repre_doc)
|
||||||
dst = os.path.join(
|
dst = os.path.join(
|
||||||
scene_path,
|
scene_path,
|
||||||
"palette-library",
|
"palette-library",
|
||||||
|
|
@ -44,7 +46,7 @@ class ImportPaletteLoader(load.LoaderPlugin):
|
||||||
|
|
||||||
harmony.save_scene()
|
harmony.save_scene()
|
||||||
|
|
||||||
msg = "Updated {}.".format(subset_name)
|
msg = "Updated {}.".format(product_name)
|
||||||
msg += " You need to reload the scene to see the changes.\n"
|
msg += " You need to reload the scene to see the changes.\n"
|
||||||
msg += "Please save workfile when ready and use Workfiles "
|
msg += "Please save workfile when ready and use Workfiles "
|
||||||
msg += "to reopen it."
|
msg += "to reopen it."
|
||||||
|
|
@ -59,13 +61,14 @@ class ImportPaletteLoader(load.LoaderPlugin):
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
harmony.remove(container["name"])
|
harmony.remove(container["name"])
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
self.remove(container)
|
self.remove(container)
|
||||||
name = self.load_palette(representation)
|
name = self.load_palette(context)
|
||||||
|
|
||||||
container["representation"] = str(representation["_id"])
|
repre_doc = context["representation"]
|
||||||
|
container["representation"] = str(repre_doc["_id"])
|
||||||
container["name"] = name
|
container["name"] = name
|
||||||
harmony.imprint(name, container)
|
harmony.imprint(name, container)
|
||||||
|
|
|
||||||
|
|
@ -70,19 +70,20 @@ class TemplateLoader(load.LoaderPlugin):
|
||||||
self_name
|
self_name
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
"""Update loaded containers.
|
"""Update loaded containers.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
container (dict): Container data.
|
container (dict): Container data.
|
||||||
representation (dict): Representation data.
|
context (dict): Representation context data.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
node_name = container["name"]
|
node_name = container["name"]
|
||||||
node = harmony.find_node_by_name(node_name, "GROUP")
|
node = harmony.find_node_by_name(node_name, "GROUP")
|
||||||
self_name = self.__class__.__name__
|
self_name = self.__class__.__name__
|
||||||
|
|
||||||
if is_representation_from_latest(representation):
|
repre_doc = context["representation"]
|
||||||
|
if is_representation_from_latest(repre_doc):
|
||||||
self._set_green(node)
|
self._set_green(node)
|
||||||
else:
|
else:
|
||||||
self._set_red(node)
|
self._set_red(node)
|
||||||
|
|
@ -110,7 +111,7 @@ class TemplateLoader(load.LoaderPlugin):
|
||||||
None, container["data"])
|
None, container["data"])
|
||||||
|
|
||||||
harmony.imprint(
|
harmony.imprint(
|
||||||
node, {"representation": str(representation["_id"])}
|
node, {"representation": str(repre_doc["_id"])}
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
|
|
@ -125,9 +126,9 @@ class TemplateLoader(load.LoaderPlugin):
|
||||||
{"function": "PypeHarmony.deleteNode", "args": [node]}
|
{"function": "PypeHarmony.deleteNode", "args": [node]}
|
||||||
)
|
)
|
||||||
|
|
||||||
def switch(self, container, representation):
|
def switch(self, container, context):
|
||||||
"""Switch representation containers."""
|
"""Switch representation containers."""
|
||||||
self.update(container, representation)
|
self.update(container, context)
|
||||||
|
|
||||||
def _set_green(self, node):
|
def _set_green(self, node):
|
||||||
"""Set node color to green `rgba(0, 255, 0, 255)`."""
|
"""Set node color to green `rgba(0, 255, 0, 255)`."""
|
||||||
|
|
|
||||||
|
|
@ -40,17 +40,17 @@ class ImportTemplateLoader(load.LoaderPlugin):
|
||||||
|
|
||||||
shutil.rmtree(temp_dir)
|
shutil.rmtree(temp_dir)
|
||||||
|
|
||||||
subset_name = context["subset"]["name"]
|
product_name = context["subset"]["name"]
|
||||||
|
|
||||||
return harmony.containerise(
|
return harmony.containerise(
|
||||||
subset_name,
|
product_name,
|
||||||
namespace,
|
namespace,
|
||||||
subset_name,
|
product_name,
|
||||||
context,
|
context,
|
||||||
self.__class__.__name__
|
self.__class__.__name__
|
||||||
)
|
)
|
||||||
|
|
||||||
def update(self, container, representation):
|
def update(self, container, context):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def remove(self, container):
|
def remove(self, container):
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
||||||
for frame in range(start, end + 1):
|
for frame in range(start, end + 1):
|
||||||
expected_files.append(
|
expected_files.append(
|
||||||
path / "{}-{}.{}".format(
|
path / "{}-{}.{}".format(
|
||||||
render_instance.subset,
|
render_instance.productName,
|
||||||
str(frame).rjust(int(info[2]) + 1, "0"),
|
str(frame).rjust(int(info[2]) + 1, "0"),
|
||||||
ext
|
ext
|
||||||
)
|
)
|
||||||
|
|
@ -89,7 +89,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
||||||
return expected_files
|
return expected_files
|
||||||
|
|
||||||
def get_instances(self, context):
|
def get_instances(self, context):
|
||||||
"""Get instances per Write node in `renderFarm` family."""
|
"""Get instances per Write node in `renderFarm` product type."""
|
||||||
version = None
|
version = None
|
||||||
if self.sync_workfile_version:
|
if self.sync_workfile_version:
|
||||||
version = context.data["version"]
|
version = context.data["version"]
|
||||||
|
|
@ -98,7 +98,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
||||||
|
|
||||||
self_name = self.__class__.__name__
|
self_name = self.__class__.__name__
|
||||||
|
|
||||||
asset_name = context.data["asset"]
|
folder_path = context.data["folderPath"]
|
||||||
|
|
||||||
for node in context.data["allNodes"]:
|
for node in context.data["allNodes"]:
|
||||||
data = harmony.read(node)
|
data = harmony.read(node)
|
||||||
|
|
@ -111,7 +111,10 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
||||||
if "container" in data["id"]:
|
if "container" in data["id"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if data["family"] != "renderFarm":
|
product_type = data.get("productType")
|
||||||
|
if product_type is None:
|
||||||
|
product_type = data.get("family")
|
||||||
|
if product_type != "renderFarm":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 0 - filename / 1 - type / 2 - zeros / 3 - start / 4 - enabled
|
# 0 - filename / 1 - type / 2 - zeros / 3 - start / 4 - enabled
|
||||||
|
|
@ -124,15 +127,14 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
||||||
|
|
||||||
# TODO: handle pixel aspect and frame step
|
# TODO: handle pixel aspect and frame step
|
||||||
# TODO: set Deadline stuff (pools, priority, etc. by presets)
|
# TODO: set Deadline stuff (pools, priority, etc. by presets)
|
||||||
# because of using 'renderFarm' as a family, replace 'Farm' with
|
# because of using 'renderFarm' as a product type, replace 'Farm'
|
||||||
# capitalized task name - issue of avalon-core Creator app
|
# with capitalized task name - issue of Creator tool
|
||||||
subset_name = node.split("/")[1]
|
product_name = node.split("/")[1]
|
||||||
task_name = context.data["anatomyData"]["task"][
|
task_name = context.data["task"].capitalize()
|
||||||
"name"].capitalize()
|
|
||||||
replace_str = ""
|
replace_str = ""
|
||||||
if task_name.lower() not in subset_name.lower():
|
if task_name.lower() not in product_name.lower():
|
||||||
replace_str = task_name
|
replace_str = task_name
|
||||||
subset_name = subset_name.replace(
|
product_name = product_name.replace(
|
||||||
'Farm',
|
'Farm',
|
||||||
replace_str)
|
replace_str)
|
||||||
|
|
||||||
|
|
@ -141,8 +143,8 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
||||||
time=get_formatted_current_time(),
|
time=get_formatted_current_time(),
|
||||||
source=context.data["currentFile"],
|
source=context.data["currentFile"],
|
||||||
label=node.split("/")[1],
|
label=node.split("/")[1],
|
||||||
subset=subset_name,
|
productName=product_name,
|
||||||
asset=asset_name,
|
folderPath=folder_path,
|
||||||
task=task_name,
|
task=task_name,
|
||||||
attachTo=False,
|
attachTo=False,
|
||||||
setMembers=[node],
|
setMembers=[node],
|
||||||
|
|
@ -151,6 +153,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
||||||
priority=50,
|
priority=50,
|
||||||
name=node.split("/")[1],
|
name=node.split("/")[1],
|
||||||
|
|
||||||
|
productType="render.farm",
|
||||||
family="render.farm",
|
family="render.farm",
|
||||||
families=["render.farm"],
|
families=["render.farm"],
|
||||||
farm=True,
|
farm=True,
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
||||||
a composite node and marked with a unique identifier.
|
a composite node and marked with a unique identifier.
|
||||||
|
|
||||||
Identifier:
|
Identifier:
|
||||||
id (str): "pyblish.avalon.instance"
|
id (str): "ayon.create.instance"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
label = "Instances"
|
label = "Instances"
|
||||||
order = pyblish.api.CollectorOrder
|
order = pyblish.api.CollectorOrder
|
||||||
hosts = ["harmony"]
|
hosts = ["harmony"]
|
||||||
families_mapping = {
|
product_type_mapping = {
|
||||||
"render": ["review", "ftrack"],
|
"render": ["review", "ftrack"],
|
||||||
"harmony.template": [],
|
"harmony.template": [],
|
||||||
"palette": ["palette", "ftrack"]
|
"palette": ["palette", "ftrack"]
|
||||||
|
|
@ -49,8 +49,14 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
||||||
if "container" in data["id"]:
|
if "container" in data["id"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# skip render farm family as it is collected separately
|
product_type = data.get("productType")
|
||||||
if data["family"] == "renderFarm":
|
if product_type is None:
|
||||||
|
product_type = data["family"]
|
||||||
|
data["productType"] = product_type
|
||||||
|
data["family"] = product_type
|
||||||
|
|
||||||
|
# skip render farm product type as it is collected separately
|
||||||
|
if product_type == "renderFarm":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
instance = context.create_instance(node.split("/")[-1])
|
instance = context.create_instance(node.split("/")[-1])
|
||||||
|
|
@ -59,11 +65,14 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
||||||
instance.data["publish"] = harmony.send(
|
instance.data["publish"] = harmony.send(
|
||||||
{"function": "node.getEnable", "args": [node]}
|
{"function": "node.getEnable", "args": [node]}
|
||||||
)["result"]
|
)["result"]
|
||||||
instance.data["families"] = self.families_mapping[data["family"]]
|
|
||||||
|
families = [product_type]
|
||||||
|
families.extend(self.product_type_mapping[product_type])
|
||||||
|
instance.data["families"] = families
|
||||||
|
|
||||||
# If set in plugin, pair the scene Version in ftrack with
|
# If set in plugin, pair the scene Version in ftrack with
|
||||||
# thumbnails and review media.
|
# thumbnails and review media.
|
||||||
if (self.pair_media and instance.data["family"] == "scene"):
|
if (self.pair_media and product_type == "scene"):
|
||||||
context.data["scene_instance"] = instance
|
context.data["scene_instance"] = instance
|
||||||
|
|
||||||
# Produce diagnostic message for any graphical
|
# Produce diagnostic message for any graphical
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue