Layout support for namespaces

This commit is contained in:
Simone Barbieri 2020-06-29 17:21:54 +01:00
parent a0096d6cdf
commit 8de72755c4
4 changed files with 156 additions and 96 deletions

View file

@ -7,20 +7,14 @@ from typing import Dict, List, Optional
from avalon import api, blender
import bpy
import pype.hosts.blender.plugin
import pype.hosts.blender.plugin as plugin
logger = logging.getLogger("pype").getChild(
"blender").getChild("load_layout")
class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
"""Load animations from a .blend file.
Warning:
Loading the same asset more then once is not properly supported at the
moment.
"""
class BlendLayoutLoader(plugin.AssetLoader):
"""Load layout from a .blend file."""
families = ["layout"]
representations = ["blend"]
@ -29,24 +23,25 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
icon = "code-fork"
color = "orange"
def _remove(self, objects, lib_container):
def _remove(self, objects, obj_container):
for obj in objects:
if obj.type == 'ARMATURE':
bpy.data.armatures.remove(obj.data)
elif obj.type == 'MESH':
bpy.data.meshes.remove(obj.data)
elif obj.type == 'CAMERA':
bpy.data.cameras.remove(obj.data)
elif obj.type == 'CURVE':
bpy.data.curves.remove(obj.data)
for element_container in bpy.data.collections[lib_container].children:
for element_container in obj_container.children:
for child in element_container.children:
bpy.data.collections.remove(child)
bpy.data.collections.remove(element_container)
bpy.data.collections.remove(bpy.data.collections[lib_container])
bpy.data.collections.remove(obj_container)
def _process(self, libpath, lib_container, container_name, actions):
relative = bpy.context.preferences.filepaths.use_relative_paths
with bpy.data.libraries.load(
libpath, link=True, relative=relative
@ -58,26 +53,38 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
scene.collection.children.link(bpy.data.collections[lib_container])
layout_container = scene.collection.children[lib_container].make_local()
layout_container.name = container_name
meshes = []
objects_local_types = ['MESH', 'CAMERA', 'CURVE']
objects = []
armatures = []
objects_list = []
containers = list(layout_container.children)
for element_container in layout_container.children:
element_container.make_local()
meshes.extend([obj for obj in element_container.objects if obj.type == 'MESH'])
armatures.extend([obj for obj in element_container.objects if obj.type == 'ARMATURE'])
for child in element_container.children:
child.make_local()
meshes.extend(child.objects)
for container in layout_container.children:
if container.name == blender.pipeline.AVALON_CONTAINERS:
containers.remove(container)
for container in containers:
container.make_local()
objects.extend([
obj for obj in container.objects
if obj.type in objects_local_types
])
armatures.extend([
obj for obj in container.objects
if obj.type == 'ARMATURE'
])
containers.extend(list(container.children))
# Link meshes first, then armatures.
# The armature is unparented for all the non-local meshes,
# when it is made local.
for obj in meshes + armatures:
obj = obj.make_local()
obj.data.make_local()
for obj in objects + armatures:
obj.make_local()
if obj.data:
obj.data.make_local()
if not obj.get(blender.pipeline.AVALON_PROPERTY):
obj[blender.pipeline.AVALON_PROPERTY] = dict()
@ -85,18 +92,16 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
avalon_info = obj[blender.pipeline.AVALON_PROPERTY]
avalon_info.update({"container_name": container_name})
action = actions.get( obj.name, None )
action = actions.get(obj.name, None)
if obj.type == 'ARMATURE' and action is not None:
obj.animation_data.action = action
objects_list.append(obj)
layout_container.pop(blender.pipeline.AVALON_PROPERTY)
bpy.ops.object.select_all(action='DESELECT')
return objects_list
return layout_container
def process_asset(
self, context: dict, name: str, namespace: Optional[str] = None,
@ -113,13 +118,17 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.hosts.blender.plugin.asset_name(asset, subset)
container_name = pype.hosts.blender.plugin.asset_name(
lib_container = plugin.asset_name(
asset, subset
)
namespace = namespace or plugin.asset_namespace(
asset, subset
)
container_name = plugin.asset_name(
asset, subset, namespace
)
container = bpy.data.collections.new(lib_container)
container.name = container_name
blender.pipeline.containerise_existing(
container,
name,
@ -134,11 +143,13 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
objects_list = self._process(
obj_container = self._process(
libpath, lib_container, container_name, {})
container_metadata["obj_container"] = obj_container
# Save the list of objects in the metadata container
container_metadata["objects"] = objects_list
container_metadata["objects"] = obj_container.all_objects
nodes = list(container.objects)
nodes.append(container)
@ -157,7 +168,6 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
Warning:
No nested collections are supported at the moment!
"""
collection = bpy.data.collections.get(
container["objectName"]
)
@ -189,8 +199,11 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
collection_libpath = collection_metadata["libpath"]
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
obj_container = collection_metadata["obj_container"]
normalized_collection_libpath = (
str(Path(bpy.path.abspath(collection_libpath)).resolve())
)
@ -206,24 +219,20 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
logger.info("Library already loaded, not updating...")
return
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
actions = {}
for obj in objects:
if obj.type == 'ARMATURE':
actions[obj.name] = obj.animation_data.action
self._remove(objects, lib_container)
self._remove(objects, obj_container)
objects_list = self._process(
obj_container = self._process(
str(libpath), lib_container, collection.name, actions)
# Save the list of objects in the metadata container
collection_metadata["objects"] = objects_list
collection_metadata["obj_container"] = obj_container
collection_metadata["objects"] = obj_container.all_objects
collection_metadata["libpath"] = str(libpath)
collection_metadata["representation"] = str(representation["_id"])
@ -255,9 +264,9 @@ class BlendLayoutLoader(pype.hosts.blender.plugin.AssetLoader):
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
obj_container = collection_metadata["obj_container"]
self._remove(objects, lib_container)
self._remove(objects, obj_container)
bpy.data.collections.remove(collection)

View file

@ -34,31 +34,32 @@ class BlendModelLoader(plugin.AssetLoader):
bpy.data.collections.remove(container)
def prepare_data(self, data, container_name):
name = data.name
data = data.make_local()
data.name = f"{name}:{container_name}"
def _process(self, libpath, lib_container, container_name):
def _process(
self, libpath, lib_container, container_name,
parent_collection
):
relative = bpy.context.preferences.filepaths.use_relative_paths
with bpy.data.libraries.load(
libpath, link=True, relative=relative
) as (_, data_to):
data_to.collections = [lib_container]
scene = bpy.context.scene
parent = parent_collection
scene.collection.children.link(bpy.data.collections[lib_container])
if parent is None:
parent = bpy.context.scene.collection
model_container = scene.collection.children[lib_container].make_local()
parent.children.link(bpy.data.collections[lib_container])
model_container = parent.children[lib_container].make_local()
model_container.name = container_name
for obj in model_container.objects:
self.prepare_data(obj, container_name)
self.prepare_data(obj.data, container_name)
plugin.prepare_data(obj, container_name)
plugin.prepare_data(obj.data, container_name)
for material_slot in obj.material_slots:
self.prepare_data(material_slot.material, container_name)
plugin.prepare_data(material_slot.material, container_name)
if not obj.get(blender.pipeline.AVALON_PROPERTY):
obj[blender.pipeline.AVALON_PROPERTY] = dict()
@ -114,7 +115,7 @@ class BlendModelLoader(plugin.AssetLoader):
container_metadata["lib_container"] = lib_container
obj_container = self._process(
libpath, lib_container, container_name)
libpath, lib_container, container_name, None)
container_metadata["obj_container"] = obj_container
@ -169,9 +170,16 @@ class BlendModelLoader(plugin.AssetLoader):
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
collection_libpath = collection_metadata["libpath"]
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
obj_container = collection_metadata["obj_container"]
obj_container = [
c for c in bpy.data.collections
if (c.name == collection_metadata["obj_container"].name and
c.library is None)
][0]
objects = obj_container.all_objects
container_name = obj_container.name
normalized_collection_libpath = (
str(Path(bpy.path.abspath(collection_libpath)).resolve())
@ -188,10 +196,12 @@ class BlendModelLoader(plugin.AssetLoader):
logger.info("Library already loaded, not updating...")
return
parent = plugin.get_parent_collection(obj_container)
self._remove(objects, obj_container)
obj_container = self._process(
str(libpath), lib_container, collection.name)
str(libpath), lib_container, container_name, parent)
# Save the list of objects in the metadata container
collection_metadata["obj_container"] = obj_container
@ -223,8 +233,13 @@ class BlendModelLoader(plugin.AssetLoader):
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
objects = collection_metadata["objects"]
obj_container = collection_metadata["obj_container"]
obj_container = [
c for c in bpy.data.collections
if (c.name == collection_metadata["obj_container"].name and
c.library is None)
][0]
objects = obj_container.all_objects
self._remove(objects, obj_container)

View file

@ -26,54 +26,54 @@ class BlendRigLoader(plugin.AssetLoader):
icon = "code-fork"
color = "orange"
def _remove(self, objects, lib_container):
def _remove(self, objects, obj_container):
for obj in objects:
if obj.type == 'ARMATURE':
bpy.data.armatures.remove(obj.data)
elif obj.type == 'MESH':
bpy.data.meshes.remove(obj.data)
for child in bpy.data.collections[lib_container].children:
for child in obj_container.children:
bpy.data.collections.remove(child)
bpy.data.collections.remove(bpy.data.collections[lib_container])
bpy.data.collections.remove(obj_container)
def prepare_data(self, data, container_name):
name = data.name
data = data.make_local()
data.name = f"{name}:{container_name}"
def _process(self, libpath, lib_container, container_name, action):
def _process(
self, libpath, lib_container, container_name,
action, parent_collection
):
relative = bpy.context.preferences.filepaths.use_relative_paths
with bpy.data.libraries.load(
libpath, link=True, relative=relative
) as (_, data_to):
data_to.collections = [lib_container]
scene = bpy.context.scene
parent = parent_collection
scene.collection.children.link(bpy.data.collections[lib_container])
if parent is None:
parent = bpy.context.scene.collection
parent.children.link(bpy.data.collections[lib_container])
rig_container = scene.collection.children[lib_container].make_local()
rig_container = parent.children[lib_container].make_local()
rig_container.name = container_name
meshes = []
armatures = [
obj for obj in rig_container.objects if obj.type == 'ARMATURE']
objects_list = []
obj for obj in rig_container.objects
if obj.type == 'ARMATURE'
]
for child in rig_container.children:
self.prepare_data(child, container_name)
meshes.extend( child.objects )
plugin.prepare_data(child, container_name)
meshes.extend(child.objects)
# Link meshes first, then armatures.
# The armature is unparented for all the non-local meshes,
# when it is made local.
for obj in meshes + armatures:
self.prepare_data(obj, container_name)
self.prepare_data(obj.data, container_name)
plugin.prepare_data(obj, container_name)
plugin.prepare_data(obj.data, container_name)
if not obj.get(blender.pipeline.AVALON_PROPERTY):
obj[blender.pipeline.AVALON_PROPERTY] = dict()
@ -131,7 +131,7 @@ class BlendRigLoader(plugin.AssetLoader):
container_metadata["lib_container"] = lib_container
obj_container = self._process(
libpath, lib_container, container_name, None)
libpath, lib_container, container_name, None, None)
container_metadata["obj_container"] = obj_container
@ -186,9 +186,16 @@ class BlendRigLoader(plugin.AssetLoader):
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
collection_libpath = collection_metadata["libpath"]
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
obj_container = collection_metadata["obj_container"]
obj_container = [
c for c in bpy.data.collections
if (c.name == collection_metadata["obj_container"].name and
c.library is None)
][0]
objects = obj_container.all_objects
container_name = obj_container.name
normalized_collection_libpath = (
str(Path(bpy.path.abspath(collection_libpath)).resolve())
@ -211,10 +218,12 @@ class BlendRigLoader(plugin.AssetLoader):
action = armatures[0].animation_data.action
parent = plugin.get_parent_collection(obj_container)
self._remove(objects, obj_container)
obj_container = self._process(
str(libpath), lib_container, collection.name, action)
str(libpath), lib_container, container_name, action, parent)
# Save the list of objects in the metadata container
collection_metadata["obj_container"] = obj_container
@ -249,8 +258,13 @@ class BlendRigLoader(plugin.AssetLoader):
collection_metadata = collection.get(
blender.pipeline.AVALON_PROPERTY)
objects = collection_metadata["objects"]
obj_container = collection_metadata["obj_container"]
obj_container = [
c for c in bpy.data.collections
if (c.name == collection_metadata["obj_container"].name and
c.library is None)
][0]
objects = obj_container.all_objects
self._remove(objects, obj_container)