from types import SimpleNamespace import bpy from .blueprint import Blueprint # blueprints: any collection with either # - an instance # - marked as asset # - with the "auto_export" flag # https://blender.stackexchange.com/questions/167878/how-to-get-all-collections-of-the-current-scene def blueprints_scan(main_scenes, library_scenes, addon_prefs): export_marked_assets = getattr(addon_prefs.auto_export,"export_marked_assets") blueprints = {} blueprints_from_objects = {} blueprint_name_from_instances = {} collections = [] # main scenes blueprint_instances_per_main_scene = {} internal_collection_instances = {} external_collection_instances = {} # meh def add_object_to_collection_instances(collection_name, object, internal=True): collection_category = internal_collection_instances if internal else external_collection_instances if not collection_name in collection_category.keys(): #print("ADDING INSTANCE OF", collection_name, "object", object.name, "categ", collection_category) collection_category[collection_name] = [] #.append(collection_name) collection_category[collection_name].append(object) for scene in main_scenes:# should it only be main scenes ? what about collection instances inside other scenes ? for object in scene.objects: #print("object", object.name) if object.instance_type == 'COLLECTION': collection = object.instance_collection collection_name = object.instance_collection.name #print(" from collection:", collection_name) collection_from_library = False for library_scene in library_scenes: # should be only in library scenes collection_from_library = library_scene.user_of_id(collection) > 0 # TODO: also check if it is an imported asset if collection_from_library: break add_object_to_collection_instances(collection_name=collection_name, object=object, internal = collection_from_library) # experiment with custom properties from assets stored in other blend files """if not collection_from_library: for property_name in object.keys(): print("stuff", property_name) for property_name in collection.keys(): print("OTHER", property_name)""" # blueprints[collection_name].instances.append(object) # FIXME: this only account for direct instances of blueprints, not for any nested blueprint inside a blueprint if scene.name not in blueprint_instances_per_main_scene.keys(): blueprint_instances_per_main_scene[scene.name] = {} if collection_name not in blueprint_instances_per_main_scene[scene.name].keys(): blueprint_instances_per_main_scene[scene.name][collection_name] = [] blueprint_instances_per_main_scene[scene.name][collection_name].append(object) blueprint_name_from_instances[object] = collection_name """# add any indirect ones # FIXME: needs to be recursive, either here or above for nested_blueprint in blueprints[collection_name].nested_blueprints: if not nested_blueprint in blueprint_instances_per_main_scene[scene.name]: blueprint_instances_per_main_scene[scene.name].append(nested_blueprint)""" for collection in bpy.data.collections: #print("collection", collection, collection.name_full, "users", collection.users) collection_from_library = False defined_in_scene = None for scene in library_scenes: # should be only in library scenes collection_from_library = scene.user_of_id(collection) > 0 if collection_from_library: defined_in_scene = scene break if not collection_from_library: continue if ( 'AutoExport' in collection and collection['AutoExport'] == True # get marked collections or export_marked_assets and collection.asset_data is not None # or if you have marked collections as assets you can auto export them too or collection.name in list(internal_collection_instances.keys()) # or if the collection has an instance in one of the main scenes ): blueprint = Blueprint(collection.name) blueprint.local = True blueprint.marked = 'AutoExport' in collection and collection['AutoExport'] == True or export_marked_assets and collection.asset_data is not None blueprint.objects = [object.name for object in collection.all_objects if not object.instance_type == 'COLLECTION'] # inneficient, double loop blueprint.nested_blueprints = [object.instance_collection.name for object in collection.all_objects if object.instance_type == 'COLLECTION'] # FIXME: not precise enough, aka "what is a blueprint" blueprint.collection = collection blueprint.instances = internal_collection_instances[collection.name] if collection.name in internal_collection_instances else [] blueprint.scene = defined_in_scene blueprints[collection.name] = blueprint # add nested collections to internal/external_collection instances # FIXME: inneficient, third loop over all_objects for object in collection.all_objects: if object.instance_type == 'COLLECTION': add_object_to_collection_instances(collection_name=object.instance_collection.name, object=object, internal = blueprint.local) # now create reverse lookup , so you can find the collection from any of its contained objects for object in collection.all_objects: blueprints_from_objects[object.name] = blueprint#collection.name # collections.append(collection) # EXTERNAL COLLECTIONS: add any collection that has an instance in the main scenes, but is not present in any of the scenes (IE NON LOCAL/ EXTERNAL) for collection_name in external_collection_instances: collection = bpy.data.collections[collection_name] blueprint = Blueprint(collection.name) blueprint.local = False blueprint.marked = True #external ones are always marked, as they have to have been marked in their original file #'AutoExport' in collection and collection['AutoExport'] == True blueprint.objects = [object.name for object in collection.all_objects if not object.instance_type == 'COLLECTION'] # inneficient, double loop blueprint.nested_blueprints = [object.instance_collection.name for object in collection.all_objects if object.instance_type == 'COLLECTION'] # FIXME: not precise enough, aka "what is a blueprint" blueprint.collection = collection blueprint.instances = external_collection_instances[collection.name] if collection.name in external_collection_instances else [] blueprints[collection.name] = blueprint #print("EXTERNAL COLLECTION", collection, dict(collection)) # add nested collections to internal/external_collection instances # FIXME: inneficient, third loop over all_objects """for object in collection.all_objects: if object.instance_type == 'COLLECTION': add_object_to_collection_instances(collection_name=object.instance_collection.name, object=object, internal = blueprint.local)""" # now create reverse lookup , so you can find the collection from any of its contained objects for object in collection.all_objects: blueprints_from_objects[object.name] = blueprint#collection.name # then add any nested collections at root level (so we can have a flat list, regardless of nesting) # TODO: do this recursively for blueprint_name in list(blueprints.keys()): parent_blueprint = blueprints[blueprint_name] for nested_blueprint_name in parent_blueprint.nested_blueprints: if not nested_blueprint_name in blueprints.keys(): collection = bpy.data.collections[nested_blueprint_name] blueprint = Blueprint(collection.name) blueprint.local = parent_blueprint.local blueprint.objects = [object.name for object in collection.all_objects if not object.instance_type == 'COLLECTION'] # inneficient, double loop blueprint.nested_blueprints = [object.instance_collection.name for object in collection.all_objects if object.instance_type == 'COLLECTION'] # FIXME: not precise enough, aka "what is a blueprint" blueprint.collection = collection blueprint.instances = external_collection_instances[collection.name] if collection.name in external_collection_instances else [] blueprint.scene = parent_blueprint.scene if parent_blueprint.local else None blueprints[collection.name] = blueprint # now create reverse lookup , so you can find the collection from any of its contained objects for object in collection.all_objects: blueprints_from_objects[object.name] = blueprint#collection.name blueprints = dict(sorted(blueprints.items())) '''print("BLUEPRINTS") for blueprint_name in blueprints: print(" ", blueprints[blueprint_name]) """print("BLUEPRINTS LOOKUP") print(blueprints_from_objects)""" print("BLUEPRINT INSTANCES PER MAIN SCENE") print(blueprint_instances_per_main_scene)''' """changes_test = {'Library': { 'Blueprint1_mesh': bpy.data.objects['Blueprint1_mesh'], 'Fox_mesh': bpy.data.objects['Fox_mesh'], 'External_blueprint2_Cylinder': bpy.data.objects['External_blueprint2_Cylinder']} } # which main scene has been impacted by this # does one of the main scenes contain an INSTANCE of an impacted blueprint for scene in main_scenes: changed_objects = list(changes_test["Library"].keys()) # just a hack for testing #bluprint_instances_in_scene = blueprint_instances_per_main_scene[scene.name] #print("instances per scene", bluprint_instances_in_scene, "changed_objects", changed_objects) changed_blueprints_with_instances_in_scene = [blueprints_from_objects[changed] for changed in changed_objects if changed in blueprints_from_objects] print("changed_blueprints_with_instances_in_scene", changed_blueprints_with_instances_in_scene) level_needs_export = len(changed_blueprints_with_instances_in_scene) > 0 if level_needs_export: print("level needs export", scene.name) for scene in library_scenes: changed_objects = list(changes_test[scene.name].keys()) changed_blueprints = [blueprints_from_objects[changed] for changed in changed_objects if changed in blueprints_from_objects] # we only care about local blueprints/collections changed_local_blueprints = [blueprint_name for blueprint_name in changed_blueprints if blueprint_name in blueprints.keys() and blueprints[blueprint_name].local] print("changed blueprints", changed_local_blueprints)""" # additional helper data structures for lookups etc blueprints_per_name = blueprints blueprints = [] # flat list internal_blueprints = [] external_blueprints = [] blueprints_per_scenes = {} blueprint_instances_per_library_scene = {} for blueprint in blueprints_per_name.values(): blueprints.append(blueprint) if blueprint.local: internal_blueprints.append(blueprint) if blueprint.scene: if not blueprint.scene.name in blueprints_per_scenes: blueprints_per_scenes[blueprint.scene.name] = [] blueprints_per_scenes[blueprint.scene.name].append(blueprint.name) # meh else: external_blueprints.append(blueprint) # we also need to have blueprint instances for data = { "blueprints": blueprints, "blueprints_per_name": blueprints_per_name, "blueprint_names": list(blueprints_per_name.keys()), "blueprints_from_objects": blueprints_from_objects, "internal_blueprints": internal_blueprints, "external_blueprints": external_blueprints, "blueprints_per_scenes": blueprints_per_scenes, "blueprint_instances_per_main_scene": blueprint_instances_per_main_scene, "blueprint_instances_per_library_scene": blueprint_instances_per_library_scene, # not sure about these two "internal_collection_instances": internal_collection_instances, "external_collection_instances": external_collection_instances, "blueprint_name_from_instances": blueprint_name_from_instances } return SimpleNamespace(**data)