def send_scene_data_to_server(scene, dummy): logger.debug( "send_scene_data_to_server(): skip_next_depsgraph_update %s, pending_test_update %s", share_data.client.skip_next_depsgraph_update, share_data.pending_test_update, ) depsgraph = bpy.context.evaluated_depsgraph_get() if depsgraph.updates: logger.debug( f"DG updates for {depsgraph.scene} {depsgraph.view_layer}") for update in depsgraph.updates: logger.debug(" ......%r", update.id.original) else: # FIXME Possible missed update : # If an updated datablock is not linked in the current scene/view_layer, the update triggers # an empty DG update batch. This can happen when the update is from a script. logger.info( f"DG updates empty for {depsgraph.scene} {depsgraph.view_layer}") # prevent processing self events, but always process test updates if not share_data.pending_test_update and share_data.client.skip_next_depsgraph_update: share_data.client.skip_next_depsgraph_update = False logger.debug( "send_scene_data_to_server canceled (skip_next_depsgraph_update = True) ..." ) return share_data.pending_test_update = False bpy_data_proxy = share_data.bpy_data_proxy depsgraph = bpy.context.evaluated_depsgraph_get() updates, delayed_updates = updates_to_check(depsgraph) # delayed update processing is delayed until the selected objects return to OBJECT mode process_delayed_updates = not delayed_updates if delayed_updates: bpy_data_proxy.append_delayed_updates(delayed_updates) # Compute the difference between the proxy state and the Blender state # It is a coarse difference at the ID level(created, removed, renamed) diff = BpyBlendDiff() diff.diff(bpy_data_proxy, safe_properties) # Ask the proxy to compute the list of elements to synchronize and update itself changeset = bpy_data_proxy.update(diff, updates, process_delayed_updates, safe_properties) # Send creations before update so that collection updates for new object have a valid target data_api.send_data_creations(changeset.creations) data_api.send_data_removals(changeset.removals) data_api.send_data_renames(changeset.renames) data_api.send_data_updates(changeset.updates) logger.debug("send_scene_data_to_server: end")
def send_scene_data_to_server(scene, dummy): logger.debug( "send_scene_data_to_server(): skip_next_depsgraph_update %s, pending_test_update %s", share_data.client.skip_next_depsgraph_update, share_data.pending_test_update, ) depsgraph = bpy.context.evaluated_depsgraph_get() if depsgraph.updates: logger.debug("Current dg updates ...") for update in depsgraph.updates: logger.debug(" ......%s", update.id.original) # prevent processing self events, but always process test updates if not share_data.pending_test_update and share_data.client.skip_next_depsgraph_update: share_data.client.skip_next_depsgraph_update = False logger.debug( "send_scene_data_to_server canceled (skip_next_depsgraph_update = True) ..." ) return share_data.pending_test_update = False bpy_data_proxy = share_data.bpy_data_proxy depsgraph = bpy.context.evaluated_depsgraph_get() # Delay the update of Object data to avoid Mesh updates in edit mode, but keep other updates. # Mesh separate delivers Collection as well as created Object and Mesh updates while the edited # object is in edit mode, and these updates are not delivered when leaving edit mode, so # make sure to process them anyway. It is also possible to edit multiple objects at once selected_objects = getattr(bpy.context, "selected_objects", {}) updates = {update.id.original for update in depsgraph.updates} delayed_updates = set() for datablock in updates: if datablock in selected_objects and datablock.mode != "OBJECT" and datablock.data is not None: delayed_updates.add(datablock) delayed_updates.add(datablock.data) updates -= delayed_updates # delayed update processing is delayed until the selected objects return to OBJECT mode process_delayed_updates = not delayed_updates if delayed_updates: bpy_data_proxy.append_delayed_updates(delayed_updates) logger.info("send_scene_data_to_server. Delaying updates ") for update in delayed_updates: logger.info("... %s", update) # Compute the difference between the proxy state and the Blender state # It is a coarse difference at the ID level(created, removed, renamed) diff = BpyBlendDiff() diff.diff(bpy_data_proxy, safe_properties) # Ask the proxy to compute the list of elements to synchronize and update itself changeset = bpy_data_proxy.update(diff, updates, process_delayed_updates, safe_properties) # Send creations before update so that collection updates for new object have a valid target data_api.send_data_creations(changeset.creations) data_api.send_data_removals(changeset.removals) data_api.send_data_renames(changeset.renames) data_api.send_data_updates(changeset.updates) logger.debug("send_scene_data_to_server: end")
def send_scene_data_to_server(scene, dummy): logger.debug( "send_scene_data_to_server(): skip_next_depsgraph_update %s, pending_test_update %s", share_data.client.skip_next_depsgraph_update, share_data.pending_test_update, ) timer = share_data.current_stats_timer if not share_data.client: logger.info("send_scene_data_to_server canceled (no client instance)") return share_data.set_dirty() with timer.child("clear_lists"): share_data.clear_lists() depsgraph = bpy.context.evaluated_depsgraph_get() if depsgraph.updates: logger.debug("Current dg updates ...") for update in depsgraph.updates: logger.debug(" ......%s", update.id.original) # prevent processing self events, but always process test updates if not share_data.pending_test_update and share_data.client.skip_next_depsgraph_update: share_data.client.skip_next_depsgraph_update = False logger.debug("send_scene_data_to_server canceled (skip_next_depsgraph_update = True) ...") return share_data.pending_test_update = False if not is_in_object_mode(): logger.info("send_scene_data_to_server canceled (not is_in_object_mode)") return update_object_state(share_data.old_objects, share_data.blender_objects) with timer.child("update_scenes_state"): update_scenes_state() with timer.child("update_collections_state"): update_collections_state() changed = False with timer.child("checkForChangeAndSendUpdates"): changed |= remove_objects_from_collections() changed |= remove_objects_from_scenes() changed |= remove_collections_from_collections() changed |= remove_collections_from_scenes() changed |= remove_collections() changed |= remove_scenes() changed |= add_scenes() changed |= add_collections() changed |= add_objects() # Updates from the VRtist protocol and from the full Blender protocol must be cafully intermixed # This is an unfortunate requirements from the current coexistence status of # both protocols # After creation of meshes : meshes are not yet supported by full Blender protocol, # but needed to properly create objects # Before creation of objects : the VRtint protocol will implicitely create objects with # unappropriate default values (e.g. transform creates an object with no data) if share_data.use_experimental_sync(): # Compute the difference between the proxy state and the Blender state # It is a coarse difference at the ID level(created, removed, renamed) diff = BpyBlendDiff() diff.diff(share_data.proxy, safe_context) # Ask the proxy to compute the list of elements to synchronize and update itself depsgraph = bpy.context.evaluated_depsgraph_get() updates, removals = share_data.proxy.update(diff, safe_context, depsgraph.updates) # Send the data update messages (includes serialization) data_api.send_data_removals(removals) data_api.send_data_updates(updates) share_data.proxy.debug_check_id_proxies() # send the VRtist transforms after full Blender protocol has the opportunity to create the object data # that is not handled by VRtist protocol, otherwise the receiver creates an empty when it receives a transform changed |= update_transforms() changed |= add_collections_to_scenes() changed |= add_collections_to_collections() changed |= add_objects_to_collections() changed |= add_objects_to_scenes() changed |= update_collections_parameters() changed |= create_vrtist_objects() changed |= delete_scene_objects() changed |= rename_objects() changed |= update_objects_visibility() changed |= update_objects_transforms() changed |= reparent_objects() changed |= shot_manager.check_montage_mode() if not changed: with timer.child("update_objects_data"): update_objects_data() # update for next change with timer.child("update_current_data"): share_data.update_current_data() logger.debug("send_scene_data_to_server: end")
def send_scene_data_to_server(scene, dummy): logger.debug( "send_scene_data_to_server(): skip_next_depsgraph_update %s, pending_test_update %s", share_data.client.skip_next_depsgraph_update, share_data.pending_test_update, ) depsgraph = bpy.context.evaluated_depsgraph_get() if depsgraph.updates: logger.debug("DG updates for {depsgraph.scene} {depsgraph.view_layer}") for update in depsgraph.updates: logger.debug(" ......%s", update.id.original) else: # FIXME Possible missed update : # If an updated datablock is not linked in the current scene/view_layer, the update triggers # an empty DG update batch. This can happen when the update is from a script. logger.info( f"DG updates empty for {depsgraph.scene} {depsgraph.view_layer}") # prevent processing self events, but always process test updates if not share_data.pending_test_update and share_data.client.skip_next_depsgraph_update: share_data.client.skip_next_depsgraph_update = False logger.debug( "send_scene_data_to_server canceled (skip_next_depsgraph_update = True) ..." ) return share_data.pending_test_update = False bpy_data_proxy = share_data.bpy_data_proxy depsgraph = bpy.context.evaluated_depsgraph_get() updates = {update.id.original for update in depsgraph.updates} # in some cases (TestShapeKey.test_rename_key), the Key update is missing. Always check for shape_keys shape_key_updates = { datablock.shape_keys for datablock in updates if hasattr(datablock, "shape_keys") and isinstance(datablock.shape_keys, T.Key) } updates.update(shape_key_updates) # Delay the update of Object data to avoid Mesh updates in edit or paint mode, but keep other updates. # Mesh separate delivers Collection as well as created Object and Mesh updates while the edited # object is in edit mode, and these updates are not delivered when leaving edit mode, so # make sure to process them anyway. It is also possible to edit multiple objects at once # When no Object is selected and a Mesh is selected the Object with the selected Mesh is the # active_object, but not in selected_objects current_objects = set(getattr(bpy.context, "selected_objects", [])) active_object = getattr(bpy.context, "active_object", None) if active_object: current_objects.add(active_object) delayed_updates = set() for datablock in updates: if datablock in current_objects and datablock.mode != "OBJECT" and datablock.data is not None: delayed_updates.add(datablock) delayed_updates.add(datablock.data) updates -= delayed_updates # delayed update processing is delayed until the selected objects return to OBJECT mode process_delayed_updates = not delayed_updates if delayed_updates: bpy_data_proxy.append_delayed_updates(delayed_updates) logger.info("send_scene_data_to_server. Delaying updates ") for update in delayed_updates: logger.info("... %s", update) # Compute the difference between the proxy state and the Blender state # It is a coarse difference at the ID level(created, removed, renamed) diff = BpyBlendDiff() diff.diff(bpy_data_proxy, safe_properties) # Ask the proxy to compute the list of elements to synchronize and update itself changeset = bpy_data_proxy.update(diff, updates, process_delayed_updates, safe_properties) # Send creations before update so that collection updates for new object have a valid target data_api.send_data_creations(changeset.creations) data_api.send_data_removals(changeset.removals) data_api.send_data_renames(changeset.renames) data_api.send_data_updates(changeset.updates) logger.debug("send_scene_data_to_server: end")