def build_collection_to_collection(data): parent_name, index = common.decode_string(data, 0) child_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning( "build_collection_to_collection %s <- %s, ignore in generic mode", parent_name, child_name) return logger.info("build_collection_to_collection %s <- %s", parent_name, child_name) parent = share_data.blender_collections[parent_name] child = share_data.blender_collections[child_name] try: parent.children.link(child) except RuntimeError as e: if not share_data.use_vrtist_protocol(): # Added by the Blender Protocol logger.info( f"build_collection_to_collection(): parent {parent_name}, child {child_name}..." ) logger.info("... Exception during parent.children.link() ...") logger.info("... Safe in generic mode ...") logger.info(f"... {e!r}") else: logger.warning( f"build_collection_to_collection(): parent {parent_name}, child {child_name}..." ) logger.warning("... Exception during parent.children.link() ...") logger.warning(f"... {e!r}")
def build_data_create(buffer): if share_data.use_vrtist_protocol(): return proxy_string, index = decode_string(buffer, 0) codec = Codec() rename_changeset = None try: datablock_proxy: DatablockProxy = codec.decode(proxy_string) logger.info("%s: %s", "build_data_create", datablock_proxy) # TODO temporary until VRtist protocol uses Blenddata instead of blender_objects & co share_data.set_dirty() _, rename_changeset = share_data.bpy_data_proxy.create_datablock( datablock_proxy) _decode_and_build_soas(datablock_proxy.mixer_uuid(), buffer, index) except DecodeError as e: logger.error(f"Decode error for {str(e.args[1])[:100]} ...") logger.error("... possible version mismatch") return except Exception: logger.error("Exception during build_data_create") for line in traceback.format_exc().splitlines(): logger.error(line) logger.error(buffer[0:200]) logger.error("...") logger.error(buffer[-200:0]) logger.error("ignored") return if rename_changeset: send_data_renames(rename_changeset)
def build_data_update(buffer: bytes): if share_data.use_vrtist_protocol(): return share_data.set_dirty() codec = Codec() try: message = BlenderDataMessage() message.decode(buffer) delta: Delta = codec.decode(message.proxy_string) logger.debug("%s: %s", "build_data_update", delta) delta.value.arrays = message.arrays share_data.bpy_data_proxy.update_datablock(delta) datablock_proxy = delta.value if datablock_proxy is not None: _build_soas(datablock_proxy.mixer_uuid, message.soas) except DecodeError as e: logger.error( f"Decode error for {str(e.args[1])[:100]} . Possible causes...") logger.error("... user error: version mismatch") logger.error( "... internal error: Proxy class not registered. Import it in blender_data.__init__.py" ) except Exception: logger.error("Exception during build_data_update") for line in traceback.format_exc().splitlines(): logger.error(line) logger.error(f"During processing of buffer for {delta}") logger.error(buffer[0:200]) logger.error("...") logger.error(buffer[-200:0]) logger.error("ignored")
def build_data_create(buffer): if share_data.use_vrtist_protocol(): return share_data.set_dirty() rename_changeset = None codec = Codec() try: message = BlenderDataMessage() message.decode(buffer) datablock_proxy = codec.decode(message.proxy_string) logger.info("%s %s", "build_data_create", datablock_proxy) datablock_proxy.arrays = message.arrays _, rename_changeset = share_data.bpy_data_proxy.create_datablock( datablock_proxy) _build_soas(datablock_proxy.mixer_uuid, message.soas) except DecodeError as e: logger.error(f"Decode error for {str(e.args[1])[:100]} ...") logger.error("... possible version mismatch") return except Exception: logger.error("Exception during build_data_create") for line in traceback.format_exc().splitlines(): logger.error(line) logger.error(buffer[0:200]) logger.error("...") logger.error(buffer[-200:0]) logger.error("ignored") return if rename_changeset: send_data_renames(rename_changeset)
def build_data_update(buffer: bytes): if share_data.use_vrtist_protocol(): return proxy_string, index = decode_string(buffer, 0) codec = Codec() try: delta: DeltaUpdate = codec.decode(proxy_string) logger.info("%s: %s", "build_data_update", delta) # TODO temporary until VRtist protocol uses Blenddata instead of blender_objects & co share_data.set_dirty() share_data.bpy_data_proxy.update_datablock(delta) datablock_proxy = delta.value if datablock_proxy is not None: _decode_and_build_soas(datablock_proxy.mixer_uuid(), buffer, index) except DecodeError as e: logger.error(f"Decode error for {str(e.args[1])[:100]} ...") logger.error("... possible version mismatch") except Exception: logger.error("Exception during build_data_update") for line in traceback.format_exc().splitlines(): logger.error(line) logger.error(f"During processing of buffer for {delta}") logger.error(buffer[0:200]) logger.error("...") logger.error(buffer[-200:0]) logger.error("ignored")
def build_collection(data): name_full, index = common.decode_string(data, 0) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning("build_collection %s, ignored in generic mode", name_full) return # Blender/Blender in VRtist (non generic) mode visible, index = common.decode_bool(data, index) hide_viewport = not visible offset, index = common.decode_vector3(data, index) temporary_visibility, index = common.decode_bool(data, index) logger.info("build_collection %s", name_full) collection = share_data.blender_collections.get(name_full) if collection is None: collection = bpy.data.collections.new(name_full) share_data.blender_collections[name_full] = collection collection.hide_viewport = hide_viewport collection.instance_offset = offset layer_collection = share_data.blender_layer_collections.get(name_full) if layer_collection: layer_collection.hide_viewport = not temporary_visibility else: # if the layer collection does not exists, store its state for later share_data.blender_collection_temporary_visibility[ name_full] = temporary_visibility
def build_remove_object_from_collection(data): collection_name, index = common.decode_string(data, 0) object_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning( "build_remove_object_from_collection %s <- %s, ignore in generic mode", collection_name, object_name) return logger.info("build_remove_object_from_collection %s <- %s", collection_name, object_name) collection = share_data.blender_collections[collection_name] object_ = share_data.blender_objects.get(object_name) if object_: # otherwise already removed by Blender protocol try: collection.objects.unlink(object_) except Exception as e: logger.info( "build_remove_object_from_collection: exception during unlink... " ) logger.info(f"... {e!r} ")
def handler_send_scene_data_to_server(scene, dummy): global processing_depsgraph_handler if processing_depsgraph_handler: logger.error("Depsgraph handler recursion attempt") return processing_depsgraph_handler = True try: logger.debug("handler_send_scene_data_to_server") # Ensure we will rebuild accessors when a depsgraph update happens # todo investigate why we need this... share_data.set_dirty() if share_data.client.block_signals: logger.debug( "handler_send_scene_data_to_server canceled (block_signals = True)" ) return if share_data.use_vrtist_protocol(): send_scene_data_to_server(scene, dummy) else: generic.send_scene_data_to_server(scene, dummy) finally: processing_depsgraph_handler = False
def send_data_creations(proxies: CreationChangeset): if share_data.use_vrtist_protocol(): return codec = Codec() for datablock_proxy in proxies: logger.info("%s %s", "send_data_create", datablock_proxy) send_media_creations(datablock_proxy) try: encoded_proxy = codec.encode(datablock_proxy) except EncodeError as e: logger.error( f"send_data_create: encode exception for {datablock_proxy}") logger.error(f"... {e!r}") return except Exception: logger.error( f"send_data_create: encode exception for {datablock_proxy}") for line in traceback.format_exc().splitlines(): logger.error(line) return buffer = BlenderDataMessage.encode(datablock_proxy, encoded_proxy) command = Command(MessageType.BLENDER_DATA_CREATE, buffer, 0) share_data.client.add_command(command)
def send_data_removals(removals: RemovalChangeset): if share_data.use_vrtist_protocol(): return for uuid, _, debug_info in removals: logger.info("send_removal: %s (%s)", uuid, debug_info) buffer = encode_string(uuid) + encode_string(debug_info) command = Command(MessageType.BLENDER_DATA_REMOVE, buffer, 0) share_data.client.add_command(command)
def build_collection_to_scene(data): scene_name, index = common.decode_string(data, 0) collection_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning("build_collection_to_scene %s <- %s", scene_name, collection_name) return logger.info("build_collection_to_scene %s <- %s", scene_name, collection_name) try: scene = share_data.blender_scenes[scene_name] except KeyError: if not share_data.use_vrtist_protocol(): # Removed by the Blender Protocol logger.info( f"build_collection_to_scene(): scene not found {scene_name}. Safe in generic mode ..." ) return else: raise collection = share_data.blender_collections[collection_name] try: scene.collection.children.link(collection) except RuntimeError as e: if not share_data.use_vrtist_protocol(): # Added by the Blender Protocol logger.info( f"build_collection_to_scene(): scene {scene_name}, collection {collection_name}..." ) logger.info( "... Exception during scene.collection.children.link() ...") logger.info("... Safe in generic mode ...") logger.info(f"... {e!r}") else: raise share_data.update_collection_temporary_visibility(collection_name)
def build_data_remove(buffer): if share_data.use_vrtist_protocol(): return message = BlenderRemoveMessage() message.decode(buffer) logger.info("build_data_remove: %s (%s)", message.uuid, message.debug_info) share_data.bpy_data_proxy.remove_datablock(message.uuid) # TODO temporary until VRtist protocol uses Blenddata instead of blender_objects & co share_data.set_dirty()
def build_data_remove(buffer): if share_data.use_vrtist_protocol(): return uuid, index = decode_string(buffer, 0) debug_info, index = decode_string(buffer, index) logger.info("build_data_remove: %s (%s)", uuid, debug_info) share_data.bpy_data_proxy.remove_datablock(uuid) # TODO temporary until VRtist protocol uses Blenddata instead of blender_objects & co share_data.set_dirty()
def send_data_renames(renames: RenameChangeset): if not renames: return if share_data.use_vrtist_protocol(): return items = [] for uuid, old_name, new_name, debug_info in renames: logger.info("send_rename: %s (%s) into %s", uuid, debug_info, new_name) items.extend([uuid, old_name, new_name]) buffer = encode_string_array(items) command = Command(MessageType.BLENDER_DATA_RENAME, buffer, 0) share_data.client.add_command(command)
def build_scene_renamed(data): # TODO check if obsolete old_name, index = common.decode_string(data, 0) new_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning("build_scene_renamed %s to %s", old_name, new_name) return logger.info("build_scene_renamed %s to %s", old_name, new_name) scene = share_data.blender_scenes.get(old_name) scene.name = new_name share_data.blender_scenes_dirty = True
def build_remove_collection_from_collection(data): parent_name, index = common.decode_string(data, 0) child_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning( "build_remove_collection_from_collection %s <- %s, ignore in generic mode", parent_name, child_name) return logger.info("build_remove_collection_from_collection %s <- %s", parent_name, child_name) parent = share_data.blender_collections[parent_name] child = share_data.blender_collections[child_name] parent.children.unlink(child)
def send_data_updates(updates: UpdateChangeset): if share_data.use_vrtist_protocol(): return codec = Codec() for update in updates: logger.debug("%s %s", "send_data_update", update) try: encoded_update = codec.encode(update) except Exception: logger.error(f"send_data_update: encode exception for {update}") for line in traceback.format_exc().splitlines(): logger.error(line) continue buffer = BlenderDataMessage.encode(update.value, encoded_update) command = Command(MessageType.BLENDER_DATA_UPDATE, buffer, 0) share_data.client.add_command(command)
def send_data_updates(updates: UpdateChangeset): if share_data.use_vrtist_protocol(): return codec = Codec() for update in updates: logger.info("%s %s", "send_data_update", update) try: encoded_update = codec.encode(update) except Exception: logger.error(f"send_data_update: encode exception for {update}") for line in traceback.format_exc().splitlines(): logger.error(line) continue items: List[bytes] = [] items.append(encode_string(encoded_update)) items.extend(soa_buffers(update.value)) command = Command(MessageType.BLENDER_DATA_UPDATE, b"".join(items), 0) share_data.client.add_command(command)
def build_add_object_to_collection(data): collection_name, index = common.decode_string(data, 0) object_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning( "build_add_object_to_collection %s <- %s, ignore in generic mode", collection_name, object_name) return logger.info("build_add_object_to_collection %s <- %s", collection_name, object_name) collection = share_data.blender_collections[collection_name] # We may have received an object creation message before this collection link message # and object creation will have created and linked the collection if needed if collection.objects.get(object_name) is None: object_ = share_data.blender_objects[object_name] collection.objects.link(object_)
def build_add_object_to_scene(data): scene_name, index = common.decode_string(data, 0) object_name, _ = common.decode_string(data, index) logger.info("build_add_object_to_scene %s <- %s", scene_name, object_name) try: scene = share_data.blender_scenes[scene_name] except KeyError: if not share_data.use_vrtist_protocol(): # Removed by the Blender Protocol logger.info( f"build_collection_to_scene(): scene not found {scene_name}. Safe in generic mode ..." ) return else: raise # We may have received an object creation message before this collection link message # and object creation will have created and linked the collecetion if needed if scene.collection.objects.get(object_name) is None: object_ = share_data.blender_objects[object_name] scene.collection.objects.link(object_)
def build_data_rename(buffer): if share_data.use_vrtist_protocol(): return strings, _ = decode_string_array(buffer, 0) # (uuid1, old1, new1, uuid2, old2, new2, ...) to ((uuid1, old1, new1), (uuid2, old2, new2), ...) args = [iter(strings)] * 3 # do not consume the iterator on the log loop ! items = list(itertools.zip_longest(*args)) for uuid, old_name, new_name in items: logger.info("build_data_rename: %s (%s) into %s", uuid, old_name, new_name) rename_changeset = share_data.bpy_data_proxy.rename_datablocks(items) # TODO temporary until VRtist protocol uses Blenddata instead of blender_objects & co share_data.set_dirty() if rename_changeset: send_data_renames(rename_changeset)
def build_collection_instance(data): instance_name, index = common.decode_string(data, 0) instantiated_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning( "build_collection_instance %s <- %s, ignore in generic mode", instantiated_name, instance_name) return logger.info("build_collection_instance %s from %s", instantiated_name, instance_name) instantiated = share_data.blender_collections[instantiated_name] instance = bpy.data.objects.new(name=instance_name, object_data=None) instance.instance_collection = instantiated instance.instance_type = "COLLECTION" share_data.blender_objects[instance_name] = instance
def build_collection_removed(data): name_full, index = common.decode_string(data, 0) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning("build_collection_remove %s, ignore in generic mode", name_full) return # Blender/Blender in VRtist (non generic) mode logger.info("build_collectionRemove %s", name_full) collection = share_data.blender_collections.get(name_full) if collection: # otherwise already removed by Blender protocol try: del share_data.blender_collections[name_full] bpy.data.collections.remove(collection) except Exception as e: logger.info( "build_remove_collection_from_scene: exception during unlink... " ) logger.info(f"... {e!r} ")
def build_remove_collection_from_scene(data): scene_name, index = common.decode_string(data, 0) collection_name, _ = common.decode_string(data, index) # This message is not emitted by VRtist, only by Blender, so it is used only for Blender/Blender sync. # In generic mode, it conflicts with generic messages, so drop it if not share_data.use_vrtist_protocol(): logger.warning("build_remove_collection_from_scene %s <- %s", scene_name, collection_name) return logger.info("build_remove_collection_from_scene %s <- %s", scene_name, collection_name) scene = share_data.blender_scenes[scene_name] collection = share_data.blender_collections.get(collection_name) if collection: # otherwise already removed by Blender protocol try: scene.collection.children.unlink(collection) except Exception as e: logger.info( "build_remove_collection_from_scene: exception during unlink... " ) logger.info(f"... {e!r} ")
def send_data_creations(proxies: CreationChangeset): if share_data.use_vrtist_protocol(): return codec = Codec() for datablock_proxy in proxies: logger.info("%s %s", "send_data_create", datablock_proxy) try: encoded_proxy = codec.encode(datablock_proxy) except Exception: logger.error( f"send_data_create: encode exception for {datablock_proxy}") for line in traceback.format_exc().splitlines(): logger.error(line) continue send_media_creations(datablock_proxy) # creation so that it is available at bpy_data_ctor() time items: List[bytes] = [] items.append(encode_string(encoded_proxy)) items.extend(soa_buffers(datablock_proxy)) command = Command(MessageType.BLENDER_DATA_CREATE, b"".join(items), 0) share_data.client.add_command(command)
def handler_on_undo_redo_post(scene, dummy): logger.error(f"Undo/redo post on {scene}") share_data.client.send_error( f"Undo/redo post from {get_mixer_prefs().user}") if not share_data.use_vrtist_protocol(): # Generic sync: reload all datablocks undone = share_data.bpy_data_proxy.snapshot_undo_post() logger.warning(f"undone uuids : {undone}") share_data.bpy_data_proxy.reload_datablocks() else: share_data.set_dirty() share_data.clear_lists() # apply only in object mode if not is_in_object_mode(): return old_objects_name = dict([ (k, None) for k in share_data.old_objects.keys() ]) # value not needed remap_objects_info() for k, v in share_data.old_objects.items(): if k in old_objects_name: old_objects_name[k] = v update_object_state(old_objects_name, share_data.old_objects) update_collections_state() update_scenes_state() remove_objects_from_scenes() remove_objects_from_collections() remove_collections_from_scenes() remove_collections_from_collections() remove_collections() add_scenes() add_objects() add_collections() add_collections_to_scenes() add_collections_to_collections() add_objects_to_collections() add_objects_to_scenes() update_collections_parameters() create_vrtist_objects() delete_scene_objects() rename_objects() update_objects_visibility() update_objects_constraints() update_objects_transforms() reparent_objects() # send selection content (including data) materials = set() for obj in bpy.context.selected_objects: update_transform(obj) if hasattr(obj, "data"): update_params(obj) if hasattr(obj, "material_slots"): for slot in obj.material_slots[:]: materials.add(slot.material) for material in materials: share_data.client.send_material(material) share_data.update_current_data()
def handler_on_undo_redo_pre(scene): if share_data.use_vrtist_protocol(): send_scene_data_to_server(scene, None) else: share_data.bpy_data_proxy.snapshot_undo_pre()