blender is using product name and type

This commit is contained in:
Jakub Trllo 2024-02-23 11:16:04 +01:00
parent d19a9cc24c
commit 36129a12bd
38 changed files with 275 additions and 240 deletions

View file

@ -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,24 @@ 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.
Dict[str, Any]: Shared data with cached products.
"""
if not shared_data.get('blender_cached_subsets'):
if not shared_data.get('blender_cached_instances'):
cache = {}
cache_legacy = {}
@ -210,19 +210,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 +236,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 +247,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 +263,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 +306,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 +342,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 +359,7 @@ class BaseCreator(Creator):
{
"id": AVALON_INSTANCE_ID,
"creator_identifier": self.identifier,
"subset": subset_name,
"productName": product_name,
}
)
@ -466,14 +467,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 +500,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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,7 +43,7 @@ class CreateWorkfile(BaseCreator, AutoCreator):
if not workfile_instance:
asset_doc = get_asset_by_name(project_name, asset_name)
subset_name = self.get_subset_name(
product_name = self.get_product_name(
task_name, task_name, asset_doc, project_name, host_name
)
data = {
@ -63,7 +63,7 @@ class CreateWorkfile(BaseCreator, AutoCreator):
)
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 +73,13 @@ 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(
product_name = self.get_product_name(
task_name, task_name, asset_doc, project_name, 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:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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["prouctName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.abc"
filepath = os.path.join(stagingdir, filename)

View file

@ -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["prouctName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.blend"
filepath = os.path.join(stagingdir, filename)

View file

@ -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["prouctName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.blend"
filepath = os.path.join(stagingdir, filename)

View file

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

View file

@ -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["prouctName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.fbx"
filepath = os.path.join(stagingdir, filename)

View file

@ -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["prouctName"]
instance_name = f"{folder_name}_{product_name}"
filename = f"{instance_name}.fbx"
filepath = os.path.join(stagingdir, filename)

View file

@ -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["prouctName"]
instance_name = f"{folder_name}_{product_name}"
fbx_filename = f"{instance_name}_{armature.name}.fbx"
filepath = os.path.join(stagingdir, fbx_filename)

View file

@ -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["family"] = product_type
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)

View file

@ -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["prouctName"]
filename = f"{folder_name}_{product_name}"
path = os.path.join(stagingdir, filename)

View file

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

View file

@ -28,25 +28,27 @@ 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["_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)

View file

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