Bug fixing and code optimization

This commit is contained in:
Simone Barbieri 2020-03-11 12:39:25 +00:00
parent de972a2fa4
commit 5bf25ffd3e
5 changed files with 262 additions and 286 deletions

View file

@ -14,6 +14,23 @@ class CreateRig(Creator):
family = "rig"
icon = "wheelchair"
# @staticmethod
# def _find_layer_collection(self, layer_collection, collection):
# found = None
# if (layer_collection.collection == collection):
# return layer_collection
# for layer in layer_collection.children:
# found = self._find_layer_collection(layer, collection)
# if found:
# return found
def process(self):
import pype.blender
@ -25,8 +42,52 @@ class CreateRig(Creator):
self.data['task'] = api.Session.get('AVALON_TASK')
lib.imprint(collection, self.data)
# Add the rig object and all the children meshes to
# a set and link them all at the end to avoid duplicates.
# Blender crashes if trying to link an object that is already linked.
# This links automatically the children meshes if they were not
# selected, and doesn't link them twice if they, insted,
# were manually selected by the user.
objects_to_link = set()
if (self.options or {}).get("useSelection"):
for obj in lib.get_selection():
collection.objects.link(obj)
objects_to_link.add( obj )
if obj.type == 'ARMATURE':
for subobj in obj.children:
objects_to_link.add( subobj )
# Create a new collection and link the widgets that
# the rig uses.
# custom_shapes = set()
# for posebone in obj.pose.bones:
# if posebone.custom_shape is not None:
# custom_shapes.add( posebone.custom_shape )
# if len( custom_shapes ) > 0:
# widgets_collection = bpy.data.collections.new(name="Widgets")
# collection.children.link(widgets_collection)
# for custom_shape in custom_shapes:
# widgets_collection.objects.link( custom_shape )
# layer_collection = self._find_layer_collection(bpy.context.view_layer.layer_collection, widgets_collection)
# layer_collection.exclude = True
for obj in objects_to_link:
collection.objects.link(obj)
return collection

View file

@ -13,7 +13,7 @@ from avalon import api
logger = logging.getLogger("pype").getChild("blender").getChild("load_action")
class BlendAnimationLoader(pype.blender.AssetLoader):
class BlendActionLoader(pype.blender.AssetLoader):
"""Load action from a .blend file.
Warning:
@ -47,7 +47,6 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
container_name = pype.blender.plugin.asset_name(
asset, subset, namespace
)
relative = bpy.context.preferences.filepaths.use_relative_paths
container = bpy.data.collections.new(lib_container)
container.name = container_name
@ -65,6 +64,7 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
relative = bpy.context.preferences.filepaths.use_relative_paths
with bpy.data.libraries.load(
libpath, link=True, relative=relative
) as (_, data_to):
@ -85,8 +85,6 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
obj = obj.make_local()
# obj.data.make_local()
if obj.animation_data is not None and obj.animation_data.action is not None:
obj.animation_data.action.make_local()

View file

@ -28,43 +28,22 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
icon = "code-fork"
color = "orange"
def process_asset(
self, context: dict, name: str, namespace: Optional[str] = None,
options: Optional[Dict] = None
) -> Optional[List]:
"""
Arguments:
name: Use pre-defined name
namespace: Use pre-defined namespace
context: Full parenthood of representation to load
options: Additional settings dictionary
"""
@staticmethod
def _remove(self, objects, lib_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)
bpy.data.collections.remove(bpy.data.collections[lib_container])
@staticmethod
def _process(self, libpath, lib_container, container_name):
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.blender.plugin.asset_name(asset, subset)
container_name = pype.blender.plugin.asset_name(
asset, subset, namespace
)
relative = bpy.context.preferences.filepaths.use_relative_paths
container = bpy.data.collections.new(lib_container)
container.name = container_name
avalon.blender.pipeline.containerise_existing(
container,
name,
namespace,
context,
self.__class__.__name__,
)
container_metadata = container.get(
avalon.blender.pipeline.AVALON_PROPERTY)
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
with bpy.data.libraries.load(
libpath, link=True, relative=relative
) as (_, data_to):
@ -77,8 +56,9 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
animation_container = scene.collection.children[lib_container].make_local()
meshes = [obj for obj in animation_container.objects if obj.type == 'MESH']
armatures = [
obj for obj in animation_container.objects if obj.type == 'ARMATURE']
armatures = [obj for obj in animation_container.objects if obj.type == 'ARMATURE']
# Should check if there is only an armature?
objects_list = []
@ -106,11 +86,51 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
animation_container.pop( avalon.blender.pipeline.AVALON_PROPERTY )
bpy.ops.object.select_all(action='DESELECT')
return objects_list
def process_asset(
self, context: dict, name: str, namespace: Optional[str] = None,
options: Optional[Dict] = None
) -> Optional[List]:
"""
Arguments:
name: Use pre-defined name
namespace: Use pre-defined namespace
context: Full parenthood of representation to load
options: Additional settings dictionary
"""
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.blender.plugin.asset_name(asset, subset)
container_name = pype.blender.plugin.asset_name(
asset, subset, namespace
)
container = bpy.data.collections.new(lib_container)
container.name = container_name
avalon.blender.pipeline.containerise_existing(
container,
name,
namespace,
context,
self.__class__.__name__,
)
container_metadata = container.get(
avalon.blender.pipeline.AVALON_PROPERTY)
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
objects_list = self._process(self, libpath, lib_container, container_name)
# Save the list of objects in the metadata container
container_metadata["objects"] = objects_list
bpy.ops.object.select_all(action='DESELECT')
nodes = list(container.objects)
nodes.append(container)
self[:] = nodes
@ -177,59 +197,16 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
logger.info("Library already loaded, not updating...")
return
# Get the armature of the rig
armatures = [obj for obj in collection_metadata["objects"]
if obj.type == 'ARMATURE']
assert(len(armatures) == 1)
for obj in collection_metadata["objects"]:
if obj.type == 'ARMATURE':
bpy.data.armatures.remove(obj.data)
elif obj.type == 'MESH':
bpy.data.meshes.remove(obj.data)
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
bpy.data.collections.remove(bpy.data.collections[lib_container])
relative = bpy.context.preferences.filepaths.use_relative_paths
with bpy.data.libraries.load(
str(libpath), link=True, relative=relative
) as (_, data_to):
data_to.collections = [lib_container]
scene = bpy.context.scene
scene.collection.children.link(bpy.data.collections[lib_container])
animation_container = scene.collection.children[lib_container].make_local()
meshes = [obj for obj in animation_container.objects if obj.type == 'MESH']
armatures = [
obj for obj in animation_container.objects if obj.type == 'ARMATURE']
objects_list = []
# Get the armature of the rig
armatures = [obj for obj in objects if obj.type == 'ARMATURE']
assert(len(armatures) == 1)
# 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._remove(self, objects, lib_container)
obj = obj.make_local()
obj.data.make_local()
if not obj.get(avalon.blender.pipeline.AVALON_PROPERTY):
obj[avalon.blender.pipeline.AVALON_PROPERTY] = dict()
avalon_info = obj[avalon.blender.pipeline.AVALON_PROPERTY]
avalon_info.update({"container_name": collection.name})
objects_list.append(obj)
animation_container.pop( avalon.blender.pipeline.AVALON_PROPERTY )
objects_list = self._process(self, str(libpath), lib_container, collection.name)
# Save the list of objects in the metadata container
collection_metadata["objects"] = objects_list
@ -266,14 +243,8 @@ class BlendAnimationLoader(pype.blender.AssetLoader):
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_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)
bpy.data.collections.remove(bpy.data.collections[lib_container])
self._remove(self, objects, lib_container)
bpy.data.collections.remove(collection)
return True

View file

@ -31,43 +31,19 @@ class BlendModelLoader(pype.blender.AssetLoader):
icon = "code-fork"
color = "orange"
def process_asset(
self, context: dict, name: str, namespace: Optional[str] = None,
options: Optional[Dict] = None
) -> Optional[List]:
"""
Arguments:
name: Use pre-defined name
namespace: Use pre-defined namespace
context: Full parenthood of representation to load
options: Additional settings dictionary
"""
@staticmethod
def _remove(self, objects, lib_container):
for obj in objects:
bpy.data.meshes.remove(obj.data)
bpy.data.collections.remove(bpy.data.collections[lib_container])
@staticmethod
def _process(self, libpath, lib_container, container_name):
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.blender.plugin.asset_name(asset, subset)
container_name = pype.blender.plugin.asset_name(
asset, subset, namespace
)
relative = bpy.context.preferences.filepaths.use_relative_paths
container = bpy.data.collections.new(lib_container)
container.name = container_name
avalon.blender.pipeline.containerise_existing(
container,
name,
namespace,
context,
self.__class__.__name__,
)
container_metadata = container.get(
avalon.blender.pipeline.AVALON_PROPERTY)
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
with bpy.data.libraries.load(
libpath, link=True, relative=relative
) as (_, data_to):
@ -102,13 +78,53 @@ class BlendModelLoader(pype.blender.AssetLoader):
model_container.pop( avalon.blender.pipeline.AVALON_PROPERTY )
bpy.ops.object.select_all(action='DESELECT')
return objects_list
def process_asset(
self, context: dict, name: str, namespace: Optional[str] = None,
options: Optional[Dict] = None
) -> Optional[List]:
"""
Arguments:
name: Use pre-defined name
namespace: Use pre-defined namespace
context: Full parenthood of representation to load
options: Additional settings dictionary
"""
libpath = self.fname
asset = context["asset"]["name"]
subset = context["subset"]["name"]
lib_container = pype.blender.plugin.asset_name(asset, subset)
container_name = pype.blender.plugin.asset_name(
asset, subset, namespace
)
collection = bpy.data.collections.new(lib_container)
collection.name = container_name
avalon.blender.pipeline.containerise_existing(
collection,
name,
namespace,
context,
self.__class__.__name__,
)
container_metadata = collection.get(
avalon.blender.pipeline.AVALON_PROPERTY)
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
objects_list = self._process(self, libpath, lib_container, container_name)
# Save the list of objects in the metadata container
container_metadata["objects"] = objects_list
bpy.ops.object.select_all(action='DESELECT')
nodes = list(container.objects)
nodes.append(container)
nodes = list(collection.objects)
nodes.append(collection)
self[:] = nodes
return nodes
@ -154,8 +170,10 @@ class BlendModelLoader(pype.blender.AssetLoader):
collection_metadata = collection.get(
avalon.blender.pipeline.AVALON_PROPERTY)
collection_libpath = collection_metadata["libpath"]
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
normalized_collection_libpath = (
str(Path(bpy.path.abspath(collection_libpath)).resolve())
)
@ -171,54 +189,15 @@ class BlendModelLoader(pype.blender.AssetLoader):
logger.info("Library already loaded, not updating...")
return
for obj in collection_metadata["objects"]:
self._remove(self, objects, lib_container)
bpy.data.meshes.remove(obj.data)
lib_container = collection_metadata["lib_container"]
bpy.data.collections.remove(bpy.data.collections[lib_container])
relative = bpy.context.preferences.filepaths.use_relative_paths
with bpy.data.libraries.load(
str(libpath), link=True, relative=relative
) as (_, data_to):
data_to.collections = [lib_container]
scene = bpy.context.scene
scene.collection.children.link(bpy.data.collections[lib_container])
model_container = scene.collection.children[lib_container].make_local()
objects_list = []
# Link meshes first, then armatures.
# The armature is unparented for all the non-local meshes,
# when it is made local.
for obj in model_container.objects:
obj = obj.make_local()
obj.data.make_local()
if not obj.get(avalon.blender.pipeline.AVALON_PROPERTY):
obj[avalon.blender.pipeline.AVALON_PROPERTY] = dict()
avalon_info = obj[avalon.blender.pipeline.AVALON_PROPERTY]
avalon_info.update({"container_name": collection.name})
objects_list.append(obj)
model_container.pop( avalon.blender.pipeline.AVALON_PROPERTY )
objects_list = self._process(self, str(libpath), lib_container, collection.name)
# Save the list of objects in the metadata container
collection_metadata["objects"] = objects_list
collection_metadata["libpath"] = str(libpath)
collection_metadata["representation"] = str(representation["_id"])
bpy.ops.object.select_all(action='DESELECT')
def remove(self, container: Dict) -> bool:
"""Remove an existing container from a Blender scene.
@ -246,11 +225,8 @@ class BlendModelLoader(pype.blender.AssetLoader):
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
for obj in objects:
self._remove(self, objects, lib_container)
bpy.data.meshes.remove(obj.data)
bpy.data.collections.remove(bpy.data.collections[lib_container])
bpy.data.collections.remove(collection)
return True

View file

@ -30,6 +30,68 @@ class BlendRigLoader(pype.blender.AssetLoader):
label = "Link Rig"
icon = "code-fork"
color = "orange"
@staticmethod
def _remove(self, objects, lib_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)
bpy.data.collections.remove(bpy.data.collections[lib_container])
@staticmethod
def _process(self, libpath, lib_container, container_name, action):
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
scene.collection.children.link(bpy.data.collections[lib_container])
rig_container = scene.collection.children[lib_container].make_local()
meshes = [obj for obj in rig_container.objects if obj.type == 'MESH']
armatures = [obj for obj in rig_container.objects if obj.type == 'ARMATURE']
objects_list = []
assert(len(armatures) == 1)
# 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()
if not obj.get(avalon.blender.pipeline.AVALON_PROPERTY):
obj[avalon.blender.pipeline.AVALON_PROPERTY] = dict()
avalon_info = obj[avalon.blender.pipeline.AVALON_PROPERTY]
avalon_info.update({"container_name": container_name})
if obj.type == 'ARMATURE' and action is not None:
obj.animation_data.action = action
objects_list.append(obj)
rig_container.pop( avalon.blender.pipeline.AVALON_PROPERTY )
bpy.ops.object.select_all(action='DESELECT')
return objects_list
def process_asset(
self, context: dict, name: str, namespace: Optional[str] = None,
@ -50,7 +112,6 @@ class BlendRigLoader(pype.blender.AssetLoader):
container_name = pype.blender.plugin.asset_name(
asset, subset, namespace
)
relative = bpy.context.preferences.filepaths.use_relative_paths
container = bpy.data.collections.new(lib_container)
container.name = container_name
@ -68,48 +129,11 @@ class BlendRigLoader(pype.blender.AssetLoader):
container_metadata["libpath"] = libpath
container_metadata["lib_container"] = lib_container
with bpy.data.libraries.load(
libpath, link=True, relative=relative
) as (_, data_to):
data_to.collections = [lib_container]
scene = bpy.context.scene
scene.collection.children.link(bpy.data.collections[lib_container])
rig_container = scene.collection.children[lib_container].make_local()
meshes = [obj for obj in rig_container.objects if obj.type == 'MESH']
armatures = [
obj for obj in rig_container.objects if obj.type == 'ARMATURE']
objects_list = []
# 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()
if not obj.get(avalon.blender.pipeline.AVALON_PROPERTY):
obj[avalon.blender.pipeline.AVALON_PROPERTY] = dict()
avalon_info = obj[avalon.blender.pipeline.AVALON_PROPERTY]
avalon_info.update({"container_name": container_name})
objects_list.append(obj)
rig_container.pop( avalon.blender.pipeline.AVALON_PROPERTY )
objects_list = self._process(self, libpath, lib_container, container_name, None)
# Save the list of objects in the metadata container
container_metadata["objects"] = objects_list
bpy.ops.object.select_all(action='DESELECT')
nodes = list(container.objects)
nodes.append(container)
self[:] = nodes
@ -159,8 +183,10 @@ class BlendRigLoader(pype.blender.AssetLoader):
collection_metadata = collection.get(
avalon.blender.pipeline.AVALON_PROPERTY)
collection_libpath = collection_metadata["libpath"]
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_container"]
normalized_collection_libpath = (
str(Path(bpy.path.abspath(collection_libpath)).resolve())
)
@ -177,64 +203,14 @@ class BlendRigLoader(pype.blender.AssetLoader):
return
# Get the armature of the rig
armatures = [obj for obj in collection_metadata["objects"]
if obj.type == 'ARMATURE']
armatures = [obj for obj in objects if obj.type == 'ARMATURE']
assert(len(armatures) == 1)
action = armatures[0].animation_data.action
for obj in collection_metadata["objects"]:
self._remove(self, objects, lib_container)
if obj.type == 'ARMATURE':
bpy.data.armatures.remove(obj.data)
elif obj.type == 'MESH':
bpy.data.meshes.remove(obj.data)
lib_container = collection_metadata["lib_container"]
bpy.data.collections.remove(bpy.data.collections[lib_container])
relative = bpy.context.preferences.filepaths.use_relative_paths
with bpy.data.libraries.load(
str(libpath), link=True, relative=relative
) as (_, data_to):
data_to.collections = [lib_container]
scene = bpy.context.scene
scene.collection.children.link(bpy.data.collections[lib_container])
rig_container = scene.collection.children[lib_container].make_local()
meshes = [obj for obj in rig_container.objects if obj.type == 'MESH']
armatures = [
obj for obj in rig_container.objects if obj.type == 'ARMATURE']
objects_list = []
assert(len(armatures) == 1)
# 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()
if not obj.get(avalon.blender.pipeline.AVALON_PROPERTY):
obj[avalon.blender.pipeline.AVALON_PROPERTY] = dict()
avalon_info = obj[avalon.blender.pipeline.AVALON_PROPERTY]
avalon_info.update({"container_name": collection.name})
objects_list.append(obj)
if obj.type == 'ARMATURE' and action is not None:
obj.animation_data.action = action
rig_container.pop( avalon.blender.pipeline.AVALON_PROPERTY )
objects_list = self._process(self, str(libpath), lib_container, collection.name, action)
# Save the list of objects in the metadata container
collection_metadata["objects"] = objects_list
@ -271,14 +247,8 @@ class BlendRigLoader(pype.blender.AssetLoader):
objects = collection_metadata["objects"]
lib_container = collection_metadata["lib_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)
bpy.data.collections.remove(bpy.data.collections[lib_container])
self._remove(self, objects, lib_container)
bpy.data.collections.remove(collection)
return True