/
model_viewer.py
124 lines (101 loc) · 4.13 KB
/
model_viewer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""\
## Model Viewer
Google has developed the `model-viewer` web component for interactively viewing very large and
detailed 3D models.
"""
import panel as pn
import param
from awesome_panel_extensions.web_component import WebComponent
# pylint: disable=line-too-long
JS = """
<script src="https://unpkg.com/@webcomponents/webcomponentsjs@2.2.7/webcomponents-loader.js"></script>
<script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.js"></script>
<script nomodule src="https://unpkg.com/@google/model-viewer/dist/model-viewer-legacy.js"></script>
<script src="https://unpkg.com/resize-observer-polyfill@1.5.1/dist/ResizeObserver.js"></script>
"""
HTML = """
<model-viewer src="https://modelviewer.dev/shared-assets/models/Astronaut.glb" alt="A 3D model of an astronaut"
auto-rotate camera-controls>
</model-viewer>
"""
MODELS = {
"Astronaut": "https://modelviewer.dev/shared-assets/models/Astronaut.glb",
"Boom Box": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/BoomBox/glTF-Binary/BoomBox.glb",
"Brain Stem": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/BrainStem/glTF-Binary/BrainStem.glb",
"Corset": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/Corset/glTF-Binary/Corset.glb",
"Damaged Helmet": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/DamagedHelmet/glTF-Binary/DamagedHelmet.glb",
"Flight Helmet": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf",
"Lantern": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/Lantern/glTF-Binary/Lantern.glb",
"Monkey": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/Suzanne/glTF/Suzanne.gltf",
"Water Bottles": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/SpecGlossVsMetalRough/glTF-Binary/SpecGlossVsMetalRough.glb",
"Robot Expressive": "https://modelviewer.dev/shared-assets/models/RobotExpressive.glb",
"Transparency Test": "https://modelviewer.dev/shared-assets/models/alpha-blend-litmus.glb",
"Metal Rough Spheres": "https://modelviewer.dev/shared-assets/models/glTF-Sample-Models/2.0/MetalRoughSpheres/glTF/MetalRoughSpheres.gltf",
}
# pylint: disable=line-too-long
SRC_DEFAULT = MODELS["Flight Helmet"]
HEIGHT_DEFAULT = 600
HEIGHT_BOUNDS = (50, 1000)
WIDTH_DEFAULT = 600
WIDTH_BOUNDS = (50, 1000)
BACKGROUND = "#9E9E9E"
PARAMETERS = [
"src",
"height",
"width",
"exposure",
# "auto_rotate",
# "camera_controls",
]
class ModelViewer(WebComponent):
"""A Wired ModelViewer"""
html = param.String(HTML)
attributes_to_watch = param.Dict({"src": "src"})
properties_to_watch = param.Dict(
{
"exposure": "exposure",
"auto-rotate": "auto_rotate",
"camera-controls": "camera_controls",
}
)
src = param.ObjectSelector(default=SRC_DEFAULT, objects=MODELS)
exposure = param.Number(1.0, bounds=(0, 2))
auto_rotate = param.Boolean()
camera_controls = param.Boolean()
height = param.Integer(default=HEIGHT_DEFAULT, bounds=HEIGHT_BOUNDS)
width = param.Integer(default=WIDTH_DEFAULT, bounds=WIDTH_BOUNDS)
style = param.String()
def __init__(self, **params):
super().__init__(**params)
self.css_pane = pn.pane.HTML()
self.js_pane = pn.pane.HTML(JS)
self._update_height_and_width()
def view(self) -> pn.Column:
"""Returns the main view of the ModelViewer
Returns:
pn.Column: The main view of the ModelViewer
"""
return pn.Column(
self,
self.js_pane,
self.css_pane,
sizing_mode="stretch_both",
)
@param.depends("height", "width", watch=True)
def _update_height_and_width(self):
if self.height:
height = self.height
else:
height = HEIGHT_DEFAULT
if self.width:
width = self.width
else:
width = WIDTH_DEFAULT
self.css_pane.object = f"""
<style>
model-viewer {{
height:{height}px;
width:{width}px;
}}
</style>
"""