From f0918ec7604734673c288e0bc55f1c5723dce7ff Mon Sep 17 00:00:00 2001 From: iLLiCiTiT Date: Thu, 19 Dec 2019 17:30:47 +0100 Subject: [PATCH] blender plugins update --- pype/plugins/blender/load/submarine_model.py | 129 ++++++++++++------ .../blender/publish/collect_current_file.py | 2 +- pype/plugins/blender/publish/collect_model.py | 3 +- pype/plugins/blender/publish/extract_model.py | 33 +++-- .../blender/publish/validate_mesh_has_uv.py | 8 +- .../validate_mesh_no_negative_scale.py | 12 +- 6 files changed, 129 insertions(+), 58 deletions(-) diff --git a/pype/plugins/blender/load/submarine_model.py b/pype/plugins/blender/load/submarine_model.py index 4535b29065..99095d74cd 100644 --- a/pype/plugins/blender/load/submarine_model.py +++ b/pype/plugins/blender/load/submarine_model.py @@ -38,7 +38,7 @@ class BlendModelLoader(pype.blender.AssetLoader): Note: It is assumed that only 1 matching collection is found. """ - for collection in bpy.data.collections: + for collection in bpy.context.blend_data.collections: if collection.name != name: continue if collection.library is None: @@ -52,18 +52,19 @@ class BlendModelLoader(pype.blender.AssetLoader): return None @staticmethod - def _collection_contains_object(collection: bpy.types.Collection, object: bpy.types.Object) -> bool: + def _collection_contains_object( + collection: bpy.types.Collection, object: bpy.types.Object + ) -> bool: """Check if the collection contains the object.""" for obj in collection.objects: if obj == object: return True return False - def process_asset(self, - context: dict, - name: str, - namespace: Optional[str] = None, - options: Optional[Dict] = None) -> Optional[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 @@ -76,21 +77,27 @@ class BlendModelLoader(pype.blender.AssetLoader): asset = context["asset"]["name"] subset = context["subset"]["name"] lib_container = pype.blender.plugin.model_name(asset, subset) - container_name = pype.blender.plugin.model_name(asset, subset, namespace) + container_name = pype.blender.plugin.model_name( + asset, subset, namespace + ) relative = bpy.context.preferences.filepaths.use_relative_paths - with bpy.data.libraries.load(libpath, link=True, relative=relative) as (_, data_to): + with bpy.context.blend_data.libraries.load( + libpath, link=True, relative=relative + ) as (_, data_to): data_to.collections = [lib_container] scene = bpy.context.scene - instance_empty = bpy.data.objects.new(container_name, None) + instance_empty = bpy.context.blend_data.objects.new( + container_name, None + ) if not instance_empty.get("avalon"): instance_empty["avalon"] = dict() avalon_info = instance_empty["avalon"] avalon_info.update({"container_name": container_name}) scene.collection.objects.link(instance_empty) instance_empty.instance_type = 'COLLECTION' - container = bpy.data.collections[lib_container] + container = bpy.context.blend_data.collections[lib_container] container.name = container_name instance_empty.instance_collection = container container.make_local() @@ -120,7 +127,9 @@ class BlendModelLoader(pype.blender.AssetLoader): Warning: No nested collections are supported at the moment! """ - collection = bpy.data.collections.get(container["objectName"]) + collection = bpy.context.blend_data.collections.get( + container["objectName"] + ) libpath = Path(api.get_representation_path(representation)) extension = libpath.suffix.lower() @@ -130,14 +139,30 @@ class BlendModelLoader(pype.blender.AssetLoader): pformat(representation, indent=2), ) - assert collection, f"The asset is not loaded: {container['objectName']}" - assert not (collection.children), "Nested collections are not supported." - assert libpath, ("No existing library file found for {container['objectName']}") - assert libpath.is_file(), f"The file doesn't exist: {libpath}" - assert extension in pype.blender.plugin.VALID_EXTENSIONS, f"Unsupported file: {libpath}" - collection_libpath = self._get_library_from_container(collection).filepath - normalized_collection_libpath = str(Path(bpy.path.abspath(collection_libpath)).resolve()) - normalized_libpath = str(Path(bpy.path.abspath(str(libpath))).resolve()) + assert collection, ( + f"The asset is not loaded: {container['objectName']}" + ) + assert not (collection.children), ( + "Nested collections are not supported." + ) + assert libpath, ( + "No existing library file found for {container['objectName']}" + ) + assert libpath.is_file(), ( + f"The file doesn't exist: {libpath}" + ) + assert extension in pype.blender.plugin.VALID_EXTENSIONS, ( + f"Unsupported file: {libpath}" + ) + collection_libpath = ( + self._get_library_from_container(collection).filepath + ) + normalized_collection_libpath = ( + str(Path(bpy.path.abspath(collection_libpath)).resolve()) + ) + normalized_libpath = ( + str(Path(bpy.path.abspath(str(libpath))).resolve()) + ) logger.debug( "normalized_collection_libpath:\n %s\nnormalized_libpath:\n %s", normalized_collection_libpath, @@ -155,29 +180,46 @@ class BlendModelLoader(pype.blender.AssetLoader): # Unlink every object collection.objects.unlink(obj) remove_obj = True - for coll in [coll for coll in bpy.data.collections if coll != collection]: - if coll.objects and self._collection_contains_object(coll, obj): + for coll in [ + coll for coll in bpy.context.blend_data.collections + if coll != collection + ]: + if ( + coll.objects and + self._collection_contains_object(coll, obj) + ): remove_obj = False if remove_obj: objects_to_remove.add(obj) + for obj in objects_to_remove: # Only delete objects that are not used elsewhere - bpy.data.objects.remove(obj) + bpy.context.blend_data.objects.remove(obj) - instance_empties = [obj for obj in collection.users_dupli_group if obj.name in collection.name] + instance_empties = [ + obj for obj in collection.users_dupli_group + if obj.name in collection.name + ] if instance_empties: instance_empty = instance_empties[0] container_name = instance_empty["avalon"]["container_name"] + relative = bpy.context.preferences.filepaths.use_relative_paths - with bpy.data.libraries.load(str(libpath), link=True, relative=relative) as (_, data_to): + with bpy.context.blend_data.libraries.load( + str(libpath), link=True, relative=relative + ) as (_, data_to): data_to.collections = [container_name] + new_collection = self._get_lib_collection(container_name, libpath) if new_collection is None: - raise ValueError("A matching collection '{container_name}' " - "should have been found in: {libpath}") + raise ValueError( + "A matching collection '{container_name}' " + "should have been found in: {libpath}" + ) + for obj in new_collection.objects: collection.objects.link(obj) - bpy.data.collections.remove(new_collection) + bpy.context.blend_data.collections.remove(new_collection) # Update the representation on the collection avalon_prop = collection[avalon.blender.pipeline.AVALON_PROPERTY] avalon_prop["representation"] = str(representation["_id"]) @@ -195,10 +237,14 @@ class BlendModelLoader(pype.blender.AssetLoader): Warning: No nested collections are supported at the moment! """ - collection = bpy.data.collections.get(container["objectName"]) + collection = bpy.context.blend_data.collections.get( + container["objectName"] + ) if not collection: return False - assert not (collection.children), "Nested collections are not supported." + assert not (collection.children), ( + "Nested collections are not supported." + ) instance_parents = list(collection.users_dupli_group) instance_objects = list(collection.objects) for obj in instance_objects + instance_parents: @@ -224,11 +270,10 @@ class CacheModelLoader(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]: + def process_asset( + self, context: dict, name: str, namespace: Optional[str] = None, + options: Optional[Dict] = None + ) -> Optional[List]: """ Arguments: name: Use pre-defined name @@ -243,17 +288,23 @@ class CacheModelLoader(pype.blender.AssetLoader): asset = context["asset"]["name"] subset = context["subset"]["name"] # TODO (jasper): evaluate use of namespace which is 'alien' to Blender. - lib_container = container_name = pype.blender.plugin.model_name(asset, subset, namespace) + lib_container = container_name = ( + pype.blender.plugin.model_name(asset, subset, namespace) + ) relative = bpy.context.preferences.filepaths.use_relative_paths - with bpy.data.libraries.load(libpath, link=True, relative=relative) as (data_from, data_to): + with bpy.context.blend_data.libraries.load( + libpath, link=True, relative=relative + ) as (data_from, data_to): data_to.collections = [lib_container] scene = bpy.context.scene - instance_empty = bpy.data.objects.new(container_name, None) + instance_empty = bpy.context.blend_data.objects.new( + container_name, None + ) scene.collection.objects.link(instance_empty) instance_empty.instance_type = 'COLLECTION' - collection = bpy.data.collections[lib_container] + collection = bpy.context.blend_data.collections[lib_container] collection.name = container_name instance_empty.instance_collection = collection diff --git a/pype/plugins/blender/publish/collect_current_file.py b/pype/plugins/blender/publish/collect_current_file.py index a097c72047..5756431314 100644 --- a/pype/plugins/blender/publish/collect_current_file.py +++ b/pype/plugins/blender/publish/collect_current_file.py @@ -12,5 +12,5 @@ class CollectBlenderCurrentFile(pyblish.api.ContextPlugin): def process(self, context): """Inject the current working file""" - current_file = bpy.data.filepath + current_file = bpy.context.blend_data.filepath context.data['currentFile'] = current_file diff --git a/pype/plugins/blender/publish/collect_model.py b/pype/plugins/blender/publish/collect_model.py index c60402f9ca..4c7e840c17 100644 --- a/pype/plugins/blender/publish/collect_model.py +++ b/pype/plugins/blender/publish/collect_model.py @@ -23,7 +23,7 @@ class CollectModel(pyblish.api.ContextPlugin): representation set. If the representation is set, it is a loaded model and we don't want to publish it. """ - for collection in bpy.data.collections: + for collection in bpy.context.blend_data.collections: avalon_prop = collection.get(AVALON_PROPERTY) or dict() if (avalon_prop.get('family') == 'model' and not avalon_prop.get('representation')): @@ -42,6 +42,7 @@ class CollectModel(pyblish.api.ContextPlugin): instance = context.create_instance( name=name, family=family, + families=[family], subset=subset, asset=asset, task=task, diff --git a/pype/plugins/blender/publish/extract_model.py b/pype/plugins/blender/publish/extract_model.py index 75ec33fb27..501c4d9d5c 100644 --- a/pype/plugins/blender/publish/extract_model.py +++ b/pype/plugins/blender/publish/extract_model.py @@ -1,10 +1,10 @@ -from pathlib import Path +import os import avalon.blender.workio -import sonar.api +import pype.api -class ExtractModel(sonar.api.Extractor): +class ExtractModel(pype.api.Extractor): """Extract as model.""" label = "Model" @@ -14,9 +14,10 @@ class ExtractModel(sonar.api.Extractor): def process(self, instance): # Define extract output file path - stagingdir = Path(self.staging_dir(instance)) + + stagingdir = self.staging_dir(instance) filename = f"{instance.name}.blend" - filepath = str(stagingdir / filename) + filepath = os.path.join(stagingdir, filename) # Perform extraction self.log.info("Performing extraction..") @@ -24,11 +25,23 @@ class ExtractModel(sonar.api.Extractor): # Just save the file to a temporary location. At least for now it's no # problem to have (possibly) extra stuff in the file. avalon.blender.workio.save_file(filepath, copy=True) + # + # # Store reference for integration + # if "files" not in instance.data: + # instance.data["files"] = list() + # + # # instance.data["files"].append(filename) - # Store reference for integration - if "files" not in instance.data: - instance.data["files"] = list() + if "representations" not in instance.data: + instance.data["representations"] = [] - instance.data["files"].append(filename) + representation = { + 'name': 'blend', + 'ext': 'blend', + 'files': filename, + "stagingDir": stagingdir, + } + instance.data["representations"].append(representation) - self.log.info("Extracted instance '%s' to: %s", instance.name, filepath) + + self.log.info("Extracted instance '%s' to: %s", instance.name, representation) diff --git a/pype/plugins/blender/publish/validate_mesh_has_uv.py b/pype/plugins/blender/publish/validate_mesh_has_uv.py index 79a42a11d5..f8c5092ab7 100644 --- a/pype/plugins/blender/publish/validate_mesh_has_uv.py +++ b/pype/plugins/blender/publish/validate_mesh_has_uv.py @@ -3,7 +3,7 @@ from typing import List import bpy import pyblish.api -import sonar.blender.action +import pype.blender.action class ValidateMeshHasUvs(pyblish.api.InstancePlugin): @@ -14,7 +14,7 @@ class ValidateMeshHasUvs(pyblish.api.InstancePlugin): families = ["model"] category = "geometry" label = "Mesh Has UV's" - actions = [sonar.blender.action.SelectInvalidAction] + actions = [pype.blender.action.SelectInvalidAction] optional = True @staticmethod @@ -34,7 +34,9 @@ class ValidateMeshHasUvs(pyblish.api.InstancePlugin): def get_invalid(cls, instance) -> List: invalid = [] # TODO (jasper): only check objects in the collection that will be published? - for obj in [obj for obj in bpy.data.objects if obj.type == 'MESH']: + for obj in [ + obj for obj in bpy.context.blend_data.objects if obj.type == 'MESH' + ]: # Make sure we are in object mode. bpy.ops.object.mode_set(mode='OBJECT') if not cls.has_uvs(obj): diff --git a/pype/plugins/blender/publish/validate_mesh_no_negative_scale.py b/pype/plugins/blender/publish/validate_mesh_no_negative_scale.py index b2a927a2ed..1f050f6844 100644 --- a/pype/plugins/blender/publish/validate_mesh_no_negative_scale.py +++ b/pype/plugins/blender/publish/validate_mesh_no_negative_scale.py @@ -3,7 +3,7 @@ from typing import List import bpy import pyblish.api -import sonar.blender.action +import pype.blender.action class ValidateMeshNoNegativeScale(pyblish.api.Validator): @@ -13,13 +13,15 @@ class ValidateMeshNoNegativeScale(pyblish.api.Validator): hosts = ["blender"] families = ["model"] label = "Mesh No Negative Scale" - actions = [sonar.blender.action.SelectInvalidAction] + actions = [pype.blender.action.SelectInvalidAction] @staticmethod def get_invalid(instance) -> List: invalid = [] # TODO (jasper): only check objects in the collection that will be published? - for obj in [obj for obj in bpy.data.objects if obj.type == 'MESH']: + for obj in [ + obj for obj in bpy.context.blend_data.objects if obj.type == 'MESH' + ]: if any(v < 0 for v in obj.scale): invalid.append(obj) @@ -28,4 +30,6 @@ class ValidateMeshNoNegativeScale(pyblish.api.Validator): def process(self, instance): invalid = self.get_invalid(instance) if invalid: - raise RuntimeError(f"Meshes found in instance with negative scale: {invalid}") + raise RuntimeError( + f"Meshes found in instance with negative scale: {invalid}" + )