예제 #1
0
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")
예제 #2
0
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")
예제 #3
0
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")
예제 #4
0
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")