Skip to content

Commit

Permalink
Add .near and .far props to camera handles (#356)
Browse files Browse the repository at this point in the history
* Add `.near` and `.far` props to camera handles

* Bump up min distance
  • Loading branch information
brentyi authored Dec 12, 2024
1 parent 9f8edaa commit e5bd814
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 7 deletions.
23 changes: 23 additions & 0 deletions docs/source/examples/05_camera_commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
23 changes: 23 additions & 0 deletions examples/05_camera_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 16 additions & 0 deletions src/viser/_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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."""
Expand Down
50 changes: 45 additions & 5 deletions src/viser/_viser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -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,
)

Expand Down
5 changes: 3 additions & 2 deletions src/viser/client/src/CameraControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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],
});
Expand Down Expand Up @@ -247,8 +249,7 @@ export function SynchronizedCameraControls() {
return (
<CameraControls
ref={viewer.cameraControlRef}
minDistance={0.1}
maxDistance={200.0}
minDistance={0.01}
dollySpeed={0.3}
smoothTime={0.05}
draggingSmoothTime={0.0}
Expand Down
12 changes: 12 additions & 0 deletions src/viser/client/src/MessageHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,18 @@ function useMessageHandler() {
viewer.sendCameraRef.current !== null && viewer.sendCameraRef.current();
return;
}
case "SetCameraNearMessage": {
const camera = viewer.cameraRef.current!;
camera.near = message.near;
camera.updateProjectionMatrix();
return;
}
case "SetCameraFarMessage": {
const camera = viewer.cameraRef.current!;
camera.far = message.far;
camera.updateProjectionMatrix();
return;
}
case "SetOrientationMessage": {
const attr = viewer.nodeAttributesFromName.current;
if (attr[message.name] === undefined) attr[message.name] = {};
Expand Down
20 changes: 20 additions & 0 deletions src/viser/client/src/WebsocketMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,8 @@ export interface ViewerCameraMessage {
wxyz: [number, number, number, number];
position: [number, number, number];
fov: number;
near: number;
far: number;
aspect: number;
look_at: [number, number, number];
up_direction: [number, number, number];
Expand Down Expand Up @@ -889,6 +891,22 @@ export interface SetCameraLookAtMessage {
type: "SetCameraLookAtMessage";
look_at: [number, number, number];
}
/** Server -> 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)
Expand Down Expand Up @@ -1201,6 +1219,8 @@ export type Message =
| SetCameraPositionMessage
| SetCameraUpDirectionMessage
| SetCameraLookAtMessage
| SetCameraNearMessage
| SetCameraFarMessage
| SetCameraFovMessage
| SetOrientationMessage
| SetPositionMessage
Expand Down

0 comments on commit e5bd814

Please sign in to comment.