mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge pull request #113 from ynput/enhancement/OP-8264_Use-productName-n-productType
Chore: Product name and type
This commit is contained in:
commit
2a0f68aa01
419 changed files with 4342 additions and 4544 deletions
|
|
@ -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.lib import set_settings
|
||||
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):
|
||||
|
|
@ -22,7 +22,7 @@ class RenderCreator(Creator):
|
|||
"""
|
||||
identifier = "render"
|
||||
label = "Render"
|
||||
family = "render"
|
||||
product_type = "render"
|
||||
description = "Render creator"
|
||||
|
||||
create_allow_context_change = True
|
||||
|
|
@ -31,7 +31,7 @@ class RenderCreator(Creator):
|
|||
mark_for_review = 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
|
||||
|
||||
try:
|
||||
|
|
@ -58,33 +58,37 @@ class RenderCreator(Creator):
|
|||
len(comps) > 1)
|
||||
for comp in comps:
|
||||
composition_name = re.sub(
|
||||
"[^{}]+".format(SUBSET_NAME_ALLOWED_SYMBOLS),
|
||||
"[^{}]+".format(PRODUCT_NAME_ALLOWED_SYMBOLS),
|
||||
"",
|
||||
comp.name
|
||||
)
|
||||
if use_composition_name:
|
||||
if "{composition}" not in subset_name_from_ui.lower():
|
||||
subset_name_from_ui += "{Composition}"
|
||||
if "{composition}" not in product_name.lower():
|
||||
product_name += "{Composition}"
|
||||
|
||||
dynamic_fill = prepare_template_data({"composition":
|
||||
composition_name})
|
||||
subset_name = subset_name_from_ui.format(**dynamic_fill)
|
||||
comp_product_name = product_name.format(**dynamic_fill)
|
||||
data["composition_name"] = composition_name
|
||||
else:
|
||||
subset_name = subset_name_from_ui
|
||||
subset_name = re.sub(r"\{composition\}", '', subset_name,
|
||||
flags=re.IGNORECASE)
|
||||
comp_product_name = re.sub(
|
||||
r"\{composition\}",
|
||||
"",
|
||||
product_name,
|
||||
flags=re.IGNORECASE
|
||||
)
|
||||
|
||||
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(
|
||||
inst.subset_name))
|
||||
inst.product_name))
|
||||
|
||||
data["members"] = [comp.id]
|
||||
data["orig_comp_name"] = composition_name
|
||||
|
||||
new_instance = CreatedInstance(self.family, subset_name, data,
|
||||
self)
|
||||
new_instance = CreatedInstance(
|
||||
self.product_type, comp_product_name, data, self
|
||||
)
|
||||
if "farm" in pre_create_data:
|
||||
use_farm = pre_create_data["farm"]
|
||||
new_instance.creator_attributes["farm"] = use_farm
|
||||
|
|
@ -96,7 +100,7 @@ class RenderCreator(Creator):
|
|||
new_instance.data_to_store())
|
||||
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:
|
||||
set_settings(True, True, [comp.id], print_msg=False)
|
||||
|
||||
|
|
@ -107,7 +111,7 @@ class RenderCreator(Creator):
|
|||
"selected by default.",
|
||||
default=True, label="Use selection"),
|
||||
BoolDef("use_composition_name",
|
||||
label="Use composition name in subset"),
|
||||
label="Use composition name in product"),
|
||||
UISeparatorDef(),
|
||||
BoolDef("farm", label="Render on farm"),
|
||||
BoolDef(
|
||||
|
|
@ -133,9 +137,14 @@ class RenderCreator(Creator):
|
|||
|
||||
def collect_instances(self):
|
||||
for instance_data in cache_and_get_instances(self):
|
||||
# legacy instances have family=='render' or 'renderLocal', use them
|
||||
creator_id = (instance_data.get("creator_identifier") or
|
||||
instance_data.get("family", '').replace("Local", ''))
|
||||
# legacy instances have product_type=='render' or 'renderLocal', use them
|
||||
creator_id = instance_data.get("creator_identifier")
|
||||
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:
|
||||
instance_data = self._handle_legacy(instance_data)
|
||||
instance = CreatedInstance.from_existing(
|
||||
|
|
@ -147,10 +156,10 @@ class RenderCreator(Creator):
|
|||
for created_inst, _changes in update_list:
|
||||
api.get_stub().imprint(created_inst.get("instance_id"),
|
||||
created_inst.data_to_store())
|
||||
subset_change = _changes.get("subset")
|
||||
if subset_change:
|
||||
name_change = _changes.get("productName")
|
||||
if name_change:
|
||||
api.get_stub().rename_item(created_inst.data["members"][0],
|
||||
subset_change.new_value)
|
||||
name_change.new_value)
|
||||
|
||||
def remove_instances(self, instances):
|
||||
"""Removes metadata and renames to original comp name if available."""
|
||||
|
|
@ -183,15 +192,15 @@ class RenderCreator(Creator):
|
|||
def get_detail_description(self):
|
||||
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
|
||||
be a final delivery product or loaded and used in another DCCs.
|
||||
|
||||
Select single composition and create instance of 'render' family or
|
||||
turn off 'Use selection' to create instance for all compositions.
|
||||
Select single composition and create instance of 'render' product type
|
||||
or turn off 'Use selection' to create instance for all compositions.
|
||||
|
||||
'Use composition name in subset' allows to explicitly add composition
|
||||
name into created subset name.
|
||||
'Use composition name in product' allows to explicitly add composition
|
||||
name into created product name.
|
||||
|
||||
Position of composition name could be set in
|
||||
`project_settings/global/tools/creator/product_name_profiles` with
|
||||
|
|
@ -201,15 +210,16 @@ class RenderCreator(Creator):
|
|||
be handled at same time.
|
||||
|
||||
If {composition} placeholder is not us 'product_name_profiles'
|
||||
composition name will be capitalized and set at the end of subset name
|
||||
if necessary.
|
||||
composition name will be capitalized and set at the end of
|
||||
product name if necessary.
|
||||
|
||||
If composition name should be used, it will be cleaned up of characters
|
||||
that would cause an issue in published file names.
|
||||
"""
|
||||
|
||||
def get_dynamic_data(self, variant, task_name, asset_doc,
|
||||
project_name, host_name, instance):
|
||||
def get_dynamic_data(
|
||||
self, project_name, asset_doc, task_name, variant, host_name, instance
|
||||
):
|
||||
dynamic_data = {}
|
||||
if instance is not None:
|
||||
composition_name = instance.get("composition_name")
|
||||
|
|
@ -234,9 +244,9 @@ class RenderCreator(Creator):
|
|||
instance_data["task"] = self.create_context.get_current_task_name()
|
||||
|
||||
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["family"] = self.family
|
||||
instance_data["productType"] = self.product_type
|
||||
|
||||
if instance_data["creator_attributes"].get("mark_for_review") is None:
|
||||
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):
|
||||
identifier = "workfile"
|
||||
family = "workfile"
|
||||
product_type = "workfile"
|
||||
|
||||
default_variant = "Main"
|
||||
|
||||
|
|
@ -20,9 +20,9 @@ class AEWorkfileCreator(AutoCreator):
|
|||
for instance_data in cache_and_get_instances(self):
|
||||
creator_id = instance_data.get("creator_identifier")
|
||||
if creator_id == self.identifier:
|
||||
subset_name = instance_data["subset"]
|
||||
product_name = instance_data["productName"]
|
||||
instance = CreatedInstance(
|
||||
self.family, subset_name, instance_data, self
|
||||
self.product_type, product_name, instance_data, self
|
||||
)
|
||||
self._add_instance_to_context(instance)
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ class AEWorkfileCreator(AutoCreator):
|
|||
def create(self, options=None):
|
||||
existing_instance = None
|
||||
for instance in self.create_context.instances:
|
||||
if instance.family == self.family:
|
||||
if instance.product_type == self.product_type:
|
||||
existing_instance = instance
|
||||
break
|
||||
|
||||
|
|
@ -49,9 +49,12 @@ class AEWorkfileCreator(AutoCreator):
|
|||
|
||||
if existing_instance is None:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name
|
||||
product_name = self.get_product_name(
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
self.default_variant,
|
||||
host_name,
|
||||
)
|
||||
data = {
|
||||
"folderPath": asset_name,
|
||||
|
|
@ -59,12 +62,16 @@ class AEWorkfileCreator(AutoCreator):
|
|||
"variant": self.default_variant,
|
||||
}
|
||||
data.update(self.get_dynamic_data(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name, None
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
self.default_variant,
|
||||
host_name,
|
||||
None,
|
||||
))
|
||||
|
||||
new_instance = CreatedInstance(
|
||||
self.family, subset_name, data, self
|
||||
self.product_type, product_name, data, self
|
||||
)
|
||||
self._add_instance_to_context(new_instance)
|
||||
|
||||
|
|
@ -76,10 +83,13 @@ class AEWorkfileCreator(AutoCreator):
|
|||
or existing_instance["task"] != task_name
|
||||
):
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name
|
||||
product_name = self.get_product_name(
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
self.default_variant,
|
||||
host_name,
|
||||
)
|
||||
existing_instance["folderPath"] = asset_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):
|
||||
"""
|
||||
Load images from Background family
|
||||
Load images from Background product type
|
||||
Creates for each background separate folder with all imported images
|
||||
from background json AND automatically created composition with layers,
|
||||
each layer for separate image.
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ class CollectAERender(publish.AbstractCollectRender):
|
|||
if not inst.data.get("active", True):
|
||||
continue
|
||||
|
||||
family = inst.data["family"]
|
||||
if family not in ["render", "renderLocal"]: # legacy
|
||||
product_type = inst.data["productType"]
|
||||
if product_type not in ["render", "renderLocal"]: # legacy
|
||||
continue
|
||||
|
||||
comp_id = int(inst.data["members"][0])
|
||||
|
|
@ -81,29 +81,32 @@ class CollectAERender(publish.AbstractCollectRender):
|
|||
fps = comp_info.frameRate
|
||||
# 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)
|
||||
if not render_q:
|
||||
raise ValueError("No file extension set in Render Queue")
|
||||
render_item = render_q[0]
|
||||
|
||||
product_type = "render"
|
||||
instance_families = inst.data.get("families", [])
|
||||
subset_name = inst.data["subset"]
|
||||
instance_families.append(product_type)
|
||||
product_name = inst.data["productName"]
|
||||
instance = AERenderInstance(
|
||||
family="render",
|
||||
productType=product_type,
|
||||
family=product_type,
|
||||
families=instance_families,
|
||||
version=version,
|
||||
time="",
|
||||
source=current_file,
|
||||
label="{} - {}".format(subset_name, family),
|
||||
subset=subset_name,
|
||||
label="{} - {}".format(product_name, product_type),
|
||||
productName=product_name,
|
||||
folderPath=inst.data["folderPath"],
|
||||
task=task_name,
|
||||
attachTo=False,
|
||||
setMembers='',
|
||||
publish=True,
|
||||
name=subset_name,
|
||||
name=product_name,
|
||||
resolutionWidth=render_item.width,
|
||||
resolutionHeight=render_item.height,
|
||||
pixelAspect=1,
|
||||
|
|
@ -176,7 +179,7 @@ class CollectAERender(publish.AbstractCollectRender):
|
|||
if "#" not in file_name: # single frame (mov)W
|
||||
path = os.path.join(base_dir, "{}_{}_{}.{}".format(
|
||||
render_instance.folderPath,
|
||||
render_instance.subset,
|
||||
render_instance.productName,
|
||||
version_str,
|
||||
ext
|
||||
))
|
||||
|
|
@ -185,7 +188,7 @@ class CollectAERender(publish.AbstractCollectRender):
|
|||
for frame in range(start, end + 1):
|
||||
path = os.path.join(base_dir, "{}_{}_{}.{}.{}".format(
|
||||
render_instance.folderPath,
|
||||
render_instance.subset,
|
||||
render_instance.productName,
|
||||
version_str,
|
||||
str(frame).zfill(self.padding_width),
|
||||
ext
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ Requires:
|
|||
None
|
||||
|
||||
Provides:
|
||||
instance -> family ("review")
|
||||
instance -> families ("review")
|
||||
"""
|
||||
import pyblish.api
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ import os
|
|||
|
||||
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):
|
||||
""" Adds the AE render instances """
|
||||
|
|
@ -15,86 +12,24 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
|
|||
default_variant = "Main"
|
||||
|
||||
def process(self, context):
|
||||
existing_instance = None
|
||||
workfile_instance = None
|
||||
for instance in context:
|
||||
if instance.data["family"] == "workfile":
|
||||
self.log.debug("Workfile instance found, won't create new")
|
||||
existing_instance = instance
|
||||
if instance.data["productType"] == "workfile":
|
||||
self.log.debug("Workfile instance found")
|
||||
workfile_instance = instance
|
||||
break
|
||||
|
||||
current_file = context.data["currentFile"]
|
||||
staging_dir = os.path.dirname(current_file)
|
||||
scene_file = os.path.basename(current_file)
|
||||
if existing_instance is None: # old publish
|
||||
instance = self._get_new_instance(context, scene_file)
|
||||
else:
|
||||
instance = existing_instance
|
||||
if workfile_instance is None:
|
||||
self.log.debug("Workfile instance not found. Skipping")
|
||||
return
|
||||
|
||||
# creating representation
|
||||
representation = {
|
||||
'name': 'aep',
|
||||
'ext': 'aep',
|
||||
'files': scene_file,
|
||||
workfile_instance.data["representations"].append({
|
||||
"name": "aep",
|
||||
"ext": "aep",
|
||||
"files": scene_file,
|
||||
"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"]
|
||||
|
||||
folder_path = get_asset_name_identifier(asset_entity)
|
||||
|
||||
instance_data = {
|
||||
"active": True,
|
||||
"folderPath": folder_path,
|
||||
"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">
|
||||
<title>Subset context</title>
|
||||
<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?
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ You can fix this with "repair" button on the right and refresh Publish at the bo
|
|||
### __Detailed Info__ (optional)
|
||||
|
||||
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>
|
||||
</error>
|
||||
</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,13 +30,13 @@ VALID_EXTENSIONS = [".blend", ".json", ".abc", ".fbx"]
|
|||
|
||||
|
||||
def prepare_scene_name(
|
||||
asset: str, subset: str, namespace: Optional[str] = None
|
||||
folder_name: str, product_name: str, namespace: Optional[str] = None
|
||||
) -> str:
|
||||
"""Return a consistent name for an asset."""
|
||||
name = f"{asset}"
|
||||
name = f"{folder_name}"
|
||||
if 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
|
||||
# characters. If the name is longer, it will raise an error.
|
||||
|
|
@ -47,7 +47,7 @@ def prepare_scene_name(
|
|||
|
||||
|
||||
def get_unique_number(
|
||||
asset: str, subset: str
|
||||
folder_name: str, product_name: str
|
||||
) -> str:
|
||||
"""Return a unique number based on the asset name."""
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
|
|
@ -64,10 +64,10 @@ def get_unique_number(
|
|||
if c.get(AVALON_PROPERTY)}
|
||||
container_names = obj_group_names.union(coll_group_names)
|
||||
count = 1
|
||||
name = f"{asset}_{count:0>2}_{subset}"
|
||||
name = f"{folder_name}_{count:0>2}_{product_name}"
|
||||
while name in container_names:
|
||||
count += 1
|
||||
name = f"{asset}_{count:0>2}_{subset}"
|
||||
name = f"{folder_name}_{count:0>2}_{product_name}"
|
||||
return f"{count:0>2}"
|
||||
|
||||
|
||||
|
|
@ -161,24 +161,22 @@ class BaseCreator(Creator):
|
|||
create_as_asset_group = False
|
||||
|
||||
@staticmethod
|
||||
def cache_subsets(shared_data):
|
||||
def cache_instance_data(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
|
||||
respective creator identifiers.
|
||||
|
||||
If legacy instances are detected in the scene, create
|
||||
`blender_cached_legacy_subsets` key and fill it with
|
||||
all legacy subsets from this family as a value. # key or value?
|
||||
`blender_cached_legacy_instances` key and fill it with
|
||||
all legacy products from this family as a value. # key or value?
|
||||
|
||||
Args:
|
||||
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_legacy = {}
|
||||
|
||||
|
|
@ -210,19 +208,19 @@ class BaseCreator(Creator):
|
|||
# Legacy creator instance
|
||||
cache_legacy.setdefault(family, []).append(obj_or_col)
|
||||
|
||||
shared_data["blender_cached_subsets"] = cache
|
||||
shared_data["blender_cached_legacy_subsets"] = cache_legacy
|
||||
shared_data["blender_cached_instances"] = cache
|
||||
shared_data["blender_cached_legacy_instances"] = cache_legacy
|
||||
|
||||
return shared_data
|
||||
|
||||
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.
|
||||
Create new instance and store it.
|
||||
|
||||
Args:
|
||||
subset_name(str): Subset name of created instance.
|
||||
product_name(str): Subset name of created instance.
|
||||
instance_data(dict): Instance base data.
|
||||
pre_create_data(dict): Data based on pre creation attributes.
|
||||
Those may affect how creator works.
|
||||
|
|
@ -236,7 +234,7 @@ class BaseCreator(Creator):
|
|||
# Create asset group
|
||||
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:
|
||||
# Create instance as empty
|
||||
instance_node = bpy.data.objects.new(name=name, object_data=None)
|
||||
|
|
@ -247,10 +245,10 @@ class BaseCreator(Creator):
|
|||
instance_node = bpy.data.collections.new(name=name)
|
||||
instances.children.link(instance_node)
|
||||
|
||||
self.set_instance_data(subset_name, instance_data)
|
||||
self.set_instance_data(product_name, instance_data)
|
||||
|
||||
instance = CreatedInstance(
|
||||
self.family, subset_name, instance_data, self
|
||||
self.product_type, product_name, instance_data, self
|
||||
)
|
||||
instance.transient_data["instance_node"] = instance_node
|
||||
self._add_instance_to_context(instance)
|
||||
|
|
@ -263,18 +261,18 @@ class BaseCreator(Creator):
|
|||
"""Override abstract method from BaseCreator.
|
||||
Collect existing instances related to this creator plugin."""
|
||||
|
||||
# Cache subsets in shared data
|
||||
self.cache_subsets(self.collection_shared_data)
|
||||
# Cache instances in shared data
|
||||
self.cache_instance_data(self.collection_shared_data)
|
||||
|
||||
# Get cached subsets
|
||||
cached_subsets = self.collection_shared_data.get(
|
||||
"blender_cached_subsets"
|
||||
# Get cached instances
|
||||
cached_instances = self.collection_shared_data.get(
|
||||
"blender_cached_instances"
|
||||
)
|
||||
if not cached_subsets:
|
||||
if not cached_instances:
|
||||
return
|
||||
|
||||
# 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)
|
||||
# Create instance object from existing data
|
||||
instance = CreatedInstance.from_existing(
|
||||
|
|
@ -306,16 +304,17 @@ class BaseCreator(Creator):
|
|||
)
|
||||
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
|
||||
# workfile instance is included in the AVALON_CONTAINER collection.
|
||||
if (
|
||||
"subset" in changes.changed_keys
|
||||
"productName" 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]
|
||||
name = prepare_scene_name(
|
||||
asset=asset_name, subset=data["subset"]
|
||||
asset_name, data["productName"]
|
||||
)
|
||||
node.name = name
|
||||
|
||||
|
|
@ -341,13 +340,13 @@ class BaseCreator(Creator):
|
|||
|
||||
def set_instance_data(
|
||||
self,
|
||||
subset_name: str,
|
||||
product_name: str,
|
||||
instance_data: dict
|
||||
):
|
||||
"""Fill instance data with required items.
|
||||
|
||||
Args:
|
||||
subset_name(str): Subset name of created instance.
|
||||
product_name(str): Subset name of created instance.
|
||||
instance_data(dict): Instance base data.
|
||||
instance_node(bpy.types.ID): Instance node in blender scene.
|
||||
"""
|
||||
|
|
@ -358,7 +357,7 @@ class BaseCreator(Creator):
|
|||
{
|
||||
"id": AVALON_INSTANCE_ID,
|
||||
"creator_identifier": self.identifier,
|
||||
"subset": subset_name,
|
||||
"productName": product_name,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -466,14 +465,14 @@ class AssetLoader(LoaderPlugin):
|
|||
filepath = self.filepath_from_context(context)
|
||||
assert Path(filepath).exists(), f"{filepath} doesn't exist."
|
||||
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
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(
|
||||
asset, subset, unique_number
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
|
||||
nodes = self.process_asset(
|
||||
|
|
@ -499,10 +498,10 @@ class AssetLoader(LoaderPlugin):
|
|||
# loader=self.__class__.__name__,
|
||||
# )
|
||||
|
||||
# asset = context["asset"]["name"]
|
||||
# subset = context["subset"]["name"]
|
||||
# folder_name = context["asset"]["name"]
|
||||
# product_name = context["subset"]["name"]
|
||||
# instance_name = prepare_scene_name(
|
||||
# asset, subset, unique_number
|
||||
# folder_name, product_name, unique_number
|
||||
# ) + '_CON'
|
||||
|
||||
# return self._get_instance_collection(instance_name, nodes)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
# -*- 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.hosts.blender.api.lib import imprint
|
||||
|
||||
|
||||
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
|
||||
transform them to the current system. Since the old subsets doesn't
|
||||
This Converter will find all legacy products in the scene and will
|
||||
transform them to the current system. Since the old products doesn't
|
||||
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
|
||||
of the same family and there is no way to handle it. This code should
|
||||
nevertheless cover all creators that came with OpenPype.
|
||||
Its limitation is that you can have multiple creators creating product
|
||||
of the same product type and there is no way to handle it. This code
|
||||
should nevertheless cover all creators that came with OpenPype.
|
||||
|
||||
"""
|
||||
identifier = "io.openpype.creators.blender.legacy"
|
||||
family_to_id = {
|
||||
product_type_to_id = {
|
||||
"action": "io.openpype.creators.blender.action",
|
||||
"camera": "io.openpype.creators.blender.camera",
|
||||
"animation": "io.openpype.creators.blender.animation",
|
||||
|
|
@ -33,42 +33,42 @@ class BlenderLegacyConvertor(SubsetConvertorPlugin):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BlenderLegacyConvertor, self).__init__(*args, **kwargs)
|
||||
self.legacy_subsets = {}
|
||||
self.legacy_instances = {}
|
||||
|
||||
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.
|
||||
|
||||
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(
|
||||
"blender_cached_legacy_subsets")
|
||||
if not self.legacy_subsets:
|
||||
self.legacy_instances = self.collection_shared_data.get(
|
||||
"blender_cached_legacy_instances")
|
||||
if not self.legacy_instances:
|
||||
return
|
||||
self.add_convertor_item(
|
||||
"Found {} incompatible subset{}".format(
|
||||
len(self.legacy_subsets),
|
||||
"s" if len(self.legacy_subsets) > 1 else ""
|
||||
"Found {} incompatible product{}".format(
|
||||
len(self.legacy_instances),
|
||||
"s" if len(self.legacy_instances) > 1 else ""
|
||||
)
|
||||
)
|
||||
|
||||
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`.
|
||||
|
||||
"""
|
||||
if not self.legacy_subsets:
|
||||
if not self.legacy_instances:
|
||||
return
|
||||
|
||||
for family, instance_nodes in self.legacy_subsets.items():
|
||||
if family in self.family_to_id:
|
||||
for product_type, instance_nodes in self.legacy_instances.items():
|
||||
if product_type in self.product_type_to_id:
|
||||
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(
|
||||
"Converting {} to {}".format(instance_node.name,
|
||||
creator_identifier)
|
||||
|
|
|
|||
|
|
@ -10,20 +10,20 @@ class CreateAction(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.action"
|
||||
label = "Action"
|
||||
family = "action"
|
||||
product_type = "action"
|
||||
icon = "male"
|
||||
|
||||
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
|
||||
collection = super().create(
|
||||
subset_name, instance_data, pre_create_data
|
||||
product_name, instance_data, pre_create_data
|
||||
)
|
||||
|
||||
# Get instance name
|
||||
name = plugin.prepare_scene_name(
|
||||
instance_data["folderPath"], subset_name
|
||||
instance_data["folderPath"], product_name
|
||||
)
|
||||
|
||||
if pre_create_data.get("use_selection"):
|
||||
|
|
|
|||
|
|
@ -8,15 +8,15 @@ class CreateAnimation(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.animation"
|
||||
label = "Animation"
|
||||
family = "animation"
|
||||
product_type = "animation"
|
||||
icon = "male"
|
||||
|
||||
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
|
||||
collection = super().create(
|
||||
subset_name, instance_data, pre_create_data
|
||||
product_name, instance_data, pre_create_data
|
||||
)
|
||||
|
||||
if pre_create_data.get("use_selection"):
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ class CreateBlendScene(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.blendscene"
|
||||
label = "Blender Scene"
|
||||
family = "blendScene"
|
||||
product_type = "blendScene"
|
||||
icon = "cubes"
|
||||
|
||||
maintain_selection = False
|
||||
|
||||
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,
|
||||
pre_create_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -11,16 +11,16 @@ class CreateCamera(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.camera"
|
||||
label = "Camera"
|
||||
family = "camera"
|
||||
product_type = "camera"
|
||||
icon = "video-camera"
|
||||
|
||||
create_as_asset_group = True
|
||||
|
||||
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,
|
||||
pre_create_data)
|
||||
|
||||
|
|
@ -30,8 +30,8 @@ class CreateCamera(plugin.BaseCreator):
|
|||
obj.parent = asset_group
|
||||
else:
|
||||
plugin.deselect_all()
|
||||
camera = bpy.data.cameras.new(subset_name)
|
||||
camera_obj = bpy.data.objects.new(subset_name, camera)
|
||||
camera = bpy.data.cameras.new(product_name)
|
||||
camera_obj = bpy.data.objects.new(product_name, camera)
|
||||
|
||||
instances = bpy.data.collections.get(AVALON_INSTANCES)
|
||||
instances.objects.link(camera_obj)
|
||||
|
|
|
|||
|
|
@ -10,16 +10,16 @@ class CreateLayout(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.layout"
|
||||
label = "Layout"
|
||||
family = "layout"
|
||||
product_type = "layout"
|
||||
icon = "cubes"
|
||||
|
||||
create_as_asset_group = True
|
||||
|
||||
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,
|
||||
pre_create_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ class CreateModel(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.model"
|
||||
label = "Model"
|
||||
family = "model"
|
||||
product_type = "model"
|
||||
icon = "cube"
|
||||
|
||||
create_as_asset_group = True
|
||||
|
||||
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,
|
||||
pre_create_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,15 @@ class CreatePointcache(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.pointcache"
|
||||
label = "Point Cache"
|
||||
family = "pointcache"
|
||||
product_type = "pointcache"
|
||||
icon = "gears"
|
||||
|
||||
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
|
||||
collection = super().create(
|
||||
subset_name, instance_data, pre_create_data
|
||||
product_name, instance_data, pre_create_data
|
||||
)
|
||||
|
||||
if pre_create_data.get("use_selection"):
|
||||
|
|
|
|||
|
|
@ -12,16 +12,16 @@ class CreateRenderlayer(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.render"
|
||||
label = "Render"
|
||||
family = "render"
|
||||
product_type = "render"
|
||||
icon = "eye"
|
||||
|
||||
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:
|
||||
# Run parent create method
|
||||
collection = super().create(
|
||||
subset_name, instance_data, pre_create_data
|
||||
product_name, instance_data, pre_create_data
|
||||
)
|
||||
|
||||
prepare_rendering(collection)
|
||||
|
|
|
|||
|
|
@ -8,15 +8,15 @@ class CreateReview(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.review"
|
||||
label = "Review"
|
||||
family = "review"
|
||||
product_type = "review"
|
||||
icon = "video-camera"
|
||||
|
||||
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
|
||||
collection = super().create(
|
||||
subset_name, instance_data, pre_create_data
|
||||
product_name, instance_data, pre_create_data
|
||||
)
|
||||
|
||||
if pre_create_data.get("use_selection"):
|
||||
|
|
|
|||
|
|
@ -10,15 +10,15 @@ class CreateRig(plugin.BaseCreator):
|
|||
|
||||
identifier = "io.openpype.creators.blender.rig"
|
||||
label = "Rig"
|
||||
family = "rig"
|
||||
product_type = "rig"
|
||||
icon = "wheelchair"
|
||||
|
||||
create_as_asset_group = True
|
||||
|
||||
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,
|
||||
pre_create_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
"""
|
||||
identifier = "io.openpype.creators.blender.workfile"
|
||||
label = "Workfile"
|
||||
family = "workfile"
|
||||
product_type = "workfile"
|
||||
icon = "fa5.file"
|
||||
|
||||
def create(self):
|
||||
|
|
@ -43,8 +43,12 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
|
||||
if not workfile_instance:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
task_name, task_name, asset_doc, project_name, host_name
|
||||
product_name = self.get_product_name(
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
task_name,
|
||||
host_name,
|
||||
)
|
||||
data = {
|
||||
"folderPath": asset_name,
|
||||
|
|
@ -53,17 +57,17 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
}
|
||||
data.update(
|
||||
self.get_dynamic_data(
|
||||
task_name,
|
||||
task_name,
|
||||
asset_doc,
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
task_name,
|
||||
host_name,
|
||||
workfile_instance,
|
||||
)
|
||||
)
|
||||
self.log.info("Auto-creating workfile instance...")
|
||||
workfile_instance = CreatedInstance(
|
||||
self.family, subset_name, data, self
|
||||
self.product_type, product_name, data, self
|
||||
)
|
||||
self._add_instance_to_context(workfile_instance)
|
||||
|
||||
|
|
@ -73,13 +77,17 @@ class CreateWorkfile(BaseCreator, AutoCreator):
|
|||
):
|
||||
# Update instance context if it's different
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
task_name, task_name, asset_doc, project_name, host_name
|
||||
product_name = self.get_product_name(
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
self.default_variant,
|
||||
host_name,
|
||||
)
|
||||
|
||||
workfile_instance["folderPath"] = asset_name
|
||||
workfile_instance["task"] = task_name
|
||||
workfile_instance["subset"] = subset_name
|
||||
workfile_instance["productName"] = product_name
|
||||
|
||||
instance_node = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not instance_node:
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ from ayon_core.hosts.blender.api import plugin
|
|||
|
||||
|
||||
def append_workfile(context, fname, do_import):
|
||||
asset = context['asset']['name']
|
||||
subset = context['subset']['name']
|
||||
folder_name = context['asset']['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,
|
||||
# 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)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
containers = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not containers:
|
||||
|
|
@ -159,6 +161,7 @@ class CacheModelLoader(plugin.AssetLoader):
|
|||
|
||||
self._link_objects(objects, asset_group, containers, asset_group)
|
||||
|
||||
product_type = context["subset"]["data"]["family"]
|
||||
asset_group[AVALON_PROPERTY] = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": AVALON_CONTAINER_ID,
|
||||
|
|
@ -169,7 +172,7 @@ class CacheModelLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": product_type,
|
||||
"objectName": group_name
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@ class BlendActionLoader(plugin.AssetLoader):
|
|||
"""
|
||||
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
lib_container = plugin.prepare_scene_name(asset, subset)
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
lib_container = plugin.prepare_scene_name(folder_name, product_name)
|
||||
container_name = plugin.prepare_scene_name(
|
||||
asset, subset, namespace
|
||||
folder_name, product_name, namespace
|
||||
)
|
||||
|
||||
container = bpy.data.collections.new(lib_container)
|
||||
|
|
|
|||
|
|
@ -39,13 +39,15 @@ class AudioLoader(plugin.AssetLoader):
|
|||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
|
|
@ -85,7 +87,7 @@ class AudioLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": context["subset"]["data"]["family"],
|
||||
"objectName": group_name,
|
||||
"audio": audio
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,20 +127,22 @@ class BlendLoader(plugin.AssetLoader):
|
|||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
try:
|
||||
family = context["representation"]["context"]["family"]
|
||||
product_type = context["subset"]["data"]["family"]
|
||||
except ValueError:
|
||||
family = "model"
|
||||
product_type = "model"
|
||||
|
||||
representation = str(context["representation"]["_id"])
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
|
|
@ -149,8 +151,8 @@ class BlendLoader(plugin.AssetLoader):
|
|||
|
||||
container, members = self._process_data(libpath, group_name)
|
||||
|
||||
if family == "layout":
|
||||
self._post_process_layout(container, asset, representation)
|
||||
if product_type == "layout":
|
||||
self._post_process_layout(container, folder_name, representation)
|
||||
|
||||
avalon_container.objects.link(container)
|
||||
|
||||
|
|
@ -164,7 +166,7 @@ class BlendLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": context["subset"]["data"]["family"],
|
||||
"objectName": group_name,
|
||||
"members": members,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class BlendSceneLoader(plugin.AssetLoader):
|
|||
|
||||
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
|
||||
with bpy.data.libraries.load(
|
||||
libpath, link=False, relative=False
|
||||
|
|
@ -82,25 +82,29 @@ class BlendSceneLoader(plugin.AssetLoader):
|
|||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
try:
|
||||
family = context["representation"]["context"]["family"]
|
||||
product_type = context["subset"]["data"]["family"]
|
||||
except ValueError:
|
||||
family = "model"
|
||||
product_type = "model"
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS)
|
||||
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)
|
||||
|
||||
|
|
@ -114,7 +118,7 @@ class BlendSceneLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": context["subset"]["data"]["family"],
|
||||
"objectName": group_name,
|
||||
"members": members,
|
||||
}
|
||||
|
|
@ -167,8 +171,12 @@ class BlendSceneLoader(plugin.AssetLoader):
|
|||
|
||||
self.exec_remove(container)
|
||||
|
||||
family = container["family"]
|
||||
asset_group, members = self._process_data(libpath, group_name, family)
|
||||
product_type = container.get("productType")
|
||||
if product_type is None:
|
||||
product_type = container["family"]
|
||||
asset_group, members = self._process_data(
|
||||
libpath, group_name, product_type
|
||||
)
|
||||
|
||||
for member in members:
|
||||
if member.name in collection_parents:
|
||||
|
|
|
|||
|
|
@ -84,13 +84,15 @@ class AbcCameraLoader(plugin.AssetLoader):
|
|||
|
||||
libpath = self.filepath_from_context(context)
|
||||
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
|
|
@ -121,7 +123,7 @@ class AbcCameraLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": context["subset"]["data"]["family"],
|
||||
"objectName": group_name,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,13 +87,15 @@ class FbxCameraLoader(plugin.AssetLoader):
|
|||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
|
|
@ -124,7 +126,7 @@ class FbxCameraLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": context["subset"]["data"]["family"],
|
||||
"objectName": group_name
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -131,13 +131,15 @@ class FbxModelLoader(plugin.AssetLoader):
|
|||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
|
|
@ -168,7 +170,7 @@ class FbxModelLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": context["subset"]["data"]["family"],
|
||||
"objectName": group_name
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,11 +50,11 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
if anim_collection:
|
||||
bpy.data.collections.remove(anim_collection)
|
||||
|
||||
def _get_loader(self, loaders, family):
|
||||
def _get_loader(self, loaders, product_type):
|
||||
name = ""
|
||||
if family == 'rig':
|
||||
if product_type == 'rig':
|
||||
name = "BlendRigLoader"
|
||||
elif family == 'model':
|
||||
elif product_type == 'model':
|
||||
name = "BlendModelLoader"
|
||||
|
||||
if name == "":
|
||||
|
|
@ -76,10 +76,12 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
|
||||
for element in data:
|
||||
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)
|
||||
loader = self._get_loader(loaders, family)
|
||||
loader = self._get_loader(loaders, product_type)
|
||||
|
||||
if not loader:
|
||||
continue
|
||||
|
|
@ -95,7 +97,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
'parent': asset_group,
|
||||
'transform': element.get('transform'),
|
||||
'action': action,
|
||||
'create_animation': True if family == 'rig' else False,
|
||||
'create_animation': True if product_type == 'rig' else False,
|
||||
'animation_asset': asset
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +129,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
# legacy_create(
|
||||
# creator_plugin,
|
||||
# name="camera",
|
||||
# # name=f"{unique_number}_{subset}_animation",
|
||||
# # name=f"{unique_number}_{product[name]}_animation",
|
||||
# asset=asset,
|
||||
# options={"useSelection": False}
|
||||
# # data={"dependencies": str(context["representation"]["_id"])}
|
||||
|
|
@ -146,13 +148,15 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.prepare_scene_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.prepare_scene_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
asset_name = plugin.prepare_scene_name(folder_name, product_name)
|
||||
unique_number = plugin.get_unique_number(folder_name, product_name)
|
||||
group_name = plugin.prepare_scene_name(
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
namespace = namespace or f"{folder_name}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
|
|
@ -177,7 +181,7 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"productType": context["subset"]["data"]["family"],
|
||||
"objectName": group_name
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +243,10 @@ class JsonLayoutLoader(plugin.AssetLoader):
|
|||
|
||||
for obj in asset_group.children:
|
||||
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
|
||||
for child in obj.children:
|
||||
if child.type == 'ARMATURE':
|
||||
|
|
|
|||
|
|
@ -93,18 +93,18 @@ class BlendLookLoader(plugin.AssetLoader):
|
|||
"""
|
||||
|
||||
libpath = self.filepath_from_context(context)
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
folder_name = context["asset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
lib_container = plugin.prepare_scene_name(
|
||||
asset, subset
|
||||
folder_name, product_name
|
||||
)
|
||||
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(
|
||||
asset, subset, unique_number
|
||||
folder_name, product_name, unique_number
|
||||
)
|
||||
|
||||
container = bpy.data.collections.new(lib_container)
|
||||
|
|
@ -131,7 +131,7 @@ class BlendLookLoader(plugin.AssetLoader):
|
|||
metadata["materials"] = materials
|
||||
|
||||
metadata["parent"] = str(context["representation"]["parent"])
|
||||
metadata["family"] = context["representation"]["context"]["family"]
|
||||
metadata["product_type"] = context["subset"]["data"]["family"]
|
||||
|
||||
nodes = list(container.objects)
|
||||
nodes.append(container)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class CollectBlenderInstanceData(pyblish.api.InstancePlugin):
|
|||
members.extend(instance_node.children)
|
||||
|
||||
# Special case for animation instances, include armatures
|
||||
if instance.data["family"] == "animation":
|
||||
if instance.data["productType"] == "animation":
|
||||
for obj in instance_node.objects:
|
||||
if obj.type == 'EMPTY' and obj.get(AVALON_PROPERTY):
|
||||
members.extend(
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ class ExtractABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
filename = f"{instance_name}.abc"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ class ExtractAnimationABC(
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
filename = f"{instance_name}.abc"
|
||||
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ class ExtractBlend(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
# Define extract output file path
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
filename = f"{instance_name}.blend"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ class ExtractBlendAnimation(
|
|||
# Define extract output file path
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
filename = f"{instance_name}.blend"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ class ExtractCameraABC(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
filename = f"{instance_name}.abc"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,9 +20,9 @@ class ExtractCamera(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
filename = f"{instance_name}.fbx"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ class ExtractFBX(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# Define extract output file path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
filename = f"{instance_name}.fbx"
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -145,9 +145,9 @@ class ExtractAnimationFBX(
|
|||
|
||||
root.select_set(True)
|
||||
armature.select_set(True)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
fbx_filename = f"{instance_name}_{armature.name}.fbx"
|
||||
filepath = os.path.join(stagingdir, fbx_filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
fbx_count = 0
|
||||
|
||||
project_name = instance.context.data["projectEntity"]["name"]
|
||||
project_name = instance.context.data["projectName"]
|
||||
for asset in asset_group.children:
|
||||
metadata = asset.get(AVALON_PROPERTY)
|
||||
if not metadata:
|
||||
|
|
@ -147,7 +147,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
continue
|
||||
|
||||
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))
|
||||
# Get blend reference
|
||||
|
|
@ -179,7 +181,8 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
json_element["reference_fbx"] = str(fbx_id)
|
||||
if 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["asset_name"] = metadata["asset_name"]
|
||||
json_element["file_path"] = metadata["libpath"]
|
||||
|
|
@ -215,7 +218,7 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
]
|
||||
|
||||
# Extract the animation as well
|
||||
if family == "rig":
|
||||
if product_type == "rig":
|
||||
f, n = self._export_animation(
|
||||
asset, instance, stagingdir, fbx_count)
|
||||
if f:
|
||||
|
|
@ -225,9 +228,9 @@ class ExtractLayout(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
json_data.append(json_element)
|
||||
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
instance_name = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
instance_name = f"{folder_name}_{product_name}"
|
||||
json_filename = f"{instance_name}.json"
|
||||
|
||||
json_path = os.path.join(stagingdir, json_filename)
|
||||
|
|
|
|||
|
|
@ -55,9 +55,9 @@ class ExtractPlayblast(publish.Extractor, publish.OptionalPyblishPluginMixin):
|
|||
|
||||
# get output path
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
filename = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
filename = f"{folder_name}_{product_name}"
|
||||
|
||||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ class ExtractThumbnail(publish.Extractor):
|
|||
return
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
asset_name = instance.data["assetEntity"]["name"]
|
||||
subset = instance.data["subset"]
|
||||
filename = f"{asset_name}_{subset}"
|
||||
folder_name = instance.data["assetEntity"]["name"]
|
||||
product_name = instance.data["productName"]
|
||||
filename = f"{folder_name}_{product_name}"
|
||||
|
||||
path = os.path.join(stagingdir, filename)
|
||||
|
||||
|
|
@ -42,11 +42,11 @@ class ExtractThumbnail(publish.Extractor):
|
|||
|
||||
camera = instance.data.get("review_camera", "AUTO")
|
||||
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)
|
||||
|
||||
presets = json.loads(self.presets)
|
||||
preset = presets.get(family, {})
|
||||
preset = presets.get(product_type, {})
|
||||
|
||||
preset.update({
|
||||
"camera": camera,
|
||||
|
|
|
|||
|
|
@ -28,25 +28,26 @@ class IntegrateAnimation(
|
|||
# Update the json file for the setdress to add the published
|
||||
# representations of the animations
|
||||
for json_dict in data:
|
||||
json_product_name = json_dict["productName"]
|
||||
i = None
|
||||
for elem in instance.context:
|
||||
if elem.data.get('subset') == json_dict['subset']:
|
||||
if elem.data["productName"] == json_product_name:
|
||||
i = elem
|
||||
break
|
||||
if not i:
|
||||
continue
|
||||
rep = None
|
||||
pub_repr = i.data.get('published_representations')
|
||||
pub_repr = i.data["published_representations"]
|
||||
for elem in pub_repr:
|
||||
if pub_repr.get(elem).get('representation').get('name') == "fbx":
|
||||
rep = pub_repr.get(elem)
|
||||
if pub_repr[elem]["representation"]["name"] == "fbx":
|
||||
rep = pub_repr[elem]
|
||||
break
|
||||
if not rep:
|
||||
continue
|
||||
obj_id = rep.get('representation').get('_id')
|
||||
obj_id = rep["representation"]["_id"]
|
||||
|
||||
if obj_id:
|
||||
json_dict['_id'] = str(obj_id)
|
||||
json_dict["representation_id"] = str(obj_id)
|
||||
|
||||
with open(json_path, "w") as file:
|
||||
json.dump(data, fp=file, indent=2)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
|
|||
hosts = ["blender"]
|
||||
label = "Validate File Saved"
|
||||
optional = False
|
||||
# TODO rename to 'exclude_product_types'
|
||||
exclude_families = []
|
||||
actions = [SaveWorkfileAction]
|
||||
|
||||
|
|
@ -41,8 +42,8 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
|
|||
|
||||
# Do not validate workfile has unsaved changes if only instances
|
||||
# present of families that should be excluded
|
||||
families = {
|
||||
instance.data["family"] for instance in context
|
||||
product_types = {
|
||||
instance.data["productType"] for instance in context
|
||||
# Consider only enabled instances
|
||||
if instance.data.get("publish", True)
|
||||
and instance.data.get("active", True)
|
||||
|
|
@ -52,7 +53,7 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
|
|||
return any(family in exclude_family
|
||||
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 "
|
||||
"unsaved changes validation..")
|
||||
return
|
||||
|
|
|
|||
|
|
@ -46,17 +46,18 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
|
|||
shared_instance_data.update(celaction_kwargs)
|
||||
|
||||
# workfile instance
|
||||
family = "workfile"
|
||||
subset = family + task.capitalize()
|
||||
product_type = "workfile"
|
||||
product_name = product_type + task.capitalize()
|
||||
# Create instance
|
||||
instance = context.create_instance(subset)
|
||||
instance = context.create_instance(product_name)
|
||||
|
||||
# creating instance data
|
||||
instance.data.update({
|
||||
"subset": subset,
|
||||
"label": scene_file,
|
||||
"family": family,
|
||||
"families": [],
|
||||
"productName": product_name,
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type],
|
||||
"representations": []
|
||||
})
|
||||
|
||||
|
|
@ -76,17 +77,19 @@ class CollectCelactionInstances(pyblish.api.ContextPlugin):
|
|||
self.log.info('Publishing Celaction workfile')
|
||||
|
||||
# render instance
|
||||
subset = f"render{task}Main"
|
||||
instance = context.create_instance(name=subset)
|
||||
product_name = f"render{task}Main"
|
||||
product_type = "render.farm"
|
||||
instance = context.create_instance(name=product_name)
|
||||
# getting instance state
|
||||
instance.data["publish"] = True
|
||||
|
||||
# add assetEntity data into instance
|
||||
instance.data.update({
|
||||
"label": "{} - farm".format(subset),
|
||||
"family": "render.farm",
|
||||
"families": [],
|
||||
"subset": subset
|
||||
"label": "{} - farm".format(product_name),
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type],
|
||||
"productName": product_name
|
||||
})
|
||||
|
||||
# adding basic script data
|
||||
|
|
|
|||
|
|
@ -19,12 +19,14 @@ class CollectRenderPath(pyblish.api.InstancePlugin):
|
|||
anatomy = instance.context.data["anatomy"]
|
||||
anatomy_data = copy.deepcopy(instance.data["anatomyData"])
|
||||
padding = anatomy.templates.get("frame_padding", 4)
|
||||
product_type = "render"
|
||||
anatomy_data.update({
|
||||
"frame": f"%0{padding}d",
|
||||
"family": "render",
|
||||
"family": product_type,
|
||||
"representation": self.output_extension,
|
||||
"ext": self.output_extension
|
||||
})
|
||||
anatomy_data["product"]["type"] = product_type
|
||||
|
||||
anatomy_filled = anatomy.format(anatomy_data)
|
||||
|
||||
|
|
|
|||
|
|
@ -147,8 +147,8 @@ def imprint(segment, data=None):
|
|||
Examples:
|
||||
data = {
|
||||
'asset': 'sq020sh0280',
|
||||
'family': 'render',
|
||||
'subset': 'subsetMain'
|
||||
'productType': 'render',
|
||||
'productName': 'productMain'
|
||||
}
|
||||
"""
|
||||
data = data or {}
|
||||
|
|
|
|||
|
|
@ -353,9 +353,9 @@ class PublishableClip:
|
|||
rename_default = False
|
||||
hierarchy_default = "{_folder_}/{_sequence_}/{_track_}"
|
||||
clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}"
|
||||
subset_name_default = "[ track name ]"
|
||||
review_track_default = "[ none ]"
|
||||
subset_family_default = "plate"
|
||||
base_product_name_default = "[ track name ]"
|
||||
base_product_type_default = "plate"
|
||||
count_from_default = 10
|
||||
count_steps_default = 10
|
||||
vertical_sync_default = False
|
||||
|
|
@ -368,7 +368,7 @@ class PublishableClip:
|
|||
|
||||
def __init__(self, segment, **kwargs):
|
||||
self.rename_index = kwargs["rename_index"]
|
||||
self.family = kwargs["family"]
|
||||
self.product_type = kwargs["family"]
|
||||
self.log = kwargs["log"]
|
||||
|
||||
# get main parent objects
|
||||
|
|
@ -486,10 +486,10 @@ class PublishableClip:
|
|||
"countFrom", {}).get("value") or self.count_from_default
|
||||
self.count_steps = self.ui_inputs.get(
|
||||
"countSteps", {}).get("value") or self.count_steps_default
|
||||
self.subset_name = self.ui_inputs.get(
|
||||
"subsetName", {}).get("value") or self.subset_name_default
|
||||
self.subset_family = self.ui_inputs.get(
|
||||
"subsetFamily", {}).get("value") or self.subset_family_default
|
||||
self.base_product_name = self.ui_inputs.get(
|
||||
"productName", {}).get("value") or self.base_product_name_default
|
||||
self.base_product_type = self.ui_inputs.get(
|
||||
"productType", {}).get("value") or self.base_product_type_default
|
||||
self.vertical_sync = self.ui_inputs.get(
|
||||
"vSyncOn", {}).get("value") or self.vertical_sync_default
|
||||
self.driving_layer = self.ui_inputs.get(
|
||||
|
|
@ -509,12 +509,14 @@ class PublishableClip:
|
|||
or self.retimed_framerange_default
|
||||
)
|
||||
|
||||
# build subset name from layer name
|
||||
if self.subset_name == "[ track name ]":
|
||||
self.subset_name = self.track_name
|
||||
# build product name from layer name
|
||||
if self.base_product_name == "[ track name ]":
|
||||
self.base_product_name = self.track_name
|
||||
|
||||
# create subset for publishing
|
||||
self.subset = self.subset_family + self.subset_name.capitalize()
|
||||
# create product for publishing
|
||||
self.product_name = (
|
||||
self.base_product_type + self.base_product_name.capitalize()
|
||||
)
|
||||
|
||||
def _replace_hash_to_expression(self, name, text):
|
||||
""" Replace hash with number in correct padding. """
|
||||
|
|
@ -608,14 +610,14 @@ class PublishableClip:
|
|||
_hero_data = deepcopy(hero_data)
|
||||
_hero_data.update({"heroTrack": False})
|
||||
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
|
||||
if self.subset in data_subset:
|
||||
_hero_data["subset"] = self.subset + str(
|
||||
if self.product_name in data_product_name:
|
||||
_hero_data["productName"] = self.product_name + str(
|
||||
self.track_index)
|
||||
# in case track name and subset name is the same then add
|
||||
if self.subset_name == self.track_name:
|
||||
_hero_data["subset"] = self.subset
|
||||
# in case track name and product name is the same then add
|
||||
if self.base_product_name == self.track_name:
|
||||
_hero_data["productName"] = self.product_name
|
||||
# assign data to return hierarchy data to tag
|
||||
tag_hierarchy_data = _hero_data
|
||||
break
|
||||
|
|
@ -637,9 +639,9 @@ class PublishableClip:
|
|||
"hierarchy": hierarchy_filled,
|
||||
"parents": self.parents,
|
||||
"hierarchyData": hierarchy_formatting_data,
|
||||
"subset": self.subset,
|
||||
"family": self.subset_family,
|
||||
"families": [self.family]
|
||||
"productName": self.product_name,
|
||||
"productType": self.base_product_type,
|
||||
"families": [self.base_product_type, self.product_type]
|
||||
}
|
||||
|
||||
def _convert_to_entity(self, type, template):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ class CreateShotClip(opfapi.Creator):
|
|||
"""Publishable clip"""
|
||||
|
||||
label = "Create Publishable Clip"
|
||||
family = "clip"
|
||||
product_type = "clip"
|
||||
icon = "film"
|
||||
defaults = ["Main"]
|
||||
|
||||
|
|
@ -32,7 +32,7 @@ class CreateShotClip(opfapi.Creator):
|
|||
|
||||
# open widget for plugins inputs
|
||||
results_back = self.create_widget(
|
||||
"Pype publish attributes creator",
|
||||
"AYON publish attributes creator",
|
||||
"Define sequential rename and fill hierarchy data.",
|
||||
gui_inputs
|
||||
)
|
||||
|
|
@ -62,7 +62,7 @@ class CreateShotClip(opfapi.Creator):
|
|||
"log": self.log,
|
||||
"ui_inputs": results_back,
|
||||
"avalon": self.data,
|
||||
"family": self.data["family"]
|
||||
"product_type": self.data["productType"]
|
||||
}
|
||||
|
||||
for i, segment in enumerate(sorted_selected_segments):
|
||||
|
|
@ -203,19 +203,19 @@ class CreateShotClip(opfapi.Creator):
|
|||
"target": "ui",
|
||||
"order": 3,
|
||||
"value": {
|
||||
"subsetName": {
|
||||
"productName": {
|
||||
"value": ["[ track name ]", "main", "bg", "fg", "bg",
|
||||
"animatic"],
|
||||
"type": "QComboBox",
|
||||
"label": "Subset Name",
|
||||
"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},
|
||||
"subsetFamily": {
|
||||
"productType": {
|
||||
"value": ["plate", "take"],
|
||||
"type": "QComboBox",
|
||||
"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},
|
||||
"reviewTrack": {
|
||||
"value": ["< none >"] + gui_tracks,
|
||||
|
|
@ -229,7 +229,7 @@ class CreateShotClip(opfapi.Creator):
|
|||
"type": "QCheckBox",
|
||||
"label": "Include audio",
|
||||
"target": "tag",
|
||||
"toolTip": "Process subsets with corresponding audio", # noqa
|
||||
"toolTip": "Process products with corresponding audio", # noqa
|
||||
"order": 3},
|
||||
"sourceResolution": {
|
||||
"value": False,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from ayon_core.lib.transcoding import (
|
|||
|
||||
|
||||
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
|
||||
during conforming to project
|
||||
|
|
@ -31,14 +31,14 @@ class LoadClip(opfapi.ClipLoader):
|
|||
# settings
|
||||
reel_group_name = "OpenPype_Reels"
|
||||
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:
|
||||
- {layerName} - original layer name token
|
||||
- {layerUID} - original layer UID token
|
||||
- {originalBasename} - original clip name taken from file
|
||||
"""
|
||||
layer_rename_template = "{asset}_{subset}<_{output}>"
|
||||
layer_rename_template = "{folder[name]}_{product[name]}<_{output}>"
|
||||
layer_rename_patterns = []
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from ayon_core.lib.transcoding import (
|
|||
)
|
||||
|
||||
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
|
||||
during conforming to project
|
||||
|
|
@ -29,14 +29,14 @@ class LoadClipBatch(opfapi.ClipLoader):
|
|||
|
||||
# settings
|
||||
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:
|
||||
- {layerName} - original layer name token
|
||||
- {layerUID} - original layer UID token
|
||||
- {originalBasename} - original clip name taken from file
|
||||
"""
|
||||
layer_rename_template = "{asset}_{subset}<_{output}>"
|
||||
layer_rename_template = "{folder[name]}_{product[name]}<_{output}>"
|
||||
layer_rename_patterns = []
|
||||
|
||||
def load(self, context, name, namespace, options):
|
||||
|
|
@ -50,17 +50,8 @@ class LoadClipBatch(opfapi.ClipLoader):
|
|||
version_name = version.get("name", None)
|
||||
colorspace = self.get_colorspace(context)
|
||||
|
||||
# TODO remove '{folder[name]}' and '{product[name]}' replacement
|
||||
clip_name_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}")
|
||||
)
|
||||
clip_name_template = self.clip_name_template
|
||||
layer_rename_template = self.layer_rename_template
|
||||
# in case output is not in context replace key to representation
|
||||
if not context["representation"]["context"].get("output"):
|
||||
clip_name_template = clip_name_template.replace(
|
||||
|
|
@ -68,8 +59,22 @@ class LoadClipBatch(opfapi.ClipLoader):
|
|||
layer_rename_template = layer_rename_template.replace(
|
||||
"output", "representation")
|
||||
|
||||
asset_doc = context["asset"]
|
||||
subset_doc = context["subset"]
|
||||
formatting_data = deepcopy(context["representation"]["context"])
|
||||
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(
|
||||
formatting_data)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,6 @@ class CollectTestSelection(pyblish.api.ContextPlugin):
|
|||
|
||||
opfapi.imprint(segment, {
|
||||
'asset': segment.name.get_value(),
|
||||
'family': 'render',
|
||||
'subset': 'subsetMain'
|
||||
'productType': 'render',
|
||||
'productName': 'productMain'
|
||||
})
|
||||
|
|
|
|||
|
|
@ -110,24 +110,25 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
|
|||
# add ocio_data to instance data
|
||||
inst_data.update(otio_data)
|
||||
|
||||
asset = marker_data["asset"]
|
||||
subset = marker_data["subset"]
|
||||
folder_path = marker_data["folderPath"]
|
||||
folder_name = folder_path.rsplit("/")[-1]
|
||||
product_name = marker_data["productName"]
|
||||
|
||||
# insert family into families
|
||||
family = marker_data["family"]
|
||||
# insert product type into families
|
||||
product_type = marker_data["productType"]
|
||||
families = [str(f) for f in marker_data["families"]]
|
||||
families.insert(0, str(family))
|
||||
families.insert(0, str(product_type))
|
||||
|
||||
# form label
|
||||
label = asset
|
||||
if asset != clip_name:
|
||||
label = folder_name
|
||||
if folder_name != clip_name:
|
||||
label += " ({})".format(clip_name)
|
||||
label += " {} [{}]".format(subset, ", ".join(families))
|
||||
label += " {} [{}]".format(product_name, ", ".join(families))
|
||||
|
||||
inst_data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"name": "{}_{}".format(folder_name, product_name),
|
||||
"label": label,
|
||||
"asset": asset,
|
||||
"folderPath": folder_path,
|
||||
"item": segment,
|
||||
"families": families,
|
||||
"publish": marker_data["publish"],
|
||||
|
|
@ -335,26 +336,28 @@ class CollectTimelineInstances(pyblish.api.ContextPlugin):
|
|||
if not hierarchy_data:
|
||||
return
|
||||
|
||||
asset = data["asset"]
|
||||
subset = "shotMain"
|
||||
folder_path = data["folderPath"]
|
||||
folder_name = folder_path.rsplit("/")[-1]
|
||||
product_name = "shotMain"
|
||||
|
||||
# insert family into families
|
||||
family = "shot"
|
||||
# insert product type into families
|
||||
product_type = "shot"
|
||||
|
||||
# form label
|
||||
label = asset
|
||||
if asset != clip_name:
|
||||
label = folder_name
|
||||
if folder_name != clip_name:
|
||||
label += " ({}) ".format(clip_name)
|
||||
label += " {}".format(subset)
|
||||
label += " [{}]".format(family)
|
||||
label += " {}".format(product_name)
|
||||
label += " [{}]".format(product_type)
|
||||
|
||||
data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"name": "{}_{}".format(folder_name, product_name),
|
||||
"label": label,
|
||||
"subset": subset,
|
||||
"asset": asset,
|
||||
"family": family,
|
||||
"families": []
|
||||
"productName": product_name,
|
||||
"folderPath": folder_path,
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type]
|
||||
})
|
||||
|
||||
instance = context.create_instance(**data)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import pyblish.api
|
|||
from ayon_core.client import get_asset_name_identifier
|
||||
import ayon_core.hosts.flame.api as opfapi
|
||||
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):
|
||||
|
|
@ -14,7 +14,7 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
|||
|
||||
def process(self, context):
|
||||
# plugin defined
|
||||
family = "workfile"
|
||||
product_type = "workfile"
|
||||
variant = "otioTimeline"
|
||||
|
||||
# main
|
||||
|
|
@ -23,14 +23,14 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
|||
project = opfapi.get_current_project()
|
||||
sequence = opfapi.get_current_sequence(opfapi.CTX.selection)
|
||||
|
||||
# create subset name
|
||||
subset_name = get_subset_name(
|
||||
family,
|
||||
variant,
|
||||
task_name,
|
||||
asset_doc,
|
||||
# create product name
|
||||
product_name = get_product_name(
|
||||
context.data["projectName"],
|
||||
asset_doc,
|
||||
task_name,
|
||||
context.data["hostName"],
|
||||
product_type,
|
||||
variant,
|
||||
project_settings=context.data["project_settings"]
|
||||
)
|
||||
|
||||
|
|
@ -41,11 +41,12 @@ class CollecTimelineOTIO(pyblish.api.ContextPlugin):
|
|||
otio_timeline = flame_export.create_otio_timeline(sequence)
|
||||
|
||||
instance_data = {
|
||||
"name": subset_name,
|
||||
"name": product_name,
|
||||
"folderPath": folder_path,
|
||||
"subset": subset_name,
|
||||
"family": "workfile",
|
||||
"families": []
|
||||
"productName": product_name,
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type]
|
||||
}
|
||||
|
||||
# create instance with workfile
|
||||
|
|
|
|||
|
|
@ -44,8 +44,8 @@ class IntegrateBatchGroup(pyblish.api.InstancePlugin):
|
|||
))
|
||||
|
||||
# load plate to batch group
|
||||
self.log.info("Loading subset `{}` into batch `{}`".format(
|
||||
instance.data["subset"], bgroup.name.get_value()
|
||||
self.log.info("Loading product `{}` into batch `{}`".format(
|
||||
instance.data["productName"], bgroup.name.get_value()
|
||||
))
|
||||
self._load_clip_to_context(instance, bgroup)
|
||||
|
||||
|
|
|
|||
|
|
@ -33,14 +33,16 @@ class GenericCreateSaver(Creator):
|
|||
|
||||
# TODO: This should be renamed together with Nuke so it is aligned
|
||||
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)
|
||||
|
||||
instance = CreatedInstance(
|
||||
family=self.family,
|
||||
subset_name=subset_name,
|
||||
product_type=self.product_type,
|
||||
product_name=product_name,
|
||||
data=instance_data,
|
||||
creator=self,
|
||||
)
|
||||
|
|
@ -111,23 +113,23 @@ class GenericCreateSaver(Creator):
|
|||
tool.SetData(f"openpype.{key}", value)
|
||||
|
||||
def _update_tool_with_data(self, tool, data):
|
||||
"""Update tool node name and output path based on subset data"""
|
||||
if "subset" not in data:
|
||||
"""Update tool node name and output path based on product data"""
|
||||
if "productName" not in data:
|
||||
return
|
||||
|
||||
original_subset = tool.GetData("openpype.subset")
|
||||
original_product_name = tool.GetData("openpype.productName")
|
||||
original_format = tool.GetData(
|
||||
"openpype.creator_attributes.image_format"
|
||||
)
|
||||
|
||||
subset = data["subset"]
|
||||
product_name = data["productName"]
|
||||
if (
|
||||
original_subset != subset
|
||||
original_product_name != product_name
|
||||
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)
|
||||
|
||||
# get frame padding from anatomy templates
|
||||
|
|
@ -137,25 +139,39 @@ class GenericCreateSaver(Creator):
|
|||
ext = data["creator_attributes"]["image_format"]
|
||||
|
||||
# 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"))
|
||||
formatting_data.update({
|
||||
"workdir": workdir,
|
||||
"frame": "0" * frame_padding,
|
||||
"ext": ext,
|
||||
"product": {
|
||||
"name": formatting_data["subset"],
|
||||
"type": formatting_data["family"],
|
||||
"name": f_product_name,
|
||||
"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
|
||||
# TODO make sure the keys are available in 'formatting_data'
|
||||
temp_rendering_path_template = (
|
||||
self.temp_rendering_path_template
|
||||
.replace("{product[name]}", "{subset}")
|
||||
.replace("{product[type]}", "{family}")
|
||||
.replace("{folder[name]}", "{asset}")
|
||||
.replace("{task[name]}", "{task}")
|
||||
.replace("{task}", "{task[name]}")
|
||||
)
|
||||
|
||||
filepath = temp_rendering_path_template.format(**formatting_data)
|
||||
|
|
@ -164,9 +180,9 @@ class GenericCreateSaver(Creator):
|
|||
tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath))
|
||||
|
||||
# Rename tool
|
||||
if tool.Name != subset:
|
||||
print(f"Renaming {tool.Name} -> {subset}")
|
||||
tool.SetAttrs({"TOOLS_Name": subset})
|
||||
if tool.Name != product_name:
|
||||
print(f"Renaming {tool.Name} -> {product_name}")
|
||||
tool.SetAttrs({"TOOLS_Name": product_name})
|
||||
|
||||
def get_managed_tool_data(self, tool):
|
||||
"""Return data of the tool if it matches creator identifier"""
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class CreateImageSaver(GenericCreateSaver):
|
|||
identifier = "io.openpype.creators.fusion.imagesaver"
|
||||
label = "Image (saver)"
|
||||
name = "image"
|
||||
family = "image"
|
||||
product_type = "image"
|
||||
description = "Fusion Saver to generate image"
|
||||
|
||||
default_frame = 0
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class CreateSaver(GenericCreateSaver):
|
|||
identifier = "io.openpype.creators.fusion.saver"
|
||||
label = "Render (saver)"
|
||||
name = "render"
|
||||
family = "render"
|
||||
product_type = "render"
|
||||
description = "Fusion Saver to generate image sequence"
|
||||
|
||||
default_frame_range_option = "asset_db"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from ayon_core.pipeline import (
|
|||
|
||||
class FusionWorkfileCreator(AutoCreator):
|
||||
identifier = "workfile"
|
||||
family = "workfile"
|
||||
product_type = "workfile"
|
||||
label = "Workfile"
|
||||
icon = "fa5.file"
|
||||
|
||||
|
|
@ -27,9 +27,12 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
if not data:
|
||||
return
|
||||
|
||||
product_name = data.get("productName")
|
||||
if product_name is None:
|
||||
product_name = data["subset"]
|
||||
instance = CreatedInstance(
|
||||
family=self.family,
|
||||
subset_name=data["subset"],
|
||||
product_type=self.product_type,
|
||||
product_name=product_name,
|
||||
data=data,
|
||||
creator=self
|
||||
)
|
||||
|
|
@ -59,7 +62,7 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
|
||||
existing_instance = None
|
||||
for instance in self.create_context.instances:
|
||||
if instance.family == self.family:
|
||||
if instance.product_type == self.product_type:
|
||||
existing_instance = instance
|
||||
break
|
||||
|
||||
|
|
@ -75,9 +78,12 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
|
||||
if existing_instance is None:
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name
|
||||
product_name = self.get_product_name(
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
self.default_variant,
|
||||
host_name,
|
||||
)
|
||||
data = {
|
||||
"folderPath": asset_name,
|
||||
|
|
@ -85,12 +91,17 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
"variant": self.default_variant,
|
||||
}
|
||||
data.update(self.get_dynamic_data(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name, None
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
self.default_variant,
|
||||
host_name,
|
||||
None
|
||||
|
||||
))
|
||||
|
||||
new_instance = CreatedInstance(
|
||||
self.family, subset_name, data, self
|
||||
self.product_type, product_name, data, self
|
||||
)
|
||||
new_instance.transient_data["comp"] = comp
|
||||
self._add_instance_to_context(new_instance)
|
||||
|
|
@ -100,10 +111,13 @@ class FusionWorkfileCreator(AutoCreator):
|
|||
or existing_instance["task"] != task_name
|
||||
):
|
||||
asset_doc = get_asset_by_name(project_name, asset_name)
|
||||
subset_name = self.get_subset_name(
|
||||
self.default_variant, task_name, asset_doc,
|
||||
project_name, host_name
|
||||
product_name = self.get_product_name(
|
||||
project_name,
|
||||
asset_doc,
|
||||
task_name,
|
||||
self.default_variant,
|
||||
host_name,
|
||||
)
|
||||
existing_instance["folderPath"] = asset_name
|
||||
existing_instance["task"] = task_name
|
||||
existing_instance["subset"] = subset_name
|
||||
existing_instance["productName"] = product_name
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
|
|||
instance.data["frame_range_source"] = frame_range_source
|
||||
|
||||
# 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"]
|
||||
end = context.data["frameEnd"]
|
||||
handle_start = context.data["handleStart"]
|
||||
|
|
@ -34,7 +34,7 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
|
|||
start_with_handle = start - handle_start
|
||||
end_with_handle = end + handle_end
|
||||
|
||||
# conditions for render family instances
|
||||
# conditions for render product type instances
|
||||
if frame_range_source == "render_range":
|
||||
# set comp render frame ranges
|
||||
start = context.data["renderFrameStart"]
|
||||
|
|
@ -70,11 +70,11 @@ class CollectInstanceData(pyblish.api.InstancePlugin):
|
|||
end_with_handle = frame
|
||||
|
||||
# Include start and end render frame in label
|
||||
subset = instance.data["subset"]
|
||||
product_name = instance.data["productName"]
|
||||
label = (
|
||||
"{subset} ({start}-{end}) [{handle_start}-{handle_end}]"
|
||||
"{product_name} ({start}-{end}) [{handle_start}-{handle_end}]"
|
||||
).format(
|
||||
subset=subset,
|
||||
product_name=product_name,
|
||||
start=int(start),
|
||||
end=int(end),
|
||||
handle_start=int(handle_start),
|
||||
|
|
|
|||
|
|
@ -49,31 +49,32 @@ class CollectFusionRender(
|
|||
if not inst.data.get("active", True):
|
||||
continue
|
||||
|
||||
family = inst.data["family"]
|
||||
if family not in ["render", "image"]:
|
||||
product_type = inst.data["productType"]
|
||||
if product_type not in ["render", "image"]:
|
||||
continue
|
||||
|
||||
task_name = context.data["task"]
|
||||
tool = inst.data["transientData"]["tool"]
|
||||
|
||||
instance_families = inst.data.get("families", [])
|
||||
subset_name = inst.data["subset"]
|
||||
product_name = inst.data["productName"]
|
||||
instance = FusionRenderInstance(
|
||||
family=family,
|
||||
tool=tool,
|
||||
workfileComp=comp,
|
||||
productType=product_type,
|
||||
family=product_type,
|
||||
families=instance_families,
|
||||
version=version,
|
||||
time="",
|
||||
source=current_file,
|
||||
label=inst.data["label"],
|
||||
subset=subset_name,
|
||||
productName=product_name,
|
||||
folderPath=inst.data["folderPath"],
|
||||
task=task_name,
|
||||
attachTo=False,
|
||||
setMembers='',
|
||||
publish=True,
|
||||
name=subset_name,
|
||||
name=product_name,
|
||||
resolutionWidth=comp_frame_format_prefs.get("Width"),
|
||||
resolutionHeight=comp_frame_format_prefs.get("Height"),
|
||||
pixelAspect=aspect_x / aspect_y,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from ayon_core.hosts.fusion.api.action import SelectInvalidAction
|
|||
|
||||
|
||||
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
|
||||
label = "Validate Unique Subsets"
|
||||
|
|
@ -18,27 +18,31 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
|
|||
@classmethod
|
||||
def get_invalid(cls, context):
|
||||
|
||||
# Collect instances per subset per asset
|
||||
instances_per_subset_asset = defaultdict(lambda: defaultdict(list))
|
||||
# Collect instances per product per folder
|
||||
instances_per_product_folder = defaultdict(lambda: defaultdict(list))
|
||||
for instance in context:
|
||||
asset = instance.data.get(
|
||||
"folderPath", context.data.get("folderPath")
|
||||
folder_path = instance.data["folderPath"]
|
||||
product_name = instance.data["productName"]
|
||||
instances_per_product_folder[folder_path][product_name].append(
|
||||
instance
|
||||
)
|
||||
subset = instance.data.get("subset", context.data.get("subset"))
|
||||
instances_per_subset_asset[asset][subset].append(instance)
|
||||
|
||||
# Find which asset + subset combination has more than one instance
|
||||
# Those are considered invalid because they'd integrate to the same
|
||||
# destination.
|
||||
invalid = []
|
||||
for asset, instances_per_subset in instances_per_subset_asset.items():
|
||||
for subset, instances in instances_per_subset.items():
|
||||
for folder_path, instances_per_product in (
|
||||
instances_per_product_folder.items()
|
||||
):
|
||||
for product_name, instances in instances_per_product.items():
|
||||
if len(instances) > 1:
|
||||
cls.log.warning(
|
||||
"{asset} > {subset} used by more than "
|
||||
"one instance: {instances}".format(
|
||||
asset=asset,
|
||||
subset=subset,
|
||||
(
|
||||
"{folder_path} > {product_name} used by more than "
|
||||
"one instance: {instances}"
|
||||
).format(
|
||||
folder_path=folder_path,
|
||||
product_name=product_name,
|
||||
instances=instances
|
||||
)
|
||||
)
|
||||
|
|
@ -52,6 +56,7 @@ class ValidateUniqueSubsets(pyblish.api.ContextPlugin):
|
|||
def process(self, context):
|
||||
invalid = self.get_invalid(context)
|
||||
if invalid:
|
||||
raise PublishValidationError("Multiple instances are set to "
|
||||
"the same asset > subset.",
|
||||
title=self.label)
|
||||
raise PublishValidationError(
|
||||
"Multiple instances are set to the same folder > product.",
|
||||
title=self.label
|
||||
)
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ class CreateComposite(harmony.Creator):
|
|||
|
||||
name = "compositeDefault"
|
||||
label = "Composite"
|
||||
family = "mindbender.template"
|
||||
product_type = "mindbender.template"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateComposite, self).__init__(*args, **kwargs)
|
||||
|
|
@ -221,7 +221,7 @@ class CreateRender(harmony.Creator):
|
|||
|
||||
name = "writeDefault"
|
||||
label = "Write"
|
||||
family = "mindbender.imagesequence"
|
||||
product_type = "mindbender.imagesequence"
|
||||
node_type = "WRITE"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ ImageSequenceLoader.getUniqueColumnName = function(columnPrefix) {
|
|||
* var args = [
|
||||
* files, // Files in file sequences.
|
||||
* asset, // Asset name.
|
||||
* subset, // Subset name.
|
||||
* productName, // Product name.
|
||||
* startFrame, // Sequence starting frame.
|
||||
* groupId // Unique group ID (uuid4).
|
||||
* ];
|
||||
|
|
@ -106,7 +106,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) {
|
|||
var doc = $.scn;
|
||||
var files = args[0];
|
||||
var asset = args[1];
|
||||
var subset = args[2];
|
||||
var productName = args[2];
|
||||
var startFrame = args[3];
|
||||
var groupId = args[4];
|
||||
var vectorFormat = null;
|
||||
|
|
@ -124,7 +124,7 @@ ImageSequenceLoader.prototype.importFiles = function(args) {
|
|||
var num = 0;
|
||||
var name = '';
|
||||
do {
|
||||
name = asset + '_' + (num++) + '_' + subset;
|
||||
name = asset + '_' + (num++) + '_' + productName;
|
||||
} while (currentGroup.getNodeByName(name) != null);
|
||||
|
||||
extension = filename.substr(pos+1).toLowerCase();
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ var TemplateLoader = function() {};
|
|||
* var args = [
|
||||
* templatePath, // Path to tpl file.
|
||||
* assetName, // Asset name.
|
||||
* subsetName, // Subset name.
|
||||
* productName, // Product name.
|
||||
* groupId // unique ID (uuid4)
|
||||
* ];
|
||||
*/
|
||||
|
|
@ -39,7 +39,7 @@ TemplateLoader.prototype.loadContainer = function(args) {
|
|||
var doc = $.scn;
|
||||
var templatePath = args[0];
|
||||
var assetName = args[1];
|
||||
var subset = args[2];
|
||||
var productName = args[2];
|
||||
var groupId = args[3];
|
||||
|
||||
// Get the current group
|
||||
|
|
@ -62,7 +62,7 @@ TemplateLoader.prototype.loadContainer = function(args) {
|
|||
var num = 0;
|
||||
var containerGroupName = '';
|
||||
do {
|
||||
containerGroupName = assetName + '_' + (num++) + '_' + subset;
|
||||
containerGroupName = assetName + '_' + (num++) + '_' + productName;
|
||||
} while (currentGroup.getNodeByName(containerGroupName) != null);
|
||||
|
||||
// import the template
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class CreateFarmRender(plugin.Creator):
|
|||
|
||||
name = "renderDefault"
|
||||
label = "Render on Farm"
|
||||
family = "renderFarm"
|
||||
product_type = "renderFarm"
|
||||
node_type = "WRITE"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class CreateRender(plugin.Creator):
|
|||
|
||||
name = "renderDefault"
|
||||
label = "Render"
|
||||
family = "render"
|
||||
product_type = "render"
|
||||
node_type = "WRITE"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ class CreateTemplate(plugin.Creator):
|
|||
|
||||
name = "templateDefault"
|
||||
label = "Template"
|
||||
family = "harmony.template"
|
||||
product_type = "harmony.template"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CreateTemplate, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ class ImportAudioLoader(load.LoaderPlugin):
|
|||
{"function": func, "args": [context["subset"]["name"], wav_file]}
|
||||
)
|
||||
|
||||
subset_name = context["subset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
return harmony.containerise(
|
||||
subset_name,
|
||||
product_name,
|
||||
namespace,
|
||||
subset_name,
|
||||
product_name,
|
||||
context,
|
||||
self.__class__.__name__
|
||||
)
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ class BackgroundLoader(load.LoaderPlugin):
|
|||
|
||||
bg_folder = os.path.dirname(path)
|
||||
|
||||
subset_name = context["subset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
# read_node_name += "_{}".format(uuid.uuid4())
|
||||
container_nodes = []
|
||||
|
||||
|
|
@ -272,9 +272,9 @@ class BackgroundLoader(load.LoaderPlugin):
|
|||
container_nodes.append(read_node)
|
||||
|
||||
return harmony.containerise(
|
||||
subset_name,
|
||||
product_name,
|
||||
namespace,
|
||||
subset_name,
|
||||
product_name,
|
||||
context,
|
||||
self.__class__.__name__,
|
||||
nodes=container_nodes
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
|||
files.append(fname.parent.joinpath(remainder[0]).as_posix())
|
||||
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
group_id = str(uuid.uuid4())
|
||||
read_node = harmony.send(
|
||||
|
|
@ -56,7 +56,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
|||
"args": [
|
||||
files,
|
||||
asset,
|
||||
subset,
|
||||
product_name,
|
||||
1,
|
||||
group_id
|
||||
]
|
||||
|
|
@ -64,7 +64,7 @@ class ImageSequenceLoader(load.LoaderPlugin):
|
|||
)["result"]
|
||||
|
||||
return harmony.containerise(
|
||||
f"{asset}_{subset}",
|
||||
f"{asset}_{product_name}",
|
||||
namespace,
|
||||
read_node,
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ class ImportPaletteLoader(load.LoaderPlugin):
|
|||
)
|
||||
|
||||
def load_palette(self, representation):
|
||||
subset_name = representation["context"]["subset"]
|
||||
name = subset_name.replace("palette", "")
|
||||
product_name = representation["context"]["subset"]
|
||||
name = product_name.replace("palette", "")
|
||||
|
||||
# Overwrite palette on disk.
|
||||
scene_path = harmony.send(
|
||||
|
|
@ -44,7 +44,7 @@ class ImportPaletteLoader(load.LoaderPlugin):
|
|||
|
||||
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 += "Please save workfile when ready and use Workfiles "
|
||||
msg += "to reopen it."
|
||||
|
|
|
|||
|
|
@ -40,12 +40,12 @@ class ImportTemplateLoader(load.LoaderPlugin):
|
|||
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
subset_name = context["subset"]["name"]
|
||||
product_name = context["subset"]["name"]
|
||||
|
||||
return harmony.containerise(
|
||||
subset_name,
|
||||
product_name,
|
||||
namespace,
|
||||
subset_name,
|
||||
product_name,
|
||||
context,
|
||||
self.__class__.__name__
|
||||
)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
|||
for frame in range(start, end + 1):
|
||||
expected_files.append(
|
||||
path / "{}-{}.{}".format(
|
||||
render_instance.subset,
|
||||
render_instance.productName,
|
||||
str(frame).rjust(int(info[2]) + 1, "0"),
|
||||
ext
|
||||
)
|
||||
|
|
@ -89,7 +89,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
|||
return expected_files
|
||||
|
||||
def get_instances(self, context):
|
||||
"""Get instances per Write node in `renderFarm` family."""
|
||||
"""Get instances per Write node in `renderFarm` product type."""
|
||||
version = None
|
||||
if self.sync_workfile_version:
|
||||
version = context.data["version"]
|
||||
|
|
@ -111,7 +111,10 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
|||
if "container" in data["id"]:
|
||||
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
|
||||
|
||||
# 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: set Deadline stuff (pools, priority, etc. by presets)
|
||||
# because of using 'renderFarm' as a family, replace 'Farm' with
|
||||
# capitalized task name - issue of avalon-core Creator app
|
||||
subset_name = node.split("/")[1]
|
||||
task_name = context.data["anatomyData"]["task"][
|
||||
"name"].capitalize()
|
||||
# because of using 'renderFarm' as a product type, replace 'Farm'
|
||||
# with capitalized task name - issue of Creator tool
|
||||
product_name = node.split("/")[1]
|
||||
task_name = context.data["task"].capitalize()
|
||||
replace_str = ""
|
||||
if task_name.lower() not in subset_name.lower():
|
||||
if task_name.lower() not in product_name.lower():
|
||||
replace_str = task_name
|
||||
subset_name = subset_name.replace(
|
||||
product_name = product_name.replace(
|
||||
'Farm',
|
||||
replace_str)
|
||||
|
||||
|
|
@ -141,7 +143,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
|||
time=get_formatted_current_time(),
|
||||
source=context.data["currentFile"],
|
||||
label=node.split("/")[1],
|
||||
subset=subset_name,
|
||||
productName=product_name,
|
||||
folderPath=folder_path,
|
||||
task=task_name,
|
||||
attachTo=False,
|
||||
|
|
@ -151,6 +153,7 @@ class CollectFarmRender(publish.AbstractCollectRender):
|
|||
priority=50,
|
||||
name=node.split("/")[1],
|
||||
|
||||
productType="render.farm",
|
||||
family="render.farm",
|
||||
families=["render.farm"],
|
||||
farm=True,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
label = "Instances"
|
||||
order = pyblish.api.CollectorOrder
|
||||
hosts = ["harmony"]
|
||||
families_mapping = {
|
||||
product_type_mapping = {
|
||||
"render": ["review", "ftrack"],
|
||||
"harmony.template": [],
|
||||
"palette": ["palette", "ftrack"]
|
||||
|
|
@ -49,8 +49,14 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
if "container" in data["id"]:
|
||||
continue
|
||||
|
||||
# skip render farm family as it is collected separately
|
||||
if data["family"] == "renderFarm":
|
||||
product_type = data.get("productType")
|
||||
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
|
||||
|
||||
instance = context.create_instance(node.split("/")[-1])
|
||||
|
|
@ -59,11 +65,14 @@ class CollectInstances(pyblish.api.ContextPlugin):
|
|||
instance.data["publish"] = harmony.send(
|
||||
{"function": "node.getEnable", "args": [node]}
|
||||
)["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
|
||||
# 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
|
||||
|
||||
# Produce diagnostic message for any graphical
|
||||
|
|
|
|||
|
|
@ -33,14 +33,16 @@ class CollectPalettes(pyblish.api.ContextPlugin):
|
|||
return
|
||||
folder_path = context.data["folderPath"]
|
||||
|
||||
product_type = "harmony.palette"
|
||||
for name, id in palettes.items():
|
||||
instance = context.create_instance(name)
|
||||
instance.data.update({
|
||||
"id": id,
|
||||
"family": "harmony.palette",
|
||||
'families': [],
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type],
|
||||
"folderPath": folder_path,
|
||||
"subset": "{}{}".format("palette", name)
|
||||
"productName": "{}{}".format("palette", name)
|
||||
})
|
||||
self.log.info(
|
||||
"Created instance:\n" + json.dumps(
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.create import get_subset_name
|
||||
from ayon_core.pipeline.create import get_product_name
|
||||
|
||||
|
||||
class CollectWorkfile(pyblish.api.ContextPlugin):
|
||||
|
|
@ -15,26 +15,27 @@ class CollectWorkfile(pyblish.api.ContextPlugin):
|
|||
|
||||
def process(self, context):
|
||||
"""Plugin entry point."""
|
||||
family = "workfile"
|
||||
product_type = "workfile"
|
||||
basename = os.path.basename(context.data["currentFile"])
|
||||
subset = get_subset_name(
|
||||
family,
|
||||
"",
|
||||
context.data["anatomyData"]["task"]["name"],
|
||||
product_name = get_product_name(
|
||||
context.data["projectName"],
|
||||
context.data["assetEntity"],
|
||||
context.data["anatomyData"]["project"]["name"],
|
||||
host_name=context.data["hostName"],
|
||||
context.data["task"],
|
||||
context.data["hostName"],
|
||||
product_type,
|
||||
"",
|
||||
project_settings=context.data["project_settings"]
|
||||
)
|
||||
|
||||
# Create instance
|
||||
instance = context.create_instance(subset)
|
||||
instance = context.create_instance(product_name)
|
||||
instance.data.update({
|
||||
"subset": subset,
|
||||
"productName": product_name,
|
||||
"label": basename,
|
||||
"name": basename,
|
||||
"family": family,
|
||||
"families": [family],
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type],
|
||||
"representations": [],
|
||||
"folderPath": context.data["folderPath"]
|
||||
})
|
||||
|
|
|
|||
|
|
@ -75,7 +75,9 @@ class ExtractTemplate(publish.Extractor):
|
|||
instance.data["representations"] = [representation]
|
||||
|
||||
instance.data["version_name"] = "{}_{}".format(
|
||||
instance.data["subset"], instance.context.data["task"])
|
||||
instance.data["productName"],
|
||||
instance.context.data["task"]
|
||||
)
|
||||
|
||||
def get_backdrops(self, node: str) -> list:
|
||||
"""Get backdrops for the node.
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
<error id="main">
|
||||
<title>Subset context</title>
|
||||
<description>
|
||||
## Invalid subset context
|
||||
## Invalid product context
|
||||
|
||||
Asset name found '{found}' in subsets, expected '{expected}'.
|
||||
Asset name found '{found}' in products, expected '{expected}'.
|
||||
|
||||
### How to repair?
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ If this is unwanted, close workfile and open again, that way different asset val
|
|||
### __Detailed Info__ (optional)
|
||||
|
||||
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 "renderCompositingDefault" from asset "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>
|
||||
</error>
|
||||
</root>
|
||||
|
|
@ -589,8 +589,8 @@ def imprint(track_item, data=None):
|
|||
Examples:
|
||||
data = {
|
||||
'asset': 'sq020sh0280',
|
||||
'family': 'render',
|
||||
'subset': 'subsetMain'
|
||||
'productType': 'render',
|
||||
'productName': 'productMain'
|
||||
}
|
||||
"""
|
||||
data = data or {}
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ class ClipLoader:
|
|||
""" Gets context and convert it to self.data
|
||||
data structure:
|
||||
{
|
||||
"name": "assetName_subsetName_representationName"
|
||||
"name": "assetName_productName_representationName"
|
||||
"path": "path/to/file/created/by/get_repr..",
|
||||
"binPath": "projectBinPath",
|
||||
}
|
||||
|
|
@ -448,10 +448,10 @@ class ClipLoader:
|
|||
repr = self.context["representation"]
|
||||
repr_cntx = repr["context"]
|
||||
asset = str(repr_cntx["asset"])
|
||||
subset = str(repr_cntx["subset"])
|
||||
product_name = str(repr_cntx["subset"])
|
||||
representation = str(repr_cntx["representation"])
|
||||
self.data["clip_name"] = self.clip_name_template.format(**repr_cntx)
|
||||
self.data["track_name"] = "_".join([subset, representation])
|
||||
self.data["track_name"] = "_".join([product_name, representation])
|
||||
self.data["versionData"] = self.context["version"]["data"]
|
||||
# gets file path
|
||||
file = get_representation_path_from_context(self.context)
|
||||
|
|
@ -659,9 +659,9 @@ class PublishClip:
|
|||
rename_default = False
|
||||
hierarchy_default = "{_folder_}/{_sequence_}/{_track_}"
|
||||
clip_name_default = "shot_{_trackIndex_:0>3}_{_clipIndex_:0>4}"
|
||||
subset_name_default = "<track_name>"
|
||||
base_product_name_default = "<track_name>"
|
||||
review_track_default = "< none >"
|
||||
subset_family_default = "plate"
|
||||
product_type_default = "plate"
|
||||
count_from_default = 10
|
||||
count_steps_default = 10
|
||||
vertical_sync_default = False
|
||||
|
|
@ -785,10 +785,10 @@ class PublishClip:
|
|||
"countFrom", {}).get("value") or self.count_from_default
|
||||
self.count_steps = self.ui_inputs.get(
|
||||
"countSteps", {}).get("value") or self.count_steps_default
|
||||
self.subset_name = self.ui_inputs.get(
|
||||
"subsetName", {}).get("value") or self.subset_name_default
|
||||
self.subset_family = self.ui_inputs.get(
|
||||
"subsetFamily", {}).get("value") or self.subset_family_default
|
||||
self.base_product_name = self.ui_inputs.get(
|
||||
"productName", {}).get("value") or self.base_product_name_default
|
||||
self.product_type = self.ui_inputs.get(
|
||||
"productType", {}).get("value") or self.product_type_default
|
||||
self.vertical_sync = self.ui_inputs.get(
|
||||
"vSyncOn", {}).get("value") or self.vertical_sync_default
|
||||
self.driving_layer = self.ui_inputs.get(
|
||||
|
|
@ -798,12 +798,14 @@ class PublishClip:
|
|||
self.audio = self.ui_inputs.get(
|
||||
"audio", {}).get("value") or False
|
||||
|
||||
# build subset name from layer name
|
||||
if self.subset_name == "<track_name>":
|
||||
self.subset_name = self.track_name
|
||||
# build product name from layer name
|
||||
if self.base_product_name == "<track_name>":
|
||||
self.base_product_name = self.track_name
|
||||
|
||||
# create subset for publishing
|
||||
self.subset = self.subset_family + self.subset_name.capitalize()
|
||||
# create product for publishing
|
||||
self.product_name = (
|
||||
self.product_type + self.base_product_name.capitalize()
|
||||
)
|
||||
|
||||
def _replace_hash_to_expression(self, name, text):
|
||||
""" Replace hash with number in correct padding. """
|
||||
|
|
@ -885,14 +887,14 @@ class PublishClip:
|
|||
for (_in, _out), hero_data in self.vertical_clip_match.items():
|
||||
hero_data.update({"heroTrack": False})
|
||||
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
|
||||
if self.subset in data_subset:
|
||||
hero_data["subset"] = self.subset + str(
|
||||
if self.product_name in data_product_name:
|
||||
hero_data["productName"] = self.product_name + str(
|
||||
self.track_index)
|
||||
# in case track name and subset name is the same then add
|
||||
if self.subset_name == self.track_name:
|
||||
hero_data["subset"] = self.subset
|
||||
# in case track name and product name is the same then add
|
||||
if self.base_product_name == self.track_name:
|
||||
hero_data["productName"] = self.product_name
|
||||
# assign data to return hierarchy data to tag
|
||||
tag_hierarchy_data = hero_data
|
||||
|
||||
|
|
@ -913,9 +915,9 @@ class PublishClip:
|
|||
"hierarchy": hierarchy_filled,
|
||||
"parents": self.parents,
|
||||
"hierarchyData": hierarchy_formatting_data,
|
||||
"subset": self.subset,
|
||||
"family": self.subset_family,
|
||||
"families": [self.data["family"]]
|
||||
"productName": self.product_name,
|
||||
"productType": self.product_type,
|
||||
"families": [self.product_type, self.data["family"]]
|
||||
}
|
||||
|
||||
def _convert_to_entity(self, type, template):
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ def tag_data():
|
|||
# "note": "Collecting track items to Nuke scripts.",
|
||||
# "icon": "icons:TagNuke.png",
|
||||
# "metadata": {
|
||||
# "family": "nukescript",
|
||||
# "subset": "main"
|
||||
# "productType": "nukescript",
|
||||
# "productName": "main"
|
||||
# }
|
||||
# },
|
||||
"Comment": {
|
||||
|
|
@ -37,17 +37,17 @@ def tag_data():
|
|||
"note": "Comment on a shot.",
|
||||
"icon": "icons:TagComment.png",
|
||||
"metadata": {
|
||||
"family": "comment",
|
||||
"subset": "main"
|
||||
"productType": "comment",
|
||||
"productName": "main"
|
||||
}
|
||||
},
|
||||
"FrameMain": {
|
||||
"editable": "1",
|
||||
"note": "Publishing a frame subset.",
|
||||
"note": "Publishing a frame product.",
|
||||
"icon": "z_layer_main.png",
|
||||
"metadata": {
|
||||
"family": "frame",
|
||||
"subset": "main",
|
||||
"productType": "frame",
|
||||
"productName": "main",
|
||||
"format": "png"
|
||||
}
|
||||
}
|
||||
|
|
@ -153,7 +153,7 @@ def add_tags_to_workfile():
|
|||
"note": task_type,
|
||||
"icon": "icons:TagGood.png",
|
||||
"metadata": {
|
||||
"family": "task",
|
||||
"productType": "task",
|
||||
"type": task_type
|
||||
}
|
||||
}
|
||||
|
|
@ -173,7 +173,7 @@ def add_tags_to_workfile():
|
|||
"path": "icons:TagActor.png"
|
||||
},
|
||||
"metadata": {
|
||||
"family": "assetbuild"
|
||||
"productType": "assetbuild"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class CreateShotClip(phiero.Creator):
|
|||
"""Publishable clip"""
|
||||
|
||||
label = "Create Publishable Clip"
|
||||
family = "clip"
|
||||
product_type = "clip"
|
||||
icon = "film"
|
||||
defaults = ["Main"]
|
||||
|
||||
|
|
@ -133,19 +133,19 @@ class CreateShotClip(phiero.Creator):
|
|||
"target": "ui",
|
||||
"order": 3,
|
||||
"value": {
|
||||
"subsetName": {
|
||||
"productName": {
|
||||
"value": ["<track_name>", "main", "bg", "fg", "bg",
|
||||
"animatic"],
|
||||
"type": "QComboBox",
|
||||
"label": "Subset Name",
|
||||
"label": "pRODUCT Name",
|
||||
"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},
|
||||
"subsetFamily": {
|
||||
"productType": {
|
||||
"value": ["plate", "take"],
|
||||
"type": "QComboBox",
|
||||
"label": "Subset Family",
|
||||
"target": "ui", "toolTip": "What use of this subset is for", # noqa
|
||||
"label": "Product Type",
|
||||
"target": "ui", "toolTip": "What use of this product is for", # noqa
|
||||
"order": 1},
|
||||
"reviewTrack": {
|
||||
"value": ["< none >"] + gui_tracks,
|
||||
|
|
@ -159,7 +159,7 @@ class CreateShotClip(phiero.Creator):
|
|||
"type": "QCheckBox",
|
||||
"label": "Include audio",
|
||||
"target": "tag",
|
||||
"toolTip": "Process subsets with corresponding audio", # noqa
|
||||
"toolTip": "Process productS with corresponding audio", # noqa
|
||||
"order": 3},
|
||||
"sourceResolution": {
|
||||
"value": False,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import ayon_core.hosts.hiero.api as phiero
|
|||
|
||||
|
||||
class LoadClip(phiero.SequenceLoader):
|
||||
"""Load a subset to timeline as clip
|
||||
"""Load a product to timeline as clip
|
||||
|
||||
Place clip to timeline on its asset origin timings collected
|
||||
during conforming to project
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from itertools import product
|
||||
import re
|
||||
import pyblish.api
|
||||
|
||||
|
|
@ -12,13 +13,13 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
|
|||
effect_categories = []
|
||||
|
||||
def process(self, instance):
|
||||
family = "effect"
|
||||
product_type = "effect"
|
||||
effects = {}
|
||||
review = instance.data.get("review")
|
||||
review_track_index = instance.context.data.get("reviewTrackIndex")
|
||||
item = instance.data["item"]
|
||||
|
||||
if "audio" in instance.data["family"]:
|
||||
if "audio" in instance.data["productType"]:
|
||||
return
|
||||
|
||||
# frame range
|
||||
|
|
@ -61,16 +62,16 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
|
|||
if not effects:
|
||||
return
|
||||
|
||||
subset = instance.data.get("subset")
|
||||
effects.update({"assignTo": subset})
|
||||
product_name = instance.data.get("productName")
|
||||
effects.update({"assignTo": product_name})
|
||||
|
||||
subset_split = re.findall(r'[A-Z][^A-Z]*', subset)
|
||||
product_name_split = re.findall(r'[A-Z][^A-Z]*', product_name)
|
||||
|
||||
if len(subset_split) > 0:
|
||||
root_name = subset.replace(subset_split[0], "")
|
||||
subset_split.insert(0, root_name.capitalize())
|
||||
if len(product_name_split) > 0:
|
||||
root_name = product_name.replace(product_name_split[0], "")
|
||||
product_name_split.insert(0, root_name.capitalize())
|
||||
|
||||
subset_split.insert(0, "effect")
|
||||
product_name_split.insert(0, "effect")
|
||||
|
||||
effect_categories = {
|
||||
x["name"]: x["effect_classes"] for x in self.effect_categories
|
||||
|
|
@ -104,8 +105,8 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
|
|||
effects_categorized[category]["assignTo"] = effects["assignTo"]
|
||||
|
||||
for category, effects in effects_categorized.items():
|
||||
name = "".join(subset_split)
|
||||
name += category.capitalize()
|
||||
product_name = "".join(product_name_split)
|
||||
product_name += category.capitalize()
|
||||
|
||||
# create new instance and inherit data
|
||||
data = {}
|
||||
|
|
@ -114,15 +115,17 @@ class CollectClipEffects(pyblish.api.InstancePlugin):
|
|||
continue
|
||||
data[key] = value
|
||||
|
||||
# change names
|
||||
data["subset"] = name
|
||||
data["family"] = family
|
||||
data["families"] = [family]
|
||||
data["name"] = data["subset"] + "_" + data["folderPath"]
|
||||
data["label"] = "{} - {}".format(
|
||||
data["folderPath"], data["subset"]
|
||||
)
|
||||
data["effects"] = effects
|
||||
data.update({
|
||||
"productName": product_name,
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type],
|
||||
"name": product_name + "_" + data["folderPath"],
|
||||
"label": "{} - {}".format(
|
||||
data["folderPath"], product_name
|
||||
),
|
||||
"effects": effects,
|
||||
})
|
||||
|
||||
# create new instance
|
||||
_instance = instance.context.create_instance(**data)
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
|
||||
Tag is expected to have metadata:
|
||||
{
|
||||
"family": "frame"
|
||||
"subset": "main"
|
||||
"productType": "frame"
|
||||
"productName": "main"
|
||||
}
|
||||
"""
|
||||
|
||||
|
|
@ -26,14 +26,14 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
self._context = context
|
||||
|
||||
# collect all sequence tags
|
||||
subset_data = self._create_frame_subset_data_sequence(context)
|
||||
product_data = self._create_frame_product_data_sequence(context)
|
||||
|
||||
self.log.debug("__ subset_data: {}".format(
|
||||
pformat(subset_data)
|
||||
self.log.debug("__ product_data: {}".format(
|
||||
pformat(product_data)
|
||||
))
|
||||
|
||||
# create instances
|
||||
self._create_instances(subset_data)
|
||||
self._create_instances(product_data)
|
||||
|
||||
def _get_tag_data(self, tag):
|
||||
data = {}
|
||||
|
|
@ -66,7 +66,7 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
|
||||
return data
|
||||
|
||||
def _create_frame_subset_data_sequence(self, context):
|
||||
def _create_frame_product_data_sequence(self, context):
|
||||
|
||||
sequence_tags = []
|
||||
sequence = context.data["activeTimeline"]
|
||||
|
|
@ -87,10 +87,13 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
if not tag_data:
|
||||
continue
|
||||
|
||||
if "family" not in tag_data:
|
||||
product_type = tag_data.get("productType")
|
||||
if product_type is None:
|
||||
product_type = tag_data.get("family")
|
||||
if not product_type:
|
||||
continue
|
||||
|
||||
if tag_data["family"] != "frame":
|
||||
if product_type != "frame":
|
||||
continue
|
||||
|
||||
sequence_tags.append(tag_data)
|
||||
|
|
@ -99,8 +102,8 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
pformat(sequence_tags)
|
||||
))
|
||||
|
||||
# first collect all available subset tag frames
|
||||
subset_data = {}
|
||||
# first collect all available product tag frames
|
||||
product_data = {}
|
||||
context_asset_doc = context.data["assetEntity"]
|
||||
context_folder_path = get_asset_name_identifier(context_asset_doc)
|
||||
|
||||
|
|
@ -110,33 +113,37 @@ class CollectFrameTagInstances(pyblish.api.ContextPlugin):
|
|||
if frame not in publish_frames:
|
||||
continue
|
||||
|
||||
subset = tag_data["subset"]
|
||||
product_name = tag_data.get("productName")
|
||||
if product_name is None:
|
||||
product_name = tag_data["subset"]
|
||||
|
||||
if subset in subset_data:
|
||||
# update existing subset key
|
||||
subset_data[subset]["frames"].append(frame)
|
||||
if product_name in product_data:
|
||||
# update existing product key
|
||||
product_data[product_name]["frames"].append(frame)
|
||||
else:
|
||||
# create new subset key
|
||||
subset_data[subset] = {
|
||||
# create new product key
|
||||
product_data[product_name] = {
|
||||
"frames": [frame],
|
||||
"format": tag_data["format"],
|
||||
"folderPath": context_folder_path
|
||||
}
|
||||
return subset_data
|
||||
return product_data
|
||||
|
||||
def _create_instances(self, subset_data):
|
||||
# create instance per subset
|
||||
for subset_name, subset_data in subset_data.items():
|
||||
name = "frame" + subset_name.title()
|
||||
def _create_instances(self, product_data):
|
||||
# create instance per product
|
||||
product_type = "image"
|
||||
for product_name, product_data in product_data.items():
|
||||
name = "frame" + product_name.title()
|
||||
data = {
|
||||
"name": name,
|
||||
"label": "{} {}".format(name, subset_data["frames"]),
|
||||
"family": "image",
|
||||
"families": ["frame"],
|
||||
"folderPath": subset_data["folderPath"],
|
||||
"subset": name,
|
||||
"format": subset_data["format"],
|
||||
"frames": subset_data["frames"]
|
||||
"label": "{} {}".format(name, product_data["frames"]),
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type, "frame"],
|
||||
"folderPath": product_data["folderPath"],
|
||||
"productName": name,
|
||||
"format": product_data["format"],
|
||||
"frames": product_data["frames"]
|
||||
}
|
||||
self._context.create_instance(**data)
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,12 @@ class CollectClipTagTasks(api.InstancePlugin):
|
|||
tasks = {}
|
||||
for tag in tags:
|
||||
t_metadata = dict(tag.metadata())
|
||||
t_family = t_metadata.get("tag.family", "")
|
||||
t_product_type = t_metadata.get("tag.productType")
|
||||
if t_product_type is None:
|
||||
t_product_type = t_metadata.get("tag.family", "")
|
||||
|
||||
# gets only task family tags and collect labels
|
||||
if "task" in t_family:
|
||||
# gets only task product type tags and collect labels
|
||||
if "task" in t_product_type:
|
||||
t_task_name = t_metadata.get("tag.label", "")
|
||||
t_task_type = t_metadata.get("tag.type", "")
|
||||
tasks[t_task_name] = {"type": t_task_type}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ class ExtractClipEffects(publish.Extractor):
|
|||
if not effects:
|
||||
return
|
||||
|
||||
subset = instance.data.get("subset")
|
||||
family = instance.data["family"]
|
||||
product_name = instance.data.get("productName")
|
||||
product_type = instance.data["productType"]
|
||||
|
||||
self.log.debug("creating staging dir")
|
||||
staging_dir = self.staging_dir(instance)
|
||||
|
|
@ -32,7 +32,7 @@ class ExtractClipEffects(publish.Extractor):
|
|||
instance.data["transfers"] = list()
|
||||
|
||||
ext = "json"
|
||||
file = subset + "." + ext
|
||||
file = product_name + "." + ext
|
||||
|
||||
# when instance is created during collection part
|
||||
resources_dir = instance.data["resourcesDir"]
|
||||
|
|
@ -68,8 +68,10 @@ class ExtractClipEffects(publish.Extractor):
|
|||
version_data.update({
|
||||
"colorspace": item.sourceMediaColourTransform(),
|
||||
"colorspaceScript": instance.context.data["colorspace"],
|
||||
"families": [family, "plate"],
|
||||
"subset": subset,
|
||||
"families": [product_type, "plate"],
|
||||
# TODO find out if 'subset' is needed (and 'productName')
|
||||
"subset": product_name,
|
||||
"productName": product_name,
|
||||
"fps": instance.context.data["fps"]
|
||||
})
|
||||
instance.data["versionData"] = version_data
|
||||
|
|
@ -77,7 +79,7 @@ class ExtractClipEffects(publish.Extractor):
|
|||
representation = {
|
||||
'files': file,
|
||||
'stagingDir': staging_dir,
|
||||
'name': family + ext.title(),
|
||||
'name': product_type + ext.title(),
|
||||
'ext': ext
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
|
|
|
|||
|
|
@ -87,22 +87,47 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
|
||||
asset, asset_name = self._get_asset_data(tag_data)
|
||||
|
||||
subset = tag_data["subset"]
|
||||
product_name = tag_data.get("productName")
|
||||
if product_name is None:
|
||||
product_name = tag_data["subset"]
|
||||
|
||||
# insert family into families
|
||||
families = [str(f) for f in tag_data["families"]]
|
||||
|
||||
# form label
|
||||
label = "{} -".format(asset)
|
||||
if asset_name != clip_name:
|
||||
label += " ({})".format(clip_name)
|
||||
label += " {}".format(subset)
|
||||
label += " {}".format(product_name)
|
||||
|
||||
# TODO: remove backward compatibility
|
||||
product_name = tag_data.get("productName")
|
||||
if product_name is None:
|
||||
# backward compatibility: subset -> productName
|
||||
product_name = tag_data.get("subset")
|
||||
|
||||
# backward compatibility: product_name should not be missing
|
||||
if not product_name:
|
||||
self.log.error(
|
||||
"Product name is not defined for: {}".format(asset))
|
||||
|
||||
# TODO: remove backward compatibility
|
||||
product_type = tag_data.get("productType")
|
||||
if product_type is None:
|
||||
# backward compatibility: family -> productType
|
||||
product_type = tag_data.get("family")
|
||||
|
||||
# backward compatibility: product_type should not be missing
|
||||
if not product_type:
|
||||
self.log.error(
|
||||
"Product type is not defined for: {}".format(asset))
|
||||
|
||||
data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"name": "{}_{}".format(asset, product_name),
|
||||
"label": label,
|
||||
"folderPath": asset,
|
||||
"asset_name": asset_name,
|
||||
"productName": product_name,
|
||||
"productType": product_type,
|
||||
"item": track_item,
|
||||
"families": families,
|
||||
"publish": tag_data["publish"],
|
||||
|
|
@ -146,7 +171,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
if not with_audio:
|
||||
continue
|
||||
|
||||
# create audio subset instance
|
||||
# create audio product instance
|
||||
self.create_audio_instance(context, **data)
|
||||
|
||||
# add audioReview attribute to plate instance data
|
||||
|
|
@ -180,7 +205,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
})
|
||||
|
||||
def create_shot_instance(self, context, **data):
|
||||
subset = "shotMain"
|
||||
product_name = "shotMain"
|
||||
master_layer = data.get("heroTrack")
|
||||
hierarchy_data = data.get("hierarchyData")
|
||||
item = data.get("item")
|
||||
|
|
@ -195,21 +220,21 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
asset = data["folderPath"]
|
||||
asset_name = data["asset_name"]
|
||||
|
||||
# insert family into families
|
||||
family = "shot"
|
||||
product_type = "shot"
|
||||
|
||||
# form label
|
||||
label = "{} -".format(asset)
|
||||
if asset_name != clip_name:
|
||||
label += " ({}) ".format(clip_name)
|
||||
label += " {}".format(subset)
|
||||
label += " {}".format(product_name)
|
||||
|
||||
data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"name": "{}_{}".format(asset, product_name),
|
||||
"label": label,
|
||||
"subset": subset,
|
||||
"family": family,
|
||||
"families": []
|
||||
"productName": product_name,
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type]
|
||||
})
|
||||
|
||||
instance = context.create_instance(**data)
|
||||
|
|
@ -238,7 +263,7 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
return folder_path, asset_name
|
||||
|
||||
def create_audio_instance(self, context, **data):
|
||||
subset = "audioMain"
|
||||
product_name = "audioMain"
|
||||
master_layer = data.get("heroTrack")
|
||||
|
||||
if not master_layer:
|
||||
|
|
@ -254,21 +279,21 @@ class PrecollectInstances(pyblish.api.ContextPlugin):
|
|||
asset = data["folderPath"]
|
||||
asset_name = data["asset_name"]
|
||||
|
||||
# insert family into families
|
||||
family = "audio"
|
||||
product_type = "audio"
|
||||
|
||||
# form label
|
||||
label = "{} -".format(asset)
|
||||
if asset_name != clip_name:
|
||||
label += " ({}) ".format(clip_name)
|
||||
label += " {}".format(subset)
|
||||
label += " {}".format(product_name)
|
||||
|
||||
data.update({
|
||||
"name": "{}_{}".format(asset, subset),
|
||||
"name": "{}_{}".format(asset, product_name),
|
||||
"label": label,
|
||||
"subset": subset,
|
||||
"family": family,
|
||||
"families": ["clip"]
|
||||
"productName": product_name,
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type, "clip"]
|
||||
})
|
||||
# remove review track attr if any
|
||||
data.pop("reviewTrack")
|
||||
|
|
|
|||
|
|
@ -59,17 +59,20 @@ class PrecollectWorkfile(pyblish.api.ContextPlugin):
|
|||
'files': base_name,
|
||||
"stagingDir": staging_dir,
|
||||
}
|
||||
family = "workfile"
|
||||
product_type = "workfile"
|
||||
instance_data = {
|
||||
"label": "{} - {}Main".format(
|
||||
asset, family),
|
||||
"name": "{}_{}".format(asset_name, family),
|
||||
asset, product_type),
|
||||
"name": "{}_{}".format(asset_name, product_type),
|
||||
"folderPath": context.data["folderPath"],
|
||||
# TODO use 'get_subset_name'
|
||||
"subset": "{}{}Main".format(asset_name, family.capitalize()),
|
||||
# TODO use 'get_product_name'
|
||||
"productName": "{}{}Main".format(
|
||||
asset_name, product_type.capitalize()
|
||||
),
|
||||
"item": project,
|
||||
"family": family,
|
||||
"families": [],
|
||||
"productType": product_type,
|
||||
"family": product_type,
|
||||
"families": [product_type],
|
||||
"representations": [workfile_representation, thumb_representation]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class CollectAssetBuilds(api.ContextPlugin):
|
|||
|
||||
Tag is expected to have name of the asset and metadata:
|
||||
{
|
||||
"family": "assetbuild"
|
||||
"productType": "assetbuild"
|
||||
}
|
||||
"""
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ class CollectAssetBuilds(api.ContextPlugin):
|
|||
asset_builds[asset_name] = asset_doc
|
||||
|
||||
for instance in context:
|
||||
if instance.data["family"] != "clip":
|
||||
if instance.data["productType"] != "clip":
|
||||
continue
|
||||
|
||||
# Exclude non-tagged instances.
|
||||
|
|
@ -38,9 +38,11 @@ class CollectAssetBuilds(api.ContextPlugin):
|
|||
|
||||
for tag in instance.data["tags"]:
|
||||
t_metadata = dict(tag.metadata())
|
||||
t_family = t_metadata.get("tag.family", "")
|
||||
t_product_type = t_metadata.get("tag.productType")
|
||||
if t_product_type is None:
|
||||
t_product_type = t_metadata.get("tag.family", "")
|
||||
|
||||
if t_family.lower() == "assetbuild":
|
||||
if t_product_type.lower() == "assetbuild":
|
||||
asset_names.append(tag["name"])
|
||||
tagged = True
|
||||
|
||||
|
|
|
|||
|
|
@ -990,7 +990,7 @@ class FileDescriptor(DescriptorBase):
|
|||
:class:`descriptor_pb2.FileDescriptorProto`.
|
||||
dependencies (list[FileDescriptor]): List of other :class:`FileDescriptor`
|
||||
objects this :class:`FileDescriptor` depends on.
|
||||
public_dependencies (list[FileDescriptor]): A subset of
|
||||
public_dependencies (list[FileDescriptor]): A product of
|
||||
:attr:`dependencies`, which were declared as "public".
|
||||
message_types_by_name (dict(str, Descriptor)): Mapping from message names
|
||||
to their :class:`Descriptor`.
|
||||
|
|
|
|||
|
|
@ -90,21 +90,21 @@ def create_interactive(creator_identifier, **kwargs):
|
|||
pane = stateutils.activePane(kwargs)
|
||||
if isinstance(pane, hou.NetworkEditor):
|
||||
pwd = pane.pwd()
|
||||
subset_name = creator.get_subset_name(
|
||||
variant=variant,
|
||||
task_name=context.get_current_task_name(),
|
||||
product_name = creator.get_product_name(
|
||||
project_name=context.get_current_project_name(),
|
||||
asset_doc=get_asset_by_name(
|
||||
project_name=context.get_current_project_name(),
|
||||
asset_name=context.get_current_asset_name()
|
||||
),
|
||||
project_name=context.get_current_project_name(),
|
||||
host_name=context.host_name
|
||||
task_name=context.get_current_task_name(),
|
||||
variant=variant,
|
||||
host_name=context.host_name,
|
||||
)
|
||||
|
||||
tool_fn = CATEGORY_GENERIC_TOOL.get(pwd.childTypeCategory())
|
||||
if tool_fn is not None:
|
||||
out_null = tool_fn(kwargs, "null")
|
||||
out_null.setName("OUT_{}".format(subset_name), unique_name=True)
|
||||
out_null.setName("OUT_{}".format(product_name), unique_name=True)
|
||||
|
||||
before = context.instances_by_id.copy()
|
||||
|
||||
|
|
|
|||
|
|
@ -99,24 +99,21 @@ class Creator(LegacyCreator):
|
|||
|
||||
class HoudiniCreatorBase(object):
|
||||
@staticmethod
|
||||
def cache_subsets(shared_data):
|
||||
def cache_instance_data(shared_data):
|
||||
"""Cache instances for Creators to shared data.
|
||||
|
||||
Create `houdini_cached_subsets` key when needed in shared data and
|
||||
Create `houdini_cached_instances` key when needed in shared data and
|
||||
fill it with all collected instances from the scene under its
|
||||
respective creator identifiers.
|
||||
|
||||
Create `houdini_cached_legacy_subsets` key for any legacy instances
|
||||
Create `houdini_cached_legacy_instance` key for any legacy instances
|
||||
detected in the scene as instances per family.
|
||||
|
||||
Args:
|
||||
Dict[str, Any]: Shared data.
|
||||
|
||||
Return:
|
||||
Dict[str, Any]: Shared data dictionary.
|
||||
|
||||
"""
|
||||
if shared_data.get("houdini_cached_subsets") is None:
|
||||
if shared_data.get("houdini_cached_instances") is None:
|
||||
cache = dict()
|
||||
cache_legacy = dict()
|
||||
|
||||
|
|
@ -141,8 +138,8 @@ class HoudiniCreatorBase(object):
|
|||
family = family_parm.eval()
|
||||
cache_legacy.setdefault(family, []).append(node)
|
||||
|
||||
shared_data["houdini_cached_subsets"] = cache
|
||||
shared_data["houdini_cached_legacy_subsets"] = cache_legacy
|
||||
shared_data["houdini_cached_instances"] = cache
|
||||
shared_data["houdini_cached_legacy_instance"] = cache_legacy
|
||||
|
||||
return shared_data
|
||||
|
||||
|
|
@ -177,7 +174,7 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
|
|||
settings_name = None
|
||||
add_publish_button = False
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
def create(self, product_name, instance_data, pre_create_data):
|
||||
try:
|
||||
self.selected_nodes = []
|
||||
|
||||
|
|
@ -192,15 +189,15 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
|
|||
asset_name = instance_data["folderPath"]
|
||||
|
||||
instance_node = self.create_instance_node(
|
||||
asset_name, subset_name, "/out", node_type)
|
||||
asset_name, product_name, "/out", node_type)
|
||||
|
||||
self.customize_node_look(instance_node)
|
||||
|
||||
instance_data["instance_node"] = instance_node.path()
|
||||
instance_data["instance_id"] = instance_node.path()
|
||||
instance = CreatedInstance(
|
||||
self.family,
|
||||
subset_name,
|
||||
self.product_type,
|
||||
product_name,
|
||||
instance_data,
|
||||
self)
|
||||
self._add_instance_to_context(instance)
|
||||
|
|
@ -234,9 +231,9 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
|
|||
|
||||
def collect_instances(self):
|
||||
# cache instances if missing
|
||||
self.cache_subsets(self.collection_shared_data)
|
||||
self.cache_instance_data(self.collection_shared_data)
|
||||
for instance in self.collection_shared_data[
|
||||
"houdini_cached_subsets"].get(self.identifier, []):
|
||||
"houdini_cached_instances"].get(self.identifier, []):
|
||||
|
||||
node_data = read(instance)
|
||||
|
||||
|
|
@ -246,6 +243,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
|
|||
node_path = instance.path()
|
||||
node_data["instance_id"] = node_path
|
||||
node_data["instance_node"] = node_path
|
||||
if "AYON_productName" in node_data:
|
||||
node_data["productName"] = node_data.pop("AYON_productName")
|
||||
|
||||
created_instance = CreatedInstance.from_existing(
|
||||
node_data, self
|
||||
|
|
@ -269,6 +268,8 @@ class HoudiniCreator(NewCreator, HoudiniCreatorBase):
|
|||
def imprint(self, node, values, update=False):
|
||||
# Never store instance node and instance id since that data comes
|
||||
# from the node's path
|
||||
if "productName" in values:
|
||||
values["AYON_productName"] = values.pop("productName")
|
||||
values.pop("instance_node", None)
|
||||
values.pop("instance_id", None)
|
||||
imprint(node, values, update=update)
|
||||
|
|
|
|||
|
|
@ -1,24 +1,24 @@
|
|||
# -*- 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.hosts.houdini.api.lib import imprint
|
||||
|
||||
|
||||
class HoudiniLegacyConvertor(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
|
||||
transform them to the current system. Since the old subsets doesn't
|
||||
This Converter will find all legacy products in the scene and will
|
||||
transform them to the current system. Since the old products doesn't
|
||||
retain any information about their original creators, the only mapping
|
||||
we can do is based on their families.
|
||||
|
||||
Its limitation is that you can have multiple creators creating subset
|
||||
of the same family and there is no way to handle it. This code should
|
||||
nevertheless cover all creators that came with OpenPype.
|
||||
Its limitation is that you can have multiple creators creating product
|
||||
name of the same product type and there is no way to handle it. This code
|
||||
should nevertheless cover all creators that came with AYON.
|
||||
|
||||
"""
|
||||
identifier = "io.openpype.creators.houdini.legacy"
|
||||
family_to_id = {
|
||||
product_type_to_id = {
|
||||
"camera": "io.openpype.creators.houdini.camera",
|
||||
"ass": "io.openpype.creators.houdini.ass",
|
||||
"imagesequence": "io.openpype.creators.houdini.imagesequence",
|
||||
|
|
@ -33,44 +33,46 @@ class HoudiniLegacyConvertor(SubsetConvertorPlugin):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HoudiniLegacyConvertor, self).__init__(*args, **kwargs)
|
||||
self.legacy_subsets = {}
|
||||
self.legacy_instances = {}
|
||||
|
||||
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.
|
||||
|
||||
This is using cached entries done in
|
||||
:py:meth:`~HoudiniCreatorBase.cache_subsets()`
|
||||
:py:meth:`~HoudiniCreatorBase.cache_instance_data()`
|
||||
|
||||
"""
|
||||
self.legacy_subsets = self.collection_shared_data.get(
|
||||
"houdini_cached_legacy_subsets")
|
||||
if not self.legacy_subsets:
|
||||
self.legacy_instances = self.collection_shared_data.get(
|
||||
"houdini_cached_legacy_instance")
|
||||
if not self.legacy_instances:
|
||||
return
|
||||
self.add_convertor_item("Found {} incompatible subset{}.".format(
|
||||
len(self.legacy_subsets), "s" if len(self.legacy_subsets) > 1 else "")
|
||||
)
|
||||
self.add_convertor_item("Found {} incompatible product{}.".format(
|
||||
len(self.legacy_instances),
|
||||
"s" if len(self.legacy_instances) > 1 else ""
|
||||
))
|
||||
|
||||
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`.
|
||||
|
||||
"""
|
||||
if not self.legacy_subsets:
|
||||
if not self.legacy_instances:
|
||||
return
|
||||
|
||||
for family, subsets in self.legacy_subsets.items():
|
||||
if family in self.family_to_id:
|
||||
for subset in subsets:
|
||||
for product_type, legacy_instances in self.legacy_instances.items():
|
||||
if product_type in self.product_type_to_id:
|
||||
for instance in legacy_instances:
|
||||
creator_id = self.product_type_to_id[product_type]
|
||||
data = {
|
||||
"creator_identifier": self.family_to_id[family],
|
||||
"instance_node": subset.path()
|
||||
"creator_identifier": creator_id,
|
||||
"instance_node": instance.path()
|
||||
}
|
||||
if family == "pointcache":
|
||||
if product_type == "pointcache":
|
||||
data["families"] = ["abc"]
|
||||
self.log.info("Converting {} to {}".format(
|
||||
subset.path(), self.family_to_id[family]))
|
||||
imprint(subset, data)
|
||||
instance.path(), creator_id))
|
||||
imprint(instance, data)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Creator plugin for creating alembic camera subsets."""
|
||||
"""Creator plugin for creating alembic camera products."""
|
||||
from ayon_core.hosts.houdini.api import plugin
|
||||
from ayon_core.pipeline import CreatedInstance, CreatorError
|
||||
|
||||
|
|
@ -11,24 +11,24 @@ class CreateAlembicCamera(plugin.HoudiniCreator):
|
|||
|
||||
identifier = "io.openpype.creators.houdini.camera"
|
||||
label = "Camera (Abc)"
|
||||
family = "camera"
|
||||
product_type = "camera"
|
||||
icon = "camera"
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
def create(self, product_name, instance_data, pre_create_data):
|
||||
import hou
|
||||
|
||||
instance_data.pop("active", None)
|
||||
instance_data.update({"node_type": "alembic"})
|
||||
|
||||
instance = super(CreateAlembicCamera, self).create(
|
||||
subset_name,
|
||||
product_name,
|
||||
instance_data,
|
||||
pre_create_data) # type: CreatedInstance
|
||||
|
||||
instance_node = hou.node(instance.get("instance_node"))
|
||||
parms = {
|
||||
"filename": hou.text.expandString(
|
||||
"$HIP/pyblish/{}.abc".format(subset_name)),
|
||||
"$HIP/pyblish/{}.abc".format(product_name)),
|
||||
"use_sop_path": False,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
|
|||
|
||||
identifier = "io.openpype.creators.houdini.ass"
|
||||
label = "Arnold ASS"
|
||||
family = "ass"
|
||||
product_type = "ass"
|
||||
icon = "magic"
|
||||
|
||||
# Default extension: `.ass` or `.ass.gz`
|
||||
|
|
@ -17,7 +17,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
|
|||
# will override it by the value in the project settings
|
||||
ext = ".ass"
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
def create(self, product_name, instance_data, pre_create_data):
|
||||
import hou
|
||||
|
||||
instance_data.pop("active", None)
|
||||
|
|
@ -27,7 +27,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
|
|||
creator_attributes["farm"] = pre_create_data["farm"]
|
||||
|
||||
instance = super(CreateArnoldAss, self).create(
|
||||
subset_name,
|
||||
product_name,
|
||||
instance_data,
|
||||
pre_create_data) # type: plugin.CreatedInstance
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
|
|||
|
||||
filepath = "{}{}".format(
|
||||
hou.text.expandString("$HIP/pyblish/"),
|
||||
"{}.$F4{}".format(subset_name, self.ext)
|
||||
"{}.$F4{}".format(product_name, self.ext)
|
||||
)
|
||||
parms = {
|
||||
# Render frame range
|
||||
|
|
@ -54,7 +54,7 @@ class CreateArnoldAss(plugin.HoudiniCreator):
|
|||
instance_node.setParms(parms)
|
||||
|
||||
# Lock any parameters in this list
|
||||
to_lock = ["ar_ass_export_enable", "family", "id"]
|
||||
to_lock = ["ar_ass_export_enable", "productType", "id"]
|
||||
self.lock_parameters(instance_node, to_lock)
|
||||
|
||||
def get_instance_attr_defs(self):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
|
||||
identifier = "io.openpype.creators.houdini.arnold_rop"
|
||||
label = "Arnold ROP"
|
||||
family = "arnold_rop"
|
||||
product_type = "arnold_rop"
|
||||
icon = "magic"
|
||||
|
||||
# Default extension
|
||||
|
|
@ -16,7 +16,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
# Default to split export and render jobs
|
||||
export_job = True
|
||||
|
||||
def create(self, subset_name, instance_data, pre_create_data):
|
||||
def create(self, product_name, instance_data, pre_create_data):
|
||||
import hou
|
||||
|
||||
# Remove the active, we are checking the bypass flag of the nodes
|
||||
|
|
@ -29,7 +29,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
instance_data["farm"] = pre_create_data.get("farm")
|
||||
|
||||
instance = super(CreateArnoldRop, self).create(
|
||||
subset_name,
|
||||
product_name,
|
||||
instance_data,
|
||||
pre_create_data) # type: plugin.CreatedInstance
|
||||
|
||||
|
|
@ -37,9 +37,9 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
|
||||
ext = pre_create_data.get("image_format")
|
||||
|
||||
filepath = "{renders_dir}{subset_name}/{subset_name}.$F4.{ext}".format(
|
||||
filepath = "{renders_dir}{product_name}/{product_name}.$F4.{ext}".format(
|
||||
renders_dir=hou.text.expandString("$HIP/pyblish/renders/"),
|
||||
subset_name=subset_name,
|
||||
product_name=product_name,
|
||||
ext=ext,
|
||||
)
|
||||
parms = {
|
||||
|
|
@ -53,9 +53,9 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
|
||||
if pre_create_data.get("export_job"):
|
||||
ass_filepath = \
|
||||
"{export_dir}{subset_name}/{subset_name}.$F4.ass".format(
|
||||
"{export_dir}{product_name}/{product_name}.$F4.ass".format(
|
||||
export_dir=hou.text.expandString("$HIP/pyblish/ass/"),
|
||||
subset_name=subset_name,
|
||||
product_name=product_name,
|
||||
)
|
||||
parms["ar_ass_export_enable"] = 1
|
||||
parms["ar_ass_file"] = ass_filepath
|
||||
|
|
@ -63,7 +63,7 @@ class CreateArnoldRop(plugin.HoudiniCreator):
|
|||
instance_node.setParms(parms)
|
||||
|
||||
# Lock any parameters in this list
|
||||
to_lock = ["family", "id"]
|
||||
to_lock = ["productType", "id"]
|
||||
self.lock_parameters(instance_node, to_lock)
|
||||
|
||||
def get_pre_create_attr_defs(self):
|
||||
|
|
|
|||
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