Skip to content

Commit

Permalink
Rebecca/lights (#277)
Browse files Browse the repository at this point in the history
* commenting out lights test

* blah

* add environment light

* lint checks

* update example rst files

* lint stuff again...

* ruff fix stuff

* unclock lint checks on git

* Various minor tweaks

* ruff

* organize defaultlights

* Match hdri files to drei

* prettier

* snake_case for Python API

* edit environment map presets

* merge changes

* dont suport cast shadows atm

* formatting

* add gizmos to light in example

* get rif of smpl

* change hex digits hehe

* badbad coffee

* prettier

* Update dependencies

* Improved handles, docs updates

* Example tweaks

---------

Co-authored-by: brentyi <[email protected]>
  • Loading branch information
beckyfeng08 and brentyi committed Sep 10, 2024
1 parent ccd821a commit 1b015c1
Show file tree
Hide file tree
Showing 24 changed files with 2,282 additions and 1,209 deletions.
175 changes: 175 additions & 0 deletions docs/source/examples/26_lighting.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
.. Comment: this file is automatically generated by `update_example_docs.py`.
It should not be modified manually.
Lights
==========================================


Visualize a mesh under different lighting conditions. To get the demo data, see ``./assets/download_dragon_mesh.sh``.



.. code-block:: python
:linenos:
import time
from pathlib import Path
import numpy as onp
import trimesh
import viser
import viser.transforms as tf
def main() -> None:
# Load mesh.
mesh = trimesh.load_mesh(str(Path(__file__).parent / "assets/dragon.obj"))
assert isinstance(mesh, trimesh.Trimesh)
mesh.apply_scale(0.05)
vertices = mesh.vertices
faces = mesh.faces
print(f"Loaded mesh with {vertices.shape} vertices, {faces.shape} faces")
# Start Viser server with mesh.
server = viser.ViserServer()
server.scene.add_mesh_simple(
name="/simple",
vertices=vertices,
faces=faces,
wxyz=tf.SO3.from_x_radians(onp.pi / 2).wxyz,
position=(0.0, 0.0, 0.0),
)
server.scene.add_mesh_trimesh(
name="/trimesh",
mesh=mesh,
wxyz=tf.SO3.from_x_radians(onp.pi / 2).wxyz,
position=(0.0, 5.0, 0.0),
)
# adding controls to custom lights in the scene
server.scene.add_transform_controls("/control0")
server.scene.add_label("/control0/label", "Directional")
server.scene.add_transform_controls("/control1")
server.scene.add_label("/control1/label", "Point")
directional_light = server.scene.add_light_directional(
name="/control0/directional_light",
color=(186, 219, 173),
)
point_light = server.scene.add_light_point(
name="/control1/point_light",
color=(192, 255, 238),
intensity=3.0,
)
# Create default light toggle.
gui_default_lights = server.gui.add_checkbox("Default lights", initial_value=True)
gui_default_lights.on_update(
lambda _: server.scene.enable_default_lights(gui_default_lights.value)
)
# Create light control inputs.
with server.gui.add_folder("Directional light"):
gui_directional_color = server.gui.add_rgb(
"Color", initial_value=directional_light.color
)
gui_directional_intensity = server.gui.add_slider(
"Intensity",
min=0.0,
max=20.0,
step=0.01,
initial_value=directional_light.intensity,
)
@gui_directional_color.on_update
def _(_) -> None:
directional_light.color = gui_directional_color.value
@gui_directional_intensity.on_update
def _(_) -> None:
directional_light.intensity = gui_directional_intensity.value
with server.gui.add_folder("Point light"):
gui_point_color = server.gui.add_rgb("Color", initial_value=point_light.color)
gui_point_intensity = server.gui.add_slider(
"Intensity",
min=0.0,
max=20.0,
step=0.01,
initial_value=point_light.intensity,
)
@gui_point_color.on_update
def _(_) -> None:
point_light.color = gui_point_color.value
@gui_point_intensity.on_update
def _(_) -> None:
point_light.intensity = gui_point_intensity.value
# Create GUI elements for controlling environment map.
with server.gui.add_folder("Environment map"):
gui_env_preset = server.gui.add_dropdown(
"Preset",
(
"None",
"apartment",
"city",
"dawn",
"forest",
"lobby",
"night",
"park",
"studio",
"sunset",
"warehouse",
),
initial_value="city",
)
gui_background = server.gui.add_checkbox("Background", False)
gui_bg_blurriness = server.gui.add_slider(
"Bg Blurriness",
min=0.0,
max=1.0,
step=0.01,
initial_value=0.0,
)
gui_bg_intensity = server.gui.add_slider(
"Bg Intensity",
min=0.0,
max=1.0,
step=0.01,
initial_value=1.0,
)
gui_env_intensity = server.gui.add_slider(
"Env Intensity",
min=0.0,
max=1.0,
step=0.01,
initial_value=1.0,
)
def update_environment_map(_) -> None:
server.scene.set_environment_map(
gui_env_preset.value if gui_env_preset.value != "None" else None,
background=gui_background.value,
background_blurriness=gui_bg_blurriness.value,
background_intensity=gui_bg_intensity.value,
environment_intensity=gui_env_intensity.value,
)
gui_env_preset.on_update(update_environment_map)
gui_background.on_update(update_environment_map)
gui_bg_blurriness.on_update(update_environment_map)
gui_bg_intensity.on_update(update_environment_map)
gui_env_intensity.on_update(update_environment_map)
while True:
time.sleep(10.0)
if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions docs/source/scene_handles.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,16 @@ methods like :func:`viser.ViserServer.add_frame()` or

.. autoclass:: viser.GaussianSplatHandle

.. autoclass:: viser.DirectionalLightHandle

.. autoclass:: viser.AmbientLightHandle

.. autoclass:: viser.HemisphereLightHandle

.. autoclass:: viser.PointLightHandle

.. autoclass:: viser.RectAreaLightHandle

.. autoclass:: viser.SpotLightHandle

<!-- prettier-ignore-end -->
165 changes: 165 additions & 0 deletions examples/26_lighting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""Lights
Visualize a mesh under different lighting conditions. To get the demo data, see `./assets/download_dragon_mesh.sh`.
"""

import time
from pathlib import Path

import numpy as onp
import trimesh
import viser
import viser.transforms as tf


def main() -> None:
# Load mesh.
mesh = trimesh.load_mesh(str(Path(__file__).parent / "assets/dragon.obj"))
assert isinstance(mesh, trimesh.Trimesh)
mesh.apply_scale(0.05)
vertices = mesh.vertices
faces = mesh.faces
print(f"Loaded mesh with {vertices.shape} vertices, {faces.shape} faces")

# Start Viser server with mesh.
server = viser.ViserServer()

server.scene.add_mesh_simple(
name="/simple",
vertices=vertices,
faces=faces,
wxyz=tf.SO3.from_x_radians(onp.pi / 2).wxyz,
position=(0.0, 0.0, 0.0),
)
server.scene.add_mesh_trimesh(
name="/trimesh",
mesh=mesh,
wxyz=tf.SO3.from_x_radians(onp.pi / 2).wxyz,
position=(0.0, 5.0, 0.0),
)

# adding controls to custom lights in the scene
server.scene.add_transform_controls("/control0", position=(0.0, 10.0, 5.0))
server.scene.add_label("/control0/label", "Directional")
server.scene.add_transform_controls("/control1", position=(0.0, -5.0, 5.0))
server.scene.add_label("/control1/label", "Point")

directional_light = server.scene.add_light_directional(
name="/control0/directional_light",
color=(186, 219, 173),
)
point_light = server.scene.add_light_point(
name="/control1/point_light",
color=(192, 255, 238),
intensity=30.0,
)

# Create default light toggle.
gui_default_lights = server.gui.add_checkbox("Default lights", initial_value=True)
gui_default_lights.on_update(
lambda _: server.scene.enable_default_lights(gui_default_lights.value)
)

# Create light control inputs.
with server.gui.add_folder("Directional light"):
gui_directional_color = server.gui.add_rgb(
"Color", initial_value=directional_light.color
)
gui_directional_intensity = server.gui.add_slider(
"Intensity",
min=0.0,
max=20.0,
step=0.01,
initial_value=directional_light.intensity,
)

@gui_directional_color.on_update
def _(_) -> None:
directional_light.color = gui_directional_color.value

@gui_directional_intensity.on_update
def _(_) -> None:
directional_light.intensity = gui_directional_intensity.value

with server.gui.add_folder("Point light"):
gui_point_color = server.gui.add_rgb("Color", initial_value=point_light.color)
gui_point_intensity = server.gui.add_slider(
"Intensity",
min=0.0,
max=200.0,
step=0.01,
initial_value=point_light.intensity,
)

@gui_point_color.on_update
def _(_) -> None:
point_light.color = gui_point_color.value

@gui_point_intensity.on_update
def _(_) -> None:
point_light.intensity = gui_point_intensity.value

# Create GUI elements for controlling environment map.
with server.gui.add_folder("Environment map"):
gui_env_preset = server.gui.add_dropdown(
"Preset",
(
"None",
"apartment",
"city",
"dawn",
"forest",
"lobby",
"night",
"park",
"studio",
"sunset",
"warehouse",
),
initial_value="city",
)
gui_background = server.gui.add_checkbox("Background", False)
gui_bg_blurriness = server.gui.add_slider(
"Bg Blurriness",
min=0.0,
max=1.0,
step=0.01,
initial_value=0.0,
)
gui_bg_intensity = server.gui.add_slider(
"Bg Intensity",
min=0.0,
max=1.0,
step=0.01,
initial_value=1.0,
)
gui_env_intensity = server.gui.add_slider(
"Env Intensity",
min=0.0,
max=1.0,
step=0.01,
initial_value=0.1,
)

def update_environment_map(_) -> None:
server.scene.set_environment_map(
gui_env_preset.value if gui_env_preset.value != "None" else None,
background=gui_background.value,
background_blurriness=gui_bg_blurriness.value,
background_intensity=gui_bg_intensity.value,
environment_intensity=gui_env_intensity.value,
)

update_environment_map(None)
gui_env_preset.on_update(update_environment_map)
gui_background.on_update(update_environment_map)
gui_bg_blurriness.on_update(update_environment_map)
gui_bg_intensity.on_update(update_environment_map)
gui_env_intensity.on_update(update_environment_map)

while True:
time.sleep(10.0)


if __name__ == "__main__":
main()
6 changes: 6 additions & 0 deletions src/viser/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,27 @@
from ._icons_enum import IconName as IconName
from ._notification_handle import NotificationHandle as NotificationHandle
from ._scene_api import SceneApi as SceneApi
from ._scene_handles import AmbientLightHandle as AmbientLightHandle
from ._scene_handles import BatchedAxesHandle as BatchedAxesHandle
from ._scene_handles import CameraFrustumHandle as CameraFrustumHandle
from ._scene_handles import DirectionalLightHandle as DirectionalLightHandle
from ._scene_handles import FrameHandle as FrameHandle
from ._scene_handles import GaussianSplatHandle as GaussianSplatHandle
from ._scene_handles import GlbHandle as GlbHandle
from ._scene_handles import Gui3dContainerHandle as Gui3dContainerHandle
from ._scene_handles import HemisphereLightHandle as HemisphereLightHandle
from ._scene_handles import ImageHandle as ImageHandle
from ._scene_handles import LabelHandle as LabelHandle
from ._scene_handles import MeshHandle as MeshHandle
from ._scene_handles import MeshSkinnedBoneHandle as MeshSkinnedBoneHandle
from ._scene_handles import MeshSkinnedHandle as MeshSkinnedHandle
from ._scene_handles import PointCloudHandle as PointCloudHandle
from ._scene_handles import PointLightHandle as PointLightHandle
from ._scene_handles import RectAreaLightHandle as RectAreaLightHandle
from ._scene_handles import SceneNodeHandle as SceneNodeHandle
from ._scene_handles import SceneNodePointerEvent as SceneNodePointerEvent
from ._scene_handles import ScenePointerEvent as ScenePointerEvent
from ._scene_handles import SpotLightHandle as SpotLightHandle
from ._scene_handles import TransformControlsHandle as TransformControlsHandle
from ._viser import CameraHandle as CameraHandle
from ._viser import ClientHandle as ClientHandle
Expand Down
2 changes: 1 addition & 1 deletion src/viser/_gui_handles.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ class GuiInputHandle(_GuiInputHandle[T], Generic[T]):
"""

def on_update(
self: TGuiHandle, func: Callable[[GuiEvent[TGuiHandle]], None]
self: TGuiHandle, func: Callable[[GuiEvent[TGuiHandle]], Any]
) -> Callable[[GuiEvent[TGuiHandle]], None]:
"""Attach a function to call when a GUI input is updated. Happens in a thread."""
self._impl.update_cb.append(func)
Expand Down
Loading

0 comments on commit 1b015c1

Please sign in to comment.