Ejemplo n.º 1
0
def network_consumer_timer():
    if not share_data.client.is_connected():
        error_msg = "Timer still registered but client disconnected."
        logger.error(error_msg)
        if get_mixer_prefs().env != "production":
            raise RuntimeError(error_msg)
        # Returning None from a timer unregister it
        return None

    # Encapsulate call to share_data.client.network_consumer because
    # if we register it directly, then bpy.app.timers.is_registered(share_data.client.network_consumer)
    # return False...
    # However, with a simple function bpy.app.timers.is_registered works.
    try:
        share_data.client.network_consumer()
    except (ClientDisconnectedException, SendSceneContentFailed) as e:
        logger.warning(e)
        share_data.client = None
        disconnect()
        return None
    except Exception as e:
        logger.error(e, stack_info=True)
        if get_mixer_prefs().env == "development":
            raise

    # Run every 1 / 100 seconds
    return 0.01
Ejemplo n.º 2
0
 def execute(self, context):
     if sys.platform == "win32":
         os.startfile(get_mixer_prefs().statistics_directory)
     else:
         opener = "open" if sys.platform == "darwin" else "xdg-open"
         subprocess.call([opener, get_mixer_prefs().statistics_directory])
     return {"FINISHED"}
Ejemplo n.º 3
0
 def poll_functors(cls, context):
     return [
         poll_is_client_connected,
         poll_already_in_a_room,
         (lambda: get_mixer_prefs().room != "", "Room name cannot be empty"),
         (lambda: get_mixer_prefs().room not in share_data.client.rooms_attributes, "Room already exists"),
     ]
Ejemplo n.º 4
0
def join_room(room_name: str):
    logger.info("join_room")

    assert share_data.client.current_room is None
    BlendData.instance().reset()
    share_data.session_id += 1
    # todo tech debt -> current_room should be set when JOIN_ROOM is received
    # todo _joining_room_name should be set in client timer
    share_data.client.current_room = room_name
    share_data.client._joining_room_name = room_name
    set_client_attributes()
    share_data.client.join_room(room_name)
    share_data.client.send_set_current_scene(bpy.context.scene.name_full)

    share_data.current_statistics = {
        "session_id": share_data.session_id,
        "blendfile": bpy.data.filepath,
        "statsfile": get_stats_filename(share_data.run_id,
                                        share_data.session_id),
        "user": get_mixer_prefs().user,
        "room": room_name,
        "children": {},
    }
    prefs = get_mixer_prefs()
    share_data.auto_save_statistics = prefs.auto_save_statistics
    share_data.statistics_directory = prefs.statistics_directory
    share_data.set_experimental_sync(prefs.experimental_sync)
    share_data.pending_test_update = False

    # join a room <==> want to track local changes
    HandlerManager.set_handlers(True)
Ejemplo n.º 5
0
    def send_mesh(self, obj):
        logger.info("send_mesh %s", obj.name_full)
        mesh = obj.data
        mesh_name = self.get_mesh_name(mesh)
        path = self.get_object_path(obj)

        binary_buffer = common.encode_string(path) + common.encode_string(mesh_name)

        binary_buffer += mesh_api.encode_mesh(
            obj, get_mixer_prefs().send_base_meshes, get_mixer_prefs().send_baked_meshes
        )

        # For now include material slots in the same message, but maybe it should be a separated message
        # like Transform
        material_link_dict = {"OBJECT": 0, "DATA": 1}
        material_links = [material_link_dict[slot.link] for slot in obj.material_slots]
        assert len(material_links) == len(obj.data.materials)
        binary_buffer += struct.pack(f"{len(material_links)}I", *material_links)

        for slot in obj.material_slots:
            if slot.link == "DATA":
                binary_buffer += common.encode_string("")
            else:
                binary_buffer += common.encode_string(slot.material.name if slot.material is not None else "")

        self.add_command(common.Command(MessageType.MESH, binary_buffer, 0))
Ejemplo n.º 6
0
    def execute(self, context):
        assert share_data.client.current_room is None
        if not is_client_connected():
            return {"CANCELLED"}

        join_room(get_mixer_prefs().room, get_mixer_prefs().experimental_sync)

        return {"FINISHED"}
Ejemplo n.º 7
0
    def execute(self, context):

        path = self.filepath
        if not Path(path).is_dir():
            path = str(Path(path).parent)

        for item in get_mixer_prefs().shared_folders:
            if item.shared_folder == path:
                return {"FINISHED"}

        item = get_mixer_prefs().shared_folders.add()
        item.shared_folder = path
        return {"FINISHED"}
Ejemplo n.º 8
0
def connect():
    prefs = get_mixer_prefs()
    logger.info(f"connect to {prefs.host}:{prefs.port}")
    if share_data.client is not None:
        # a server shutdown was not processed
        logger.debug("connect: share_data.client is not None")
        share_data.client = None

    if not create_main_client(prefs.host, prefs.port):
        if is_localhost(prefs.host):
            if prefs.no_start_server:
                raise RuntimeError(
                    f"Cannot connect to existing server at {prefs.host}:{prefs.port} and MIXER_NO_START_SERVER environment variable exists"
                )
            start_local_server()
            if not wait_for_server(prefs.host, prefs.port):
                raise RuntimeError("Unable to start local server")
        else:
            raise RuntimeError(
                f"Unable to connect to remote server {prefs.host}:{prefs.port}"
            )

    assert is_client_connected()

    set_client_attributes()
    HandlerManager._set_connection_handler(True)
Ejemplo n.º 9
0
 def draw(self, context):
     layout = self.layout
     mixer_prefs = get_mixer_prefs()
     layout.prop(
         mixer_prefs, "VRtist", text="Path", icon=("ERROR" if not os.path.exists(mixer_prefs.VRtist) else "NONE")
     )
     layout.operator(bl_operators.LaunchVRtistOperator.bl_idname, text="Launch VRTist")
Ejemplo n.º 10
0
 def poll(cls, context):
     return (
         os.path.isfile(get_mixer_prefs().VRtist)
         and is_client_connected()
         and share_data.client.current_room is not None
         and share_data.client.client_id is not None
     )
Ejemplo n.º 11
0
    def execute(self, context):
        bpy.data.window_managers["WinMan"].mixer.send_base_meshes = False
        mixer_prefs = get_mixer_prefs()
        if not share_data.client.current_room:
            try:
                connect()
            except Exception as e:
                self.report({"ERROR"}, f"vrtist.launch connect error : {e}")
                return {"CANCELLED"}

            join_room(mixer_prefs.room, mixer_prefs.experimental)

        args = [
            mixer_prefs.VRtist,
            "--room",
            share_data.client.current_room,
            "--hostname",
            mixer_prefs.host,
            "--port",
            str(mixer_prefs.port),
            "--master",
            str(share_data.client.client_id),
        ]
        subprocess.Popen(args,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT,
                         shell=False)
        return {"FINISHED"}
Ejemplo n.º 12
0
def draw_advanced_settings_ui(layout: bpy.types.UILayout):
    mixer_prefs = get_mixer_prefs()
    layout.prop(mixer_prefs, "data_directory", text="Data Directory")
    layout.prop(mixer_prefs, "ignore_version_check")
    layout.prop(mixer_prefs, "log_level")
    layout.prop(mixer_prefs, "show_server_console")
    layout.prop(mixer_prefs, "vrtist_protocol")
Ejemplo n.º 13
0
def connect():
    logger.info("connect")
    BlendData.instance().reset()
    if share_data.client is not None:
        # a server shutdown was not processed
        logger.debug("connect: share_data.client is not None")
        share_data.client = None

    prefs = get_mixer_prefs()
    if not create_main_client(prefs.host, prefs.port):
        if is_localhost(prefs.host):
            start_local_server()
            if not wait_for_server(prefs.host, prefs.port):
                logger.error("Unable to start local server")
                return False
        else:
            logger.error("Unable to connect to remote server %s:%s",
                         prefs.host, prefs.port)
            return False

    assert is_client_connected()

    set_client_attributes()

    return True
Ejemplo n.º 14
0
def send_scene_content():
    """
    Initial data send to fill a new room.
    """

    from mixer.handlers import HandlerManager, send_scene_data_to_server

    if get_mixer_prefs().no_send_scene_content:
        return

    with HandlerManager(False):
        # mesh baking may trigger depsgraph_updatewhen more than one view layer and
        # cause to reenter send_scene_data_to_server() and send duplicate messages

        share_data.clear_before_state()
        share_data.init_proxy()
        share_data.client.send_group_begin()

        # Temporary waiting for material sync. Should move to send_scene_data_to_server
        for material in bpy.data.materials:
            share_data.client.send_material(material)

        send_scene_data_to_server(None, None)

        shot_manager.send_scene()
        share_data.client.send_frame_start_end(bpy.context.scene.frame_start, bpy.context.scene.frame_end)
        share_data.start_frame = bpy.context.scene.frame_start
        share_data.end_frame = bpy.context.scene.frame_end
        share_data.client.send_frame(bpy.context.scene.frame_current)

        share_data.client.send_group_end()
Ejemplo n.º 15
0
def draw_developer_settings_ui(layout: bpy.types.UILayout):
    mixer_prefs = get_mixer_prefs()
    layout.prop(mixer_prefs, "statistics_directory", text="Stats Directory")
    layout.operator(bl_operators.OpenStatsDirOperator.bl_idname,
                    text="Open Directory")
    layout.operator(bl_operators.WriteStatisticsOperator.bl_idname,
                    text="Write Statistics")
    layout.prop(mixer_prefs,
                "auto_save_statistics",
                text="Auto Save Statistics")
    layout.prop(mixer_prefs,
                "no_send_scene_content",
                text="No send_scene_content")
    layout.prop(mixer_prefs,
                "no_start_server",
                text="Do not start server on connect")
    layout.prop(mixer_prefs, "send_base_meshes", text="Send Base Meshes")
    layout.prop(mixer_prefs, "send_baked_meshes", text="Send Baked Meshes")
    layout.prop(mixer_prefs, "commands_send_interval")

    box = layout.box().column()
    box.label(text="Gizmos")
    box.prop(mixer_prefs, "display_own_gizmos")
    box.prop(mixer_prefs, "display_frustums_gizmos")
    box.prop(mixer_prefs, "display_names_gizmos")
    box.prop(mixer_prefs, "display_ids_gizmos")
    box.prop(mixer_prefs, "display_selections_gizmos")
Ejemplo n.º 16
0
    def draw(self, context):
        layout = self.layout.column()

        mixer_prefs = get_mixer_prefs()

        draw_user_settings_ui(layout.row())

        if not self.connected():
            draw_connection_settings_ui(layout.row())
            layout.operator(bl_operators.ConnectOperator.bl_idname, text="Connect")
        else:
            layout.label(
                text=f"Connected to {mixer_prefs.host}:{mixer_prefs.port} with ID {share_data.client.client_id}"
            )
            layout.operator(bl_operators.DisconnectOperator.bl_idname, text="Disconnect")

            if not share_data.client.current_room:
                split = layout.split(factor=0.6)
                split.prop(mixer_prefs, "room", text="Room")
                split.operator(bl_operators.CreateRoomOperator.bl_idname)
            else:
                split = layout.split(factor=0.6)
                split.label(text=f"Room: {share_data.client.current_room}")
                split.label(text=f"Join: {get_mixer_props().joining_percentage * 100:.2f} %")
                split.operator(bl_operators.LeaveRoomOperator.bl_idname, text="Leave Room")

            self.draw_rooms(layout)
            self.draw_users(layout)

        self.draw_advanced_options(layout)
        self.draw_developer_options(layout)
Ejemplo n.º 17
0
def set_client_attributes():
    prefs = get_mixer_prefs()
    username = prefs.user
    usercolor = prefs.color
    share_data.client.set_client_attributes(
        {ClientAttributes.USERNAME: username, ClientAttributes.USERCOLOR: list(usercolor)}
    )
Ejemplo n.º 18
0
def join_room(room_name: str, vrtist_protocol: bool = False, shared_folders=None, ignore_version_check: bool = False):
    prefs = get_mixer_prefs()
    logger.warning(f"join: room: {room_name}, user: {prefs.user}")

    for line in tech_infos():
        logger.warning(line)

    assert share_data.client.current_room is None
    share_data.session_id += 1
    # todo tech debt -> current_room should be set when JOIN_ROOM is received
    # todo _joining_room_name should be set in client timer
    share_data.client.current_room = room_name
    share_data.client._joining_room_name = room_name
    set_client_attributes()
    blender_version = bpy.app.version_string
    mixer_version = mixer.display_version
    share_data.client.join_room(room_name, blender_version, mixer_version, ignore_version_check, not vrtist_protocol)

    if shared_folders is None:
        shared_folders = []
    share_data.init_protocol(vrtist_protocol, shared_folders)
    share_data.pending_test_update = False

    # join a room <==> want to track local changes
    HandlerManager.set_handlers(True)
Ejemplo n.º 19
0
def users_frustrum_draw():
    prefs = get_mixer_prefs()

    if not prefs.display_frustums_gizmos or share_data.client.current_room is None:
        return

    import bgl
    import gpu
    from gpu_extras.batch import batch_for_shader

    shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
    shader.bind()

    bgl.glLineWidth(1.5)
    bgl.glEnable(bgl.GL_DEPTH_TEST)
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glEnable(bgl.GL_LINE_SMOOTH)
    bgl.glPointSize(4)

    indices = ((1, 2), (2, 3), (3, 4), (4, 1), (0, 1), (0, 2), (0, 3), (0, 4), (0, 5))

    def per_user_callback(user_dict):
        user_color = user_dict.get(ClientAttributes.USERCOLOR, DEFAULT_COLOR)
        shader.uniform_float("color", (*user_color, 1))
        return True

    def per_frustum_callback(user_dict, frustum):
        position = [tuple(coord) for coord in frustum]
        batch = batch_for_shader(shader, "LINES", {"pos": position}, indices=indices)
        batch.draw(shader)

        batch = batch_for_shader(shader, "POINTS", {"pos": position}, indices=(5,))
        batch.draw(shader)

    users_frustrum_draw_iteration(per_user_callback, per_frustum_callback)
Ejemplo n.º 20
0
def users_selection_draw():
    import bgl
    import gpu
    from gpu_extras.batch import batch_for_shader

    prefs = get_mixer_prefs()

    if not prefs.display_selections_gizmos or share_data.client.current_room is None:
        return

    shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR")
    shader.bind()

    bgl.glLineWidth(1.5)
    bgl.glEnable(bgl.GL_DEPTH_TEST)
    bgl.glEnable(bgl.GL_BLEND)
    bgl.glEnable(bgl.GL_LINE_SMOOTH)

    indices = ((0, 1), (1, 2), (2, 3), (0, 3), (4, 5), (5, 6), (6, 7), (4, 7), (0, 4), (1, 5), (2, 6), (3, 7))

    def per_user_callback(user_dict):
        user_color = user_dict.get(ClientAttributes.USERCOLOR, DEFAULT_COLOR)
        shader.uniform_float("color", (*user_color, 1))
        return True

    def per_object_callback(user_dict, object, matrix, local_bbox):
        bbox_corners = [matrix @ Vector(corner) for corner in local_bbox]

        batch = batch_for_shader(shader, "LINES", {"pos": bbox_corners}, indices=indices)
        batch.draw(shader)

    users_selection_draw_iteration(per_user_callback, per_object_callback)
Ejemplo n.º 21
0
    def execute(self, context):
        assert not share_data.client.current_room
        share_data.set_dirty()

        props = get_mixer_props()
        room_index = props.room_index
        room = props.rooms[room_index].name
        logger.warning(f"JoinRoomOperator.execute({room})")
        room_attributes = get_selected_room_dict()
        logger.warning(
            f"Client Blender version: {room_attributes.get(RoomAttributes.BLENDER_VERSION, '')}"
        )
        logger.warning(
            f"Client Mixer version: {room_attributes.get(RoomAttributes.MIXER_VERSION, '')}"
        )

        clear_undo_history()

        mixer_prefs = get_mixer_prefs()
        shared_folders = []
        for item in mixer_prefs.shared_folders:
            shared_folders.append(item.shared_folder)
        join_room(
            room,
            not room_attributes.get(RoomAttributes.GENERIC_PROTOCOL, True),
            shared_folders,
            mixer_prefs.ignore_version_check,
        )

        return {"FINISHED"}
Ejemplo n.º 22
0
    def execute(self, context):
        prefs = get_mixer_prefs()
        try:
            self.report({"INFO"},
                        f'Connecting to "{prefs.host}:{prefs.port}" ...')
            try:
                connect()
            except Exception as e:
                self.report({"ERROR"}, f"mixer.connect error : {e}")
                return {"CANCELLED"}

            self.report({"INFO"},
                        f'Connected to "{prefs.host}:{prefs.port}" ...')
        except socket.gaierror as e:
            msg = f'Cannot connect to "{prefs.host}": invalid host name or address'
            self.report({"ERROR"}, msg)
            if prefs.env != "production":
                raise e
        except Exception as e:
            self.report({"ERROR"}, repr(e))
            if prefs.env != "production":
                raise e
            return {"CANCELLED"}

        return {"FINISHED"}
Ejemplo n.º 23
0
    def draw(self, context):
        layout = self.layout.column()
        mixer_prefs = get_mixer_prefs()

        draw_user_settings_ui(layout.row())

        layout.separator(factor=0.2)
        split = layout.split(factor=0.258, align=False)
        split.label(text="Host:")
        split.prop(mixer_prefs, "host", text="")

        split = layout.split(factor=0.258, align=False)
        split.label(text="Room:")
        split.prop(mixer_prefs, "room", text="")
        layout.separator(factor=1.0)

        row = layout.row()
        row.scale_y = 1.5
        row.operator(bl_operators.LaunchVRtistOperator.bl_idname,
                     text="Launch VRTist")

        layout.separator(factor=1)
        layout.prop(mixer_prefs,
                    "VRtist",
                    text="Path",
                    icon=("ERROR" if not os.path.exists(mixer_prefs.VRtist)
                          else "NONE"))
        layout.prop(mixer_prefs, "VRtist_suffix", text="Save Suffix")
        layout.separator(factor=0.5)
Ejemplo n.º 24
0
def users_selection_draw_iteration(per_user_callback,
                                   per_object_callback,
                                   collection_detail=True):
    if share_data.client is None:
        return

    prefs = get_mixer_prefs()

    for user_dict in share_data.client.clients_attributes.values():
        scenes = user_dict.get(ClientAttributes.USERSCENES, None)
        if not scenes:
            continue

        user_id = user_dict[ClientAttributes.ID]
        user_room = user_dict[ClientAttributes.ROOM]
        if (not prefs.display_own_gizmos and share_data.client.client_id
                == user_id) or share_data.client.current_room != user_room:
            continue  # don't draw my own selection or selection from users outside my room

        if not per_user_callback(user_dict):
            continue

        scene_dict = scenes.get(bpy.context.scene.name_full)
        if scene_dict is None:
            return

        selected_names = scene_dict.get(
            ClientAttributes.USERSCENES_SELECTED_OBJECTS, None)
        if selected_names is None:
            return

        selected_objects = (obj for obj in bpy.data.objects
                            if obj.name_full in set(selected_names))
        for obj in selected_objects:
            objects = [obj]
            parent_matrix = IDENTITY_MATRIX

            if obj.type == "EMPTY" and obj.instance_collection is not None:
                if collection_detail:
                    objects = obj.instance_collection.objects
                else:
                    objects = []
                parent_matrix = obj.matrix_world

                per_object_callback(user_dict, obj,
                                    obj.matrix_world @ BBOX_SCALE_MATRIX,
                                    DEFAULT_BBOX)

            for obj in objects:
                bbox = obj.bound_box

                diag = Vector(bbox[2]) - Vector(bbox[4])
                if diag.length_squared == 0:
                    bbox = DEFAULT_BBOX

                per_object_callback(
                    user_dict, obj,
                    parent_matrix @ obj.matrix_world @ BBOX_SCALE_MATRIX, bbox)
Ejemplo n.º 25
0
    def poll(cls, context):
        # Check VRtist process to auto disconnect
        if cls.vrtist_process is not None and cls.vrtist_process.poll() is not None:
            cls.vrtist_process = None
            leave_current_room()
            disconnect()

        # Manage button state
        return os.path.isfile(get_mixer_prefs().VRtist)
Ejemplo n.º 26
0
def draw_developer_settings_ui(layout: bpy.types.UILayout):
    mixer_prefs = get_mixer_prefs()
    layout.prop(mixer_prefs, "no_send_scene_content", text="No send_scene_content")
    layout.prop(mixer_prefs, "no_start_server", text="Do not start server on connect")
    layout.prop(mixer_prefs, "send_base_meshes", text="Send Base Meshes")
    layout.prop(mixer_prefs, "send_baked_meshes", text="Send Baked Meshes")
    layout.prop(mixer_prefs, "commands_send_interval")
    layout.prop(mixer_prefs, "display_own_gizmos")
    layout.prop(mixer_prefs, "display_ids_gizmos")
Ejemplo n.º 27
0
    def draw_rooms(self, layout):
        mixer_props = get_mixer_props()

        if collapsable_panel(layout,
                             mixer_props,
                             "display_rooms",
                             text="Server Rooms"):

            # main box should probably be removed
            # layout = layout.row()
            # layout.separator(factor=1.2)
            # layout = layout.column()

            layout = layout.box().column()
            ROOM_UL_ItemRenderer.draw_header(layout)
            layout.template_list("ROOM_UL_ItemRenderer",
                                 "",
                                 mixer_props,
                                 "rooms",
                                 mixer_props,
                                 "room_index",
                                 rows=2)
            row = layout.row()
            row.scale_y = 1.3
            if share_data.client.current_room is None:
                row.operator(bl_operators.JoinRoomOperator.bl_idname,
                             text="Join Selected Room")
            else:
                row.operator(bl_operators.LeaveRoomOperator.bl_idname,
                             depress=True)

            if len(mixer_props.rooms):
                self.draw_current_room_properties(layout)

            prefs = get_mixer_prefs()
            if prefs.display_debugging_tools:
                if collapsable_panel(layout,
                                     mixer_props,
                                     "display_advanced_room_control",
                                     text="Room Debugging Tools"):
                    box = layout.box()
                    col = box.column()
                    col.operator(bl_operators.DeleteRoomOperator.bl_idname)
                    col.operator(bl_operators.DownloadRoomOperator.bl_idname)
                    subbox = col.box()
                    subbox.row().operator(
                        bl_operators.UploadRoomOperator.bl_idname)
                    row = subbox.row()
                    row.prop(mixer_props, "upload_room_name", text="Name")
                    row.prop(
                        mixer_props,
                        "internal_upload_room_filepath",
                        text="File",
                        icon=("ERROR" if not os.path.exists(
                            mixer_props.upload_room_filepath) else "NONE"),
                    )
Ejemplo n.º 28
0
    def execute(self, context):
        from mixer.broadcaster.room_bake import load_room, upload_room

        prefs = get_mixer_prefs()
        props = get_mixer_props()

        attributes, commands = load_room(props.upload_room_filepath)
        upload_room(prefs.host, prefs.port, props.upload_room_name, attributes, commands)

        return {"FINISHED"}
Ejemplo n.º 29
0
def draw_shared_folders_settings_ui(layout: bpy.types.UILayout):
    mixer_props = get_mixer_props()
    mixer_prefs = get_mixer_prefs()
    row = layout.row()
    row.template_list(
        "SHAREDFOLDER_UL_ItemRenderer", "", mixer_prefs, "shared_folders", mixer_props, "shared_folder_index", rows=4
    )
    col = row.column(align=True)
    col.operator(bl_operators.SharedFoldersAddFolderOperator.bl_idname, text="", icon="ADD")
    col.operator(bl_operators.SharedFoldersRemoveFolderOperator.bl_idname, text="", icon="REMOVE")
Ejemplo n.º 30
0
def draw_user_settings_ui(layout: bpy.types.UILayout):
    mixer_prefs = get_mixer_prefs()

    split = layout.split(factor=0.258, align=False)
    split.label(text="User:"******"user", text="")
    sub_row = row.row()
    sub_row.scale_x = 0.4
    sub_row.prop(mixer_prefs, "color", text="")