def pre_save_datablock(proxy: DatablockProxy, target: T.ID, context: Context) -> T.ID: """Process attributes that must be saved first and return a possibly updated reference to the target""" # WARNING this is called from save() and from apply() # When called from save, the proxy has all the synchronized properties # WHen called from apply, the proxy only contains the updated properties if isinstance(target, T.Mesh) and proxy_requires_clear_geometry( proxy, target): target.clear_geometry() elif isinstance(target, T.Material): use_nodes = proxy.data("use_nodes") if use_nodes: target.use_nodes = True is_grease_pencil = proxy.data("is_grease_pencil") # will be None for a DeltaUpdate that does not modify "is_grease_pencil" if is_grease_pencil is not None: # Seems to be write once as no depsgraph update is fired if is_grease_pencil and not target.grease_pencil: bpy.data.materials.create_gpencil_data(target) elif not is_grease_pencil and target.grease_pencil: bpy.data.materials.remove_gpencil_data(target) elif isinstance(target, T.Scene): from mixer.blender_data.misc_proxies import NonePtrProxy # Set 'use_node' to True first is the only way I know to be able to set the 'node_tree' attribute use_nodes = proxy.data("use_nodes") if use_nodes: target.use_nodes = True sequence_editor = proxy.data("sequence_editor") if sequence_editor is not None: # NonePtrProxy or StructProxy if not isinstance(sequence_editor, NonePtrProxy) and target.sequence_editor is None: target.sequence_editor_create() elif isinstance( sequence_editor, NonePtrProxy) and target.sequence_editor is not None: target.sequence_editor_clear() elif isinstance(target, T.Light): # required first to have access to new light type attributes light_type = proxy.data("type") if light_type is not None and light_type != target.type: target.type = light_type # must reload the reference target = proxy.target(context) elif isinstance(target, T.World): use_nodes = proxy.data("use_nodes") if use_nodes: target.use_nodes = True return target
def rename_datablock(self, proxy: DatablockProxy, new_name: str, datablock: T.ID): """ Rename a bpy.data collection item and update the proxy state (receiver side) """ logger.info("rename_datablock proxy %s datablock %r into %s", proxy, datablock, new_name) datablock.name = new_name proxy._data["name"] = new_name
def pre_save_datablock(proxy: DatablockProxy, target: T.ID, context: Context) -> T.ID: """Process attributes that must be saved first and return a possibly updated reference to the target""" # WARNING this is called from save() and from apply() # When called from save, the proxy has all the synchronized properties # WHen called from apply, the proxy only contains the updated properties if target.library: return target # animation_data is handled in StructProxy (parent class of DatablockProxy) if isinstance(target, T.Mesh): from mixer.blender_data.mesh_proxy import MeshProxy assert isinstance(proxy, MeshProxy) if proxy.requires_clear_geometry(target): target.clear_geometry() elif isinstance(target, T.Material): is_grease_pencil = proxy.data("is_grease_pencil") # will be None for a DeltaUpdate that does not modify "is_grease_pencil" if is_grease_pencil is not None: # Seems to be write once as no depsgraph update is fired if is_grease_pencil and not target.grease_pencil: bpy.data.materials.create_gpencil_data(target) elif not is_grease_pencil and target.grease_pencil: bpy.data.materials.remove_gpencil_data(target) elif isinstance(target, T.Scene): from mixer.blender_data.misc_proxies import NonePtrProxy sequence_editor = proxy.data("sequence_editor") if sequence_editor is not None: # NonePtrProxy or StructProxy if not isinstance(sequence_editor, NonePtrProxy) and target.sequence_editor is None: target.sequence_editor_create() elif isinstance( sequence_editor, NonePtrProxy) and target.sequence_editor is not None: target.sequence_editor_clear() elif isinstance(target, _morphable_types): # required first to have access to new datablock attributes type_ = proxy.data("type") if type_ is not None and type_ != target.type: target.type = type_ # must reload the reference target = target.type_recast() uuid = proxy.mixer_uuid context.proxy_state.remove_datablock(uuid) context.proxy_state.add_datablock(uuid, target) elif isinstance(target, T.Action): groups = proxy.data("groups") if groups: groups.save(target.groups, target, "groups", context) return target
def load( self, bl_instance: T.ID, key: str, context: Context, bpy_data_collection_name: str = None, ): """ Load a datablock into this proxy """ if bl_instance.is_embedded_data and bpy_data_collection_name is not None: logger.error( f"DatablockProxy.load() for {bl_instance} : is_embedded_data is True and bpy_prop_collection is {bpy_data_collection_name}. Item ignored" ) return if bl_instance.is_embedded_data: self._bpy_data_collection = None if bpy_data_collection_name is not None: self._bpy_data_collection = bpy_data_collection_name self._class_name = bl_instance.__class__.__name__ self._data.clear() properties = context.synchronized_properties.properties(bl_instance) # this assumes that specifics.py apply only to ID, not Struct properties = specifics.conditional_properties(bl_instance, properties) try: context.visit_state.datablock_proxy = self for name, bl_rna_property in properties: attr = getattr(bl_instance, name) attr_value = read_attribute(attr, name, bl_rna_property, context) # Also write None values to reset attributes like Camera.dof.focus_object # TODO for scene, test difference, only send update if dirty as continuous updates to scene # master collection will conflicting writes with Master Collection self._data[name] = attr_value finally: context.visit_state.datablock_proxy = None specifics.post_save_id(self, bl_instance) uuid = bl_instance.get("mixer_uuid") if uuid: # It is a bpy.data ID, not an ID "embedded" inside another ID, like scene.collection id_ = context.proxy_state.datablocks.get(uuid) if id_ is not bl_instance: # this occurs when # - when we find a reference to a BlendData ID that was not loaded # - the ID are not properly ordred at creation time, for instance (objects, meshes) # instead of (meshes, objects) : a bug logger.debug( "DatablockProxy.load(): %s not in context.proxy_state.datablocks[uuid]", bl_instance) self._datablock_uuid = bl_instance.mixer_uuid context.proxy_state.proxies[uuid] = self self.attach_media_descriptor(bl_instance) return self
def rename_datablock(self, proxy: DatablockProxy, new_name: str, datablock: T.ID): """ Rename a bpy.data collection item and update the proxy state """ logger.info("rename_datablock proxy %s datablock %s into %s", proxy, datablock, new_name) proxy.rename(new_name) datablock.name = new_name
def load(self, datablock: T.ID): """Load the custom properties of datablock, skipping API defined properties""" keys, rna_ui = self._user_keys(datablock) # This only load custom properties with a UI if rna_ui is None: self._dict.clear() self._rna_ui.clear() return self self._rna_ui = rna_ui.to_dict() self._dict = {name: datablock.get(name) for name in keys}
def pre_save_id(proxy: Proxy, target: T.ID) -> T.ID: """Process attributes that must be saved first and return a possibly updated reference to the target Args: bpy_struct: The collection that contgains the ID attr_name: Its key Returns: [bpy.types.ID]: a possibly new ID """ if isinstance(target, T.Scene): # Set 'use_node' to True first is the only way I know to be able to set the 'node_tree' attribute use_nodes = proxy.data("use_nodes") if use_nodes: target.use_nodes = True sequence_editor = proxy.data("sequence_editor") if sequence_editor is not None and target.sequence_editor is None: target.sequence_editor_create() elif isinstance(target, T.Light): # required first to have access to new light type attributes light_type = proxy.data("type") if light_type is not None and light_type != target.type: target.type = light_type # must reload the reference target = proxy.target() elif isinstance(target, T.ColorManagedViewSettings): use_curve_mapping = proxy.data("use_curve_mapping") if use_curve_mapping: target.use_curve_mapping = True elif isinstance(target, bpy.types.World): use_nodes = proxy.data("use_nodes") if use_nodes: target.use_nodes = True return target
def _user_keys(self, datablock: T.ID): keys = set(datablock.keys()) rna_ui = datablock.get("_RNA_UI", None) keys -= {"_RNA_UI"} keys -= set(datablock.bl_rna.properties.keys()) return keys, rna_ui