Skip to content

Commit

Permalink
Refactor: make_container_publishable. (#47)
Browse files Browse the repository at this point in the history
- Created get_all_outliner_children to ease children/objects listing
- Moved presetting of create_openpype_instance from init to invoke for reliability
  • Loading branch information
Tilix4 committed May 5, 2023
1 parent e70912b commit 4720be1
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 32 deletions.
5 changes: 2 additions & 3 deletions openpype/hosts/blender/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from openpype.hosts.blender.api.utils import (
BL_OUTLINER_TYPES,
assign_loader_to_datablocks,
get_all_outliner_children,
get_instanced_collections,
)
from openpype.lib import Logger
Expand Down Expand Up @@ -131,9 +132,7 @@ def ls() -> Iterator:
# Add children and objects of outliner entities to skip
datablocks_to_skip.update(
chain.from_iterable(
list(d.children_recursive) + list(d.all_objects)
if hasattr(d, "all_objects")
else d.children_recursive
get_all_outliner_children(d)
for d in container_datablocks
if isinstance(d, tuple(BL_OUTLINER_TYPES))
)
Expand Down
108 changes: 79 additions & 29 deletions openpype/hosts/blender/api/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@
get_subset_by_id,
get_version_by_id,
)
from openpype.hosts.blender.api.lib import ls
from openpype.hosts.blender.api.lib import add_datablocks_to_container, ls
from openpype.hosts.blender.api.properties import OpenpypeContainer
from openpype.hosts.blender.api.utils import (
BL_OUTLINER_TYPES,
BL_TYPE_DATAPATH,
get_all_outliner_children,
link_to_collection,
)
from openpype.pipeline import legacy_io
from openpype.pipeline.constants import AVALON_INSTANCE_ID
Expand Down Expand Up @@ -517,8 +520,8 @@ class SCENE_OT_CreateOpenpypeInstance(
bl_label = "Create OpenPype Instance"
bl_options = {"REGISTER", "UNDO"}

def __init__(self):
super().__init__()
def invoke(self, context, _event):
wm = context.window_manager

# Set assets list
self.all_assets.clear()
Expand All @@ -530,8 +533,6 @@ def __init__(self):
# Setup all data
_update_entries_preset(self, bpy.context)

def invoke(self, context, _event):
wm = context.window_manager
return wm.invoke_props_dialog(self)

def draw(self, context):
Expand Down Expand Up @@ -949,36 +950,82 @@ class SCENE_OT_MakeContainerPublishable(bpy.types.Operator):
# NOTE cannot use AVALON_PROPERTY because of circular dependency
# and the refactor is very big, but must be done soon

@classmethod
def poll(cls, context):
# Check selected collection is in loaded containers
if context.collection is not context.scene.collection:
return context.collection.name in {
container["objectName"] for container in ls()
}
def invoke(self, context, _event):
wm = context.window_manager

# Prefill with outliner collection
matched_container = context.scene.openpype_containers.get(
context.collection.name
)
if matched_container:
self.container_name = matched_container.name

return wm.invoke_props_dialog(self)

def draw(self, context):
layout = self.layout

layout.prop_search(
self, "container_name", context.scene, "openpype_containers"
)

def execute(self, context):
if not self.container_name:
self.report({"WARNING"}, "No container to make publishable...")
return {"CANCELLED"}

# Recover required data
container_collection = bpy.data.collections.get(self.container_name)
avalon_data = container_collection["avalon"]
project_name = legacy_io.current_project()
version_doc = get_version_by_id(project_name, avalon_data["parent"])
subset_doc = get_subset_by_id(project_name, version_doc["parent"])

# Build and update metadata
metadata = {
"id": AVALON_INSTANCE_ID,
"family": avalon_data["family"],
"asset": avalon_data["asset_name"],
"subset": subset_doc["name"],
"task": legacy_io.Session.get("AVALON_TASK"),
"active": True,
}
container_collection["avalon"] = metadata
openpype_instances = context.scene.openpype_instances
openpype_containers = context.scene.openpype_containers
container = openpype_containers.get(self.container_name)
avalon_data = container["avalon"]

# Check not from library
if container.outliner_entity.override_library:
self.report(
{"WARNING"},
"Overriden entity cannot be converted to instance. Please switch to append.",
)
return {"CANCELLED"}

# Get creator name
for creator_name, creator_attrs in bpy.context.scene[
"openpype_creators"
].items():
if creator_attrs["family"] == avalon_data.get("family"):
break

# Create instance
bpy.ops.scene.create_openpype_instance(
creator_name=creator_name,
asset_name=avalon_data["asset_name"],
subset_name=avalon_data["name"],
datablock_name="",
gather_into_collection=isinstance(
container.outliner_entity, bpy.types.Collection
),
)

# Add datablocks to instance
new_instance = openpype_instances[-1]
container_outliner_children = get_all_outliner_children(
container.outliner_entity
)
add_datablocks_to_container(
{
d_ref.datablock
for d_ref in container.datablock_refs
if d_ref.datablock != container.outliner_entity
and not d_ref.datablock in container_outliner_children
},
new_instance,
)

# Remove container
openpype_containers.remove(
openpype_containers.find(self.container_name)
)

return {"FINISHED"}


Expand All @@ -990,7 +1037,10 @@ def draw_op_collection_menu(self, context):
"""
layout = self.layout
layout.separator()
op = layout.operator(

row = layout.row()
row.operator_context = "INVOKE_DEFAULT"
op = row.operator(
SCENE_OT_MakeContainerPublishable.bl_idname,
text=SCENE_OT_MakeContainerPublishable.bl_label,
)
Expand Down
23 changes: 23 additions & 0 deletions openpype/hosts/blender/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,29 @@ def get_children_recursive(
yield from get_children_recursive(child)


def get_all_outliner_children(
entity: Union[bpy.types.Collection, bpy.types.Object]
) -> Set[Union[bpy.types.Collection, bpy.types.Object]]:
"""Get all outliner children of an outliner entity.
For a Collection, it is both objects and children collections.
For an Object, only objects parented to the given one.
Args:
entity (Union[bpy.types.Collection, bpy.types.Object]): Outliner entity to get children from.
Returns:
Set[Union[bpy.types.Collection, bpy.types.Object]]: All outliner children.
"""
if not entity:
return set()

if hasattr(entity, "all_objects"):
return set(entity.children_recursive) | set(entity.all_objects)
else:
return set(entity.children_recursive)


def get_parent_collection(
entity: Union[bpy.types.Collection, bpy.types.Object],
) -> Optional[bpy.types.Collection]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def get_collections() -> Generator:
def process(self, context):
"""Collect instances from the current Blender scene."""
# Create instance from outliner datablocks
# TODO is it only conformation code and must be removed?
for c in [
collection
for collection in self.get_collections()
Expand Down

0 comments on commit 4720be1

Please sign in to comment.