diff --git a/docs/source/examples/05_camera_commands.rst b/docs/source/examples/05_camera_commands.rst index 0ab96f0c..82d1f008 100644 --- a/docs/source/examples/05_camera_commands.rst +++ b/docs/source/examples/05_camera_commands.rst @@ -25,6 +25,29 @@ corresponding client automatically. num_frames = 20 + @server.on_client_connect + def _(client: viser.ClientHandle) -> None: + """For each client that connects, create GUI elements for adjusting the + near/far clipping planes.""" + + client.camera.far = 10.0 + + near_slider = client.gui.add_slider( + "Near", min=0.01, max=10.0, step=0.001, initial_value=client.camera.near + ) + far_slider = client.gui.add_slider( + "Far", min=1, max=20.0, step=0.001, initial_value=client.camera.far + ) + + @near_slider.on_update + def _(_) -> None: + client.camera.near = near_slider.value + + @far_slider.on_update + def _(_) -> None: + client.camera.far = far_slider.value + + @server.on_client_connect def _(client: viser.ClientHandle) -> None: """For each client that connects, we create a set of random frames + a click handler for each frame. diff --git a/examples/05_camera_commands.py b/examples/05_camera_commands.py index 744dde64..a0659832 100644 --- a/examples/05_camera_commands.py +++ b/examples/05_camera_commands.py @@ -15,6 +15,29 @@ num_frames = 20 +@server.on_client_connect +def _(client: viser.ClientHandle) -> None: + """For each client that connects, create GUI elements for adjusting the + near/far clipping planes.""" + + client.camera.far = 10.0 + + near_slider = client.gui.add_slider( + "Near", min=0.01, max=10.0, step=0.001, initial_value=client.camera.near + ) + far_slider = client.gui.add_slider( + "Far", min=1, max=20.0, step=0.001, initial_value=client.camera.far + ) + + @near_slider.on_update + def _(_) -> None: + client.camera.near = near_slider.value + + @far_slider.on_update + def _(_) -> None: + client.camera.far = far_slider.value + + @server.on_client_connect def _(client: viser.ClientHandle) -> None: """For each client that connects, we create a set of random frames + a click handler for each frame. diff --git a/src/viser/_messages.py b/src/viser/_messages.py index 6f543e85..ae94bbf4 100644 --- a/src/viser/_messages.py +++ b/src/viser/_messages.py @@ -177,6 +177,8 @@ class ViewerCameraMessage(Message): wxyz: Tuple[float, float, float, float] position: Tuple[float, float, float] fov: float + near: float + far: float aspect: float look_at: Tuple[float, float, float] up_direction: Tuple[float, float, float] @@ -701,6 +703,20 @@ class SetCameraLookAtMessage(Message): look_at: Tuple[float, float, float] +@dataclasses.dataclass +class SetCameraNearMessage(Message): + """Server -> client message to set the camera's near clipping plane.""" + + near: float + + +@dataclasses.dataclass +class SetCameraFarMessage(Message): + """Server -> client message to set the camera's far clipping plane.""" + + far: float + + @dataclasses.dataclass class SetCameraFovMessage(Message): """Server -> client message to set the camera's field of view.""" diff --git a/src/viser/_viser.py b/src/viser/_viser.py index 7025b7ee..e746c2fd 100644 --- a/src/viser/_viser.py +++ b/src/viser/_viser.py @@ -75,6 +75,8 @@ class _CameraHandleState: position: npt.NDArray[np.float64] fov: float aspect: float + near: float + far: float look_at: npt.NDArray[np.float64] up_direction: npt.NDArray[np.float64] update_timestamp: float @@ -92,6 +94,8 @@ def __init__(self, client: ClientHandle) -> None: position=np.zeros(3), fov=0.0, aspect=0.0, + near=0.01, + far=1000.0, look_at=np.zeros(3), up_direction=np.zeros(3), update_timestamp=0.0, @@ -204,6 +208,40 @@ def fov(self, fov: float) -> None: _messages.SetCameraFovMessage(fov) ) + @property + def near(self) -> float: + """Near clipping plane distance. Synchronized automatically when + assigned.""" + assert self._state.update_timestamp != 0.0 + return self._state.near + + @near.setter + def near(self, near: float) -> None: + if np.allclose(self._state.near, near): + return + self._state.near = near + self._state.update_timestamp = time.time() + self._state.client._websock_connection.queue_message( + _messages.SetCameraNearMessage(near) + ) + + @property + def far(self) -> float: + """Far clipping plane distance. Synchronized automatically when + assigned.""" + assert self._state.update_timestamp != 0.0 + return self._state.far + + @far.setter + def far(self, far: float) -> None: + if np.allclose(self._state.far, far): + return + self._state.far = far + self._state.update_timestamp = time.time() + self._state.client._websock_connection.queue_message( + _messages.SetCameraFarMessage(far) + ) + @property def aspect(self) -> float: """Canvas width divided by height. Not assignable.""" @@ -599,11 +637,13 @@ async def handle_camera_message( client, np.array(message.wxyz), np.array(message.position), - message.fov, - message.aspect, - np.array(message.look_at), - np.array(message.up_direction), - time.time(), + fov=message.fov, + aspect=message.aspect, + near=message.near, + far=message.far, + look_at=np.array(message.look_at), + up_direction=np.array(message.up_direction), + update_timestamp=time.time(), camera_cb=client.camera._state.camera_cb, ) diff --git a/src/viser/client/src/CameraControls.tsx b/src/viser/client/src/CameraControls.tsx index 5151781a..217c5142 100644 --- a/src/viser/client/src/CameraControls.tsx +++ b/src/viser/client/src/CameraControls.tsx @@ -96,6 +96,8 @@ export function SynchronizedCameraControls() { position: t_world_camera.toArray(), aspect: three_camera.aspect, fov: (three_camera.fov * Math.PI) / 180.0, + near: three_camera.near, + far: three_camera.far, look_at: [lookAt.x, lookAt.y, lookAt.z], up_direction: [up.x, up.y, up.z], }); @@ -247,8 +249,7 @@ export function SynchronizedCameraControls() { return ( client message to set the camera's near clipping plane. + * + * (automatically generated) + */ +export interface SetCameraNearMessage { + type: "SetCameraNearMessage"; + near: number; +} +/** Server -> client message to set the camera's far clipping plane. + * + * (automatically generated) + */ +export interface SetCameraFarMessage { + type: "SetCameraFarMessage"; + far: number; +} /** Server -> client message to set the camera's field of view. * * (automatically generated) @@ -1201,6 +1219,8 @@ export type Message = | SetCameraPositionMessage | SetCameraUpDirectionMessage | SetCameraLookAtMessage + | SetCameraNearMessage + | SetCameraFarMessage | SetCameraFovMessage | SetOrientationMessage | SetPositionMessage