Skip to content

Commit

Permalink
feat: support lighting and shadows for scatter and mesh
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenbreddels committed May 17, 2021
1 parent 8459407 commit e0689f2
Show file tree
Hide file tree
Showing 17 changed files with 15,132 additions and 228 deletions.
324 changes: 315 additions & 9 deletions ipyvolume/pylab.py

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions ipyvolume/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import numpy as np
import pytest
import pythreejs

import ipyvolume
import ipyvolume.pylab as p3
Expand Down Expand Up @@ -449,3 +450,82 @@ def test_datasets():
ipyvolume.datasets.aquariusA2.fetch()
ipyvolume.datasets.hdz2000.fetch()
ipyvolume.datasets.zeldovich.fetch()


def test_mesh_material():
x = [0, 1]
scatter = ipv.scatter(x, x, x)
assert isinstance(scatter.material, pythreejs.ShaderMaterial)
scatter = ipv.scatter(x, x, x, lighting_model='PHYSICAL')
assert isinstance(scatter.material, pythreejs.MeshPhysicalMaterial)
# TODO: test passed parameters and PHONG/LAMBERT
# TODO: test plot_trisurf


def test_light_components():
ambient = ipyvolume.light_ambient()
assert ambient.type == 'AmbientLight'
assert ambient.color == 'white'
assert ambient.intensity == 1

hemisphere = ipyvolume.light_hemisphere()
assert hemisphere.type == 'HemisphereLight'
assert hemisphere.color == 'white'
assert hemisphere.groundColor == 'red'
assert hemisphere.intensity == 1
assert hemisphere.position[0] == 0
assert hemisphere.position[1] == 1
assert hemisphere.position[2] == 0

directional = ipyvolume.light_directional()
assert directional.color == 'white'
assert directional.intensity == 1
assert directional.position[0] == 10
assert directional.position[1] == 10
assert directional.position[2] == 10
assert directional.target.position[0] == 0
assert directional.target.position[1] == 0
assert directional.target.position[2] == 0
assert directional.castShadow is True
assert directional.shadow.bias == -0.0008
assert directional.shadow.radius == 1
assert directional.shadow.camera.near == 0.1
assert directional.shadow.camera.far == 100
assert directional.shadow.mapSize[0] == 1024
assert directional.shadow.camera.left == -10 / 2
assert directional.shadow.camera.right == 10 / 2
assert directional.shadow.camera.top == 10 / 2
assert directional.shadow.camera.bottom == -10 / 2

spot = ipyvolume.light_spot()
assert spot.color == 'white'
assert spot.intensity == 1
assert spot.position[0] == 10
assert spot.position[1] == 10
assert spot.position[2] == 10
assert spot.target.position[0] == 0
assert spot.target.position[1] == 0
assert spot.target.position[2] == 0
assert spot.shadow.camera.fov == 90
assert spot.castShadow is True
assert spot.shadow.mapSize[0] == 1024
assert spot.shadow.bias == -0.0008
assert spot.shadow.radius == 1
assert spot.shadow.camera.near == 0.5
assert spot.shadow.camera.far == 5000

point = ipyvolume.light_point()
assert point.color == 'white'
assert point.intensity == 1
assert point.position[0] == 10
assert point.position[1] == 10
assert point.position[2] == 10
assert point.shadow.camera.fov == 90
assert point.castShadow is True
assert point.distance == 0
assert point.decay == 1
assert point.shadow.mapSize[0] == 1024
assert point.shadow.bias == -0.0008
assert point.shadow.radius == 1
assert point.shadow.camera.near == 0.5
assert point.shadow.camera.far == 5000
45 changes: 33 additions & 12 deletions ipyvolume/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,31 @@ class Mesh(widgets.Widget):
color = Array(default_value="red", allow_none=True).tag(sync=True, **color_serialization)
visible = traitlets.CBool(default_value=True).tag(sync=True)

material = traitlets.Instance(
pythreejs.ShaderMaterial, help='A :any:`pythreejs.ShaderMaterial` that is used for the mesh'
).tag(sync=True, **widgets.widget_serialization)
material = traitlets.Union([
traitlets.Instance(pythreejs.ShaderMaterial),
traitlets.Instance(pythreejs.MeshPhysicalMaterial),
traitlets.Instance(pythreejs.MeshPhongMaterial),
traitlets.Instance(pythreejs.MeshLambertMaterial),
], help='A :any:`pythreejs.Material` that is used for the mesh').tag(sync=True, **widgets.widget_serialization)

@traitlets.default('material')
def _default_material(self):
return pythreejs.ShaderMaterial(side=pythreejs.enums.Side.DoubleSide)

line_material = traitlets.Instance(
pythreejs.ShaderMaterial, help='A :any:`pythreejs.ShaderMaterial` that is used for the lines/wireframe'
).tag(sync=True, **widgets.widget_serialization)
line_material = traitlets.Union([
traitlets.Instance(pythreejs.ShaderMaterial),
traitlets.Instance(pythreejs.MeshPhysicalMaterial),
traitlets.Instance(pythreejs.MeshPhongMaterial),
traitlets.Instance(pythreejs.MeshLambertMaterial),
], help='A :any:`pythreejs.Material` that is used for the lines/wireframe').tag(sync=True, **widgets.widget_serialization)

@traitlets.default('line_material')
def _default_line_material(self):
return pythreejs.ShaderMaterial()

cast_shadow = traitlets.CBool(default_value=True).tag(sync=True)
receive_shadow = traitlets.CBool(default_value=True).tag(sync=True)


@widgets.register
class Scatter(widgets.Widget):
Expand Down Expand Up @@ -147,6 +156,9 @@ class Scatter(widgets.Widget):
visible = traitlets.CBool(default_value=True).tag(sync=True)
shader_snippets = traitlets.Dict({'size': '\n'}).tag(sync=True)

cast_shadow = traitlets.CBool(default_value=True).tag(sync=True)
receive_shadow = traitlets.CBool(default_value=True).tag(sync=True)

texture = traitlets.Union(
[
traitlets.Instance(ipywebrtc.MediaStream),
Expand All @@ -157,17 +169,23 @@ class Scatter(widgets.Widget):
]
).tag(sync=True, **texture_serialization)

material = traitlets.Instance(
pythreejs.ShaderMaterial, help='A :any:`pythreejs.ShaderMaterial` that is used for the mesh'
).tag(sync=True, **widgets.widget_serialization)
material = traitlets.Union([
traitlets.Instance(pythreejs.ShaderMaterial),
traitlets.Instance(pythreejs.MeshPhysicalMaterial),
traitlets.Instance(pythreejs.MeshPhongMaterial),
traitlets.Instance(pythreejs.MeshLambertMaterial),
], help='A :any:`pythreejs.Material` that is used for the mesh').tag(sync=True, **widgets.widget_serialization)

@traitlets.default('material')
def _default_material(self):
return pythreejs.ShaderMaterial()

line_material = traitlets.Instance(
pythreejs.ShaderMaterial, help='A :any:`pythreejs.ShaderMaterial` that is used for the lines/wireframe'
).tag(sync=True, **widgets.widget_serialization)
line_material = traitlets.Union([
traitlets.Instance(pythreejs.ShaderMaterial),
traitlets.Instance(pythreejs.MeshPhysicalMaterial),
traitlets.Instance(pythreejs.MeshPhongMaterial),
traitlets.Instance(pythreejs.MeshLambertMaterial),
], help='A :any:`pythreejs.Material` that is used for the lines/wireframe').tag(sync=True, **widgets.widget_serialization)

@traitlets.default('line_material')
def _default_line_material(self):
Expand Down Expand Up @@ -267,6 +285,9 @@ class Figure(ipywebrtc.MediaStream):
volumes = traitlets.List(traitlets.Instance(Volume), [], allow_none=False).tag(
sync=True, **widgets.widget_serialization
)
lights = traitlets.List(traitlets.Instance(pythreejs.Light), [], allow_none=False).tag(
sync=True, **widgets.widget_serialization
)

animation = traitlets.Float(1000.0).tag(sync=True)
animation_exponent = traitlets.Float(1.0).tag(sync=True)
Expand Down
Loading

0 comments on commit e0689f2

Please sign in to comment.