Skip to content

Commit

Permalink
More consistent API for environment map orientation (#334)
Browse files Browse the repository at this point in the history
* More consistent API for environment map orientation

* Comments

* Add env map coordinate frame comments
  • Loading branch information
brentyi authored Nov 14, 2024
1 parent 44c1af6 commit 9096d19
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 19 deletions.
4 changes: 2 additions & 2 deletions src/viser/_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,9 +495,9 @@ class EnvironmentMapMessage(Message):
background: bool
background_blurriness: float
background_intensity: float
background_rotation: tuple[float, float, float]
background_wxyz: Tuple[float, float, float, float]
environment_intensity: float
environment_rotation: tuple[float, float, float]
environment_wxyz: Tuple[float, float, float, float]


@dataclasses.dataclass
Expand Down
29 changes: 22 additions & 7 deletions src/viser/_scene_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ def set_up_direction(
(similar to Blender, 3DS Max, ROS, etc), the most common alternative is
+Y (OpenGL, Maya, etc).
In practice, the impact of this can improve (1) the ergonomics of
camera controls, which will default to the same up direction as the
scene, and (2) lighting, because the default lights and environment map
are oriented to match the scene's up direction.
Args:
direction: New up direction. Can either be a string (one of +x, +y,
+z, -x, -y, -z) or a length-3 direction vector.
Expand Down Expand Up @@ -491,9 +496,19 @@ def set_environment_map(
background: bool = False,
background_blurriness: float = 0.0,
background_intensity: float = 1.0,
background_rotation: tuple[float, float, float] = (0.0, 0.0, 0.0),
background_wxyz: tuple[float, float, float, float] | np.ndarray = (
1.0,
0.0,
0.0,
0.0,
),
environment_intensity: float = 1.0,
environment_rotation: tuple[float, float, float] = (0.0, 0.0, 0.0),
environment_wxyz: tuple[float, float, float, float] | np.ndarray = (
1.0,
0.0,
0.0,
0.0,
),
) -> None:
"""Set the environment map for the scene. This will set some lights and background.
Expand All @@ -502,19 +517,19 @@ def set_environment_map(
background: Show or hide the environment map in the background.
background_blurriness: Blur factor of the environment map background (0-1).
background_intensity: Intensity of the background.
background_rotation: Rotation of the background in radians.
background_wxyz: Orientation of the background.
environment_intensity: Intensity of the environment lighting.
environment_rotation: Rotation of the environment lighting in radians.
environment_wxyz: Orientation of the environment lighting.
"""
self._websock_interface.queue_message(
_messages.EnvironmentMapMessage(
hdri=hdri,
background=background,
background_blurriness=background_blurriness,
background_intensity=background_intensity,
background_rotation=background_rotation,
background_wxyz=cast_vector(background_wxyz, 4),
environment_intensity=environment_intensity,
environment_rotation=environment_rotation,
environment_wxyz=cast_vector(environment_wxyz, 4),
)
)

Expand All @@ -526,7 +541,7 @@ def enable_default_lights(self, enabled: bool = True) -> None:
see :meth:`SceneApi.set_environment_map()`.
Args:
enabled: True if user wants default lighting. False is user does
enabled: True if user wants default lighting. False if user does
not want default lighting.
"""
self._websock_interface.queue_message(_messages.EnableLightsMessage(enabled))
Expand Down
50 changes: 48 additions & 2 deletions src/viser/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,52 @@ function DefaultLights() {
);
const environmentMap = viewer.useSceneTree((state) => state.environmentMap);

// Environment map frames:
// - We want the `background_wxyz` and `environment_wxyz` to be in the Viser
// world frame. This is different from the threejs world frame, which should
// not be exposed to the user.
// - `backgroundRotation` and `environmentRotation` for the `Environment` component
// are in the threejs world frame.
const [R_threeworld_world, setR_threeworld_world] = React.useState(
// In Python, this will be set by `set_up_direction()`.
viewer.nodeAttributesFromName.current![""]!.wxyz!,
);
useFrame(() => {
const currentR_threeworld_world =
viewer.nodeAttributesFromName.current![""]!.wxyz!;
if (currentR_threeworld_world !== R_threeworld_world) {
setR_threeworld_world(currentR_threeworld_world);
}
});

const Rquat_threeworld_world = new THREE.Quaternion(
R_threeworld_world[1],
R_threeworld_world[2],
R_threeworld_world[3],
R_threeworld_world[0],
);
const Rquat_world_threeworld = Rquat_threeworld_world.clone().invert();
const backgroundRotation = new THREE.Euler().setFromQuaternion(
new THREE.Quaternion(
environmentMap.background_wxyz[1],
environmentMap.background_wxyz[2],
environmentMap.background_wxyz[3],
environmentMap.background_wxyz[0],
)
.premultiply(Rquat_threeworld_world)
.multiply(Rquat_world_threeworld),
);
const environmentRotation = new THREE.Euler().setFromQuaternion(
new THREE.Quaternion(
environmentMap.environment_wxyz[1],
environmentMap.environment_wxyz[2],
environmentMap.environment_wxyz[3],
environmentMap.environment_wxyz[0],
)
.premultiply(Rquat_threeworld_world)
.multiply(Rquat_world_threeworld),
);

let envMapNode;
if (environmentMap.hdri === null) {
envMapNode = null;
Expand All @@ -510,9 +556,9 @@ function DefaultLights() {
background={environmentMap.background}
backgroundBlurriness={environmentMap.background_blurriness}
backgroundIntensity={environmentMap.background_intensity}
backgroundRotation={environmentMap.background_rotation}
backgroundRotation={backgroundRotation}
environmentIntensity={environmentMap.environment_intensity}
environmentRotation={environmentMap.environment_rotation}
environmentRotation={environmentRotation}
/>
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/viser/client/src/SceneTreeState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ export function useSceneTreeState(
background: false,
background_blurriness: 0,
background_intensity: 1,
background_rotation: [0, 0, 0],
background_wxyz: [1, 0, 0, 0],
environment_intensity: 1,
environment_rotation: [0, 0, 0],
environment_wxyz: [1, 0, 0, 0],
},
setClickable: (name, clickable) =>
set((state) => {
Expand Down
4 changes: 2 additions & 2 deletions src/viser/client/src/WebsocketMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,9 +301,9 @@ export interface EnvironmentMapMessage {
background: boolean;
background_blurriness: number;
background_intensity: number;
background_rotation: [number, number, number];
background_wxyz: [number, number, number, number];
environment_intensity: number;
environment_rotation: [number, number, number];
environment_wxyz: [number, number, number, number];
}
/** Spot light message.
*
Expand Down
7 changes: 3 additions & 4 deletions src/viser/transforms/_so3.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import dataclasses
from typing import Tuple
from typing import NamedTuple, Tuple

import numpy as onp
import numpy.typing as onpt
Expand All @@ -11,8 +11,7 @@
from .utils import broadcast_leading_axes, get_epsilon


@dataclasses.dataclass(frozen=True)
class RollPitchYaw:
class RollPitchYaw(NamedTuple):
"""Struct containing roll, pitch, and yaw Euler angles."""

roll: onpt.NDArray[onp.floating]
Expand Down Expand Up @@ -131,7 +130,7 @@ def as_rpy_radians(self) -> RollPitchYaw:
"""Computes roll, pitch, and yaw angles. Uses the ZYX mobile robot convention.
Returns:
Named tuple containing Euler angles in radians.
NamedTuple containing Euler angles in radians.
"""
return RollPitchYaw(
roll=self.compute_roll_radians(),
Expand Down

0 comments on commit 9096d19

Please sign in to comment.