nerfstudio-project / viser

Web-based 3D visualization + Python
https://viser.studio/latest
Apache License 2.0
833 stars 49 forks source link

Is it possible to visualize 2+ viser scenes side-by-side? #329

Open lahavlipson opened 2 days ago

lahavlipson commented 2 days ago

Is there a way to view multiple viser scenes side-by-side on a single page (e.g., to compare two methods)?

It seems that, currently, each ViserServer object only has one scene and one gui attribute.

Thanks!

Also related, from January: #154

brentyi commented 2 days ago

Hi Lahav!

Unfortunately, we can currently only view one scene per page. To view multiple scenes you'll have to open two windows or tabs.

There are two patterns I can suggest which you may or may not find useful though:

Per-client scenes. If you need multiple scene from one server, the ClientHandle objects also have scene and gui attributes that will apply to only one connected client:

import time
import numpy as np
import viser

def main() -> None:
    server = viser.ViserServer()

    @server.on_client_connect
    def _(client: viser.ClientHandle) -> None:
        client.scene.add_box("/box", np.random.uniform(0, 1, 3), dimensions=(1, 1, 1))

    while True:
        time.sleep(1.0)

if __name__ == "__main__":
    main()

Synchronizing cameras across multiple servers. If you launch multiple servers from a single script, it can be nice when comparing things to synchronize cameras across them:

import itertools
import time
import viser

def main() -> None:
    """Example for launching two Viser servers that sync all client cameras."""
    server1 = viser.ViserServer()
    server2 = viser.ViserServer()

    server1.scene.world_axes.visible = True
    server2.scene.world_axes.visible = True

    # Which client is controlling the camera.
    client_in_control: viser.ClientHandle | None = None
    last_camera_move = time.time()

    @server1.on_client_connect
    @server2.on_client_connect
    def _(client: viser.ClientHandle) -> None:
        @client.camera.on_update
        def _(_) -> None:
            nonlocal client_in_control
            nonlocal last_camera_move

            # Only allow one client to control the camera at a time.
            if client_in_control is not client and time.time() - last_camera_move < 0.1:
                return
            client_in_control = client
            last_camera_move = time.time()

            # Update the camera for all other clients.
            for other_client in itertools.chain(
                server1.get_clients().values(), server2.get_clients().values()
            ):
                if other_client is not client:
                    with other_client.atomic():
                        other_client.camera.wxyz = client.camera.wxyz
                        other_client.camera.position = client.camera.position

    while True:
        time.sleep(1.0)

if __name__ == "__main__":
    main()