webgpu 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- webgpu/__init__.py +12 -0
- webgpu/_version.py +21 -0
- webgpu/camera.py +189 -0
- webgpu/canvas.py +144 -0
- webgpu/clipping.py +137 -0
- webgpu/colormap.py +325 -0
- webgpu/draw.py +35 -0
- webgpu/font.py +162 -0
- webgpu/fonts.json +52 -0
- webgpu/gpu.py +191 -0
- webgpu/input_handler.py +81 -0
- webgpu/jupyter.py +159 -0
- webgpu/jupyter_pyodide.py +363 -0
- webgpu/labels.py +132 -0
- webgpu/light.py +12 -0
- webgpu/lilgui.py +73 -0
- webgpu/link/__init__.py +3 -0
- webgpu/link/base.py +431 -0
- webgpu/link/link.js +431 -0
- webgpu/link/proxy.py +81 -0
- webgpu/link/websocket.py +115 -0
- webgpu/main.py +177 -0
- webgpu/platform.py +129 -0
- webgpu/render_object.py +155 -0
- webgpu/scene.py +201 -0
- webgpu/shaders/__init__.py +0 -0
- webgpu/shaders/camera.wgsl +21 -0
- webgpu/shaders/clipping.wgsl +35 -0
- webgpu/shaders/colormap.wgsl +60 -0
- webgpu/shaders/font.wgsl +53 -0
- webgpu/shaders/light.wgsl +9 -0
- webgpu/shaders/text.wgsl +57 -0
- webgpu/shaders/triangulation.wgsl +34 -0
- webgpu/shaders/vector.wgsl +118 -0
- webgpu/triangles.py +66 -0
- webgpu/uniforms.py +111 -0
- webgpu/utils.py +379 -0
- webgpu/vectors.py +101 -0
- webgpu/webgpu_api.py +1731 -0
- webgpu-0.0.1.dist-info/METADATA +32 -0
- webgpu-0.0.1.dist-info/RECORD +44 -0
- webgpu-0.0.1.dist-info/WHEEL +5 -0
- webgpu-0.0.1.dist-info/licenses/LICENSE +504 -0
- webgpu-0.0.1.dist-info/top_level.txt +1 -0
webgpu/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .clipping import Clipping
|
|
2
|
+
from .colormap import Colormap
|
|
3
|
+
from .font import Font
|
|
4
|
+
from .render_object import RenderObject
|
|
5
|
+
from .scene import Scene
|
|
6
|
+
from .utils import (
|
|
7
|
+
BaseBinding,
|
|
8
|
+
BufferBinding,
|
|
9
|
+
UniformBinding,
|
|
10
|
+
create_bind_group,
|
|
11
|
+
read_shader_file,
|
|
12
|
+
)
|
webgpu/_version.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
|
|
5
|
+
|
|
6
|
+
TYPE_CHECKING = False
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from typing import Tuple
|
|
9
|
+
from typing import Union
|
|
10
|
+
|
|
11
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
12
|
+
else:
|
|
13
|
+
VERSION_TUPLE = object
|
|
14
|
+
|
|
15
|
+
version: str
|
|
16
|
+
__version__: str
|
|
17
|
+
__version_tuple__: VERSION_TUPLE
|
|
18
|
+
version_tuple: VERSION_TUPLE
|
|
19
|
+
|
|
20
|
+
__version__ = version = '0.0.1'
|
|
21
|
+
__version_tuple__ = version_tuple = (0, 0, 1)
|
webgpu/camera.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from .uniforms import BaseBinding, Binding, UniformBase, ct
|
|
4
|
+
from .utils import read_shader_file
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CameraUniforms(UniformBase):
|
|
8
|
+
"""Uniforms class, derived from ctypes.Structure to ensure correct memory layout"""
|
|
9
|
+
|
|
10
|
+
_binding = Binding.CAMERA
|
|
11
|
+
|
|
12
|
+
_fields_ = [
|
|
13
|
+
("model_view", ct.c_float * 16),
|
|
14
|
+
("model_view_projection", ct.c_float * 16),
|
|
15
|
+
("normal_mat", ct.c_float * 16),
|
|
16
|
+
("aspect", ct.c_float),
|
|
17
|
+
("padding", ct.c_uint32 * 3),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Transform:
|
|
22
|
+
def __init__(self):
|
|
23
|
+
self._mat = np.identity(4)
|
|
24
|
+
self._rot_mat = np.identity(4)
|
|
25
|
+
self._center = (0.5, 0.5, 0)
|
|
26
|
+
self._scale = 1
|
|
27
|
+
|
|
28
|
+
def translate(self, dx=0.0, dy=0.0, dz=0.0):
|
|
29
|
+
translation = np.array([[1, 0, 0, dx], [0, 1, 0, dy], [0, 0, 1, dz], [0, 0, 0, 1]])
|
|
30
|
+
self._mat = translation @ self._mat
|
|
31
|
+
|
|
32
|
+
def scale(self, s):
|
|
33
|
+
self._scale *= s
|
|
34
|
+
|
|
35
|
+
def rotate(self, ang_x, ang_y=0):
|
|
36
|
+
rx = np.radians(ang_x)
|
|
37
|
+
cx = np.cos(rx)
|
|
38
|
+
sx = np.sin(rx)
|
|
39
|
+
|
|
40
|
+
rotation_x = np.array(
|
|
41
|
+
[
|
|
42
|
+
[1, 0, 0, 0],
|
|
43
|
+
[0, cx, -sx, 0],
|
|
44
|
+
[0, sx, cx, 0],
|
|
45
|
+
[0, 0, 0, 1],
|
|
46
|
+
]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
ry = np.radians(ang_y)
|
|
50
|
+
cy = np.cos(ry)
|
|
51
|
+
sy = np.sin(ry)
|
|
52
|
+
rotation_y = np.array(
|
|
53
|
+
[
|
|
54
|
+
[cy, 0, sy, 0],
|
|
55
|
+
[0, 1, 0, 0],
|
|
56
|
+
[-sy, 0, cy, 0],
|
|
57
|
+
[0, 0, 0, 1],
|
|
58
|
+
]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
self._rot_mat = rotation_x @ rotation_y @ self._rot_mat
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def mat(self):
|
|
65
|
+
return self._mat @ self._rot_mat @ self._scale_mat @ self._center_mat
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def normal_mat(self):
|
|
69
|
+
return self._mat @ self._rot_mat @ self._scale_mat @ self._center_mat
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def _center_mat(self):
|
|
73
|
+
cx, cy, cz = self._center
|
|
74
|
+
return np.array([[1, 0, 0, -cx], [0, 1, 0, -cy], [0, 0, 1, -cz], [0, 0, 0, 1]])
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def _scale_mat(self):
|
|
78
|
+
s = self._scale
|
|
79
|
+
return np.array([[s, 0, 0, 0], [0, s, 0, 0], [0, 0, s, 0], [0, 0, 0, 1]])
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class Camera:
|
|
83
|
+
def __init__(self, canvas):
|
|
84
|
+
self.canvas = canvas
|
|
85
|
+
self.uniforms = CameraUniforms(canvas.device)
|
|
86
|
+
self.transform = Transform()
|
|
87
|
+
self._render_function = None
|
|
88
|
+
self._is_moving = False
|
|
89
|
+
self._is_rotating = False
|
|
90
|
+
|
|
91
|
+
canvas.on_resize(self._update_uniforms)
|
|
92
|
+
|
|
93
|
+
def get_bindings(self) -> list[BaseBinding]:
|
|
94
|
+
return self.uniforms.get_bindings()
|
|
95
|
+
|
|
96
|
+
def get_shader_code(self):
|
|
97
|
+
return read_shader_file("camera.wgsl", __file__)
|
|
98
|
+
|
|
99
|
+
def __del__(self):
|
|
100
|
+
del self.uniforms
|
|
101
|
+
|
|
102
|
+
def register_callbacks(self, input_handler, redraw_function):
|
|
103
|
+
self._render_function = redraw_function
|
|
104
|
+
input_handler.on_mousedown(self._on_mousedown)
|
|
105
|
+
input_handler.on_mouseup(self._on_mouseup)
|
|
106
|
+
input_handler.on_mouseout(self._on_mouseup)
|
|
107
|
+
input_handler.on_mousemove(self._on_mousemove)
|
|
108
|
+
input_handler.on_wheel(self._on_wheel)
|
|
109
|
+
|
|
110
|
+
def unregister_callbacks(self, input_handler):
|
|
111
|
+
input_handler.unregister("mousedown", self._on_mousedown)
|
|
112
|
+
input_handler.unregister("mouseup", self._on_mouseup)
|
|
113
|
+
input_handler.unregister("mouseout", self._on_mouseup)
|
|
114
|
+
input_handler.unregister("mousemove", self._on_mousemove)
|
|
115
|
+
input_handler.unregister("wheel", self._on_wheel)
|
|
116
|
+
|
|
117
|
+
def _on_mousedown(self, ev):
|
|
118
|
+
if ev["button"] == 0:
|
|
119
|
+
self._is_rotating = True
|
|
120
|
+
if ev["button"] == 1:
|
|
121
|
+
self._is_moving = True
|
|
122
|
+
|
|
123
|
+
def _on_mouseup(self, _):
|
|
124
|
+
self._is_moving = False
|
|
125
|
+
self._is_rotating = False
|
|
126
|
+
self._is_zooming = False
|
|
127
|
+
|
|
128
|
+
def _on_wheel(self, ev):
|
|
129
|
+
self.transform.scale(1 - ev["deltaY"] / 1000)
|
|
130
|
+
self._render()
|
|
131
|
+
if hasattr(ev, "preventDefault"):
|
|
132
|
+
ev.preventDefault()
|
|
133
|
+
|
|
134
|
+
def _on_mousemove(self, ev):
|
|
135
|
+
if self._is_rotating:
|
|
136
|
+
s = 0.3
|
|
137
|
+
self.transform.rotate(s * ev["movementY"], s * ev["movementX"])
|
|
138
|
+
self._render()
|
|
139
|
+
if self._is_moving:
|
|
140
|
+
s = 0.01
|
|
141
|
+
self.transform.translate(s * ev["movementX"], -s * ev["movementY"])
|
|
142
|
+
self._render()
|
|
143
|
+
|
|
144
|
+
def _render(self):
|
|
145
|
+
self._update_uniforms()
|
|
146
|
+
if self._render_function:
|
|
147
|
+
self._render_function()
|
|
148
|
+
|
|
149
|
+
def _update_uniforms(self):
|
|
150
|
+
near = 0.1
|
|
151
|
+
far = 10
|
|
152
|
+
fov = 45
|
|
153
|
+
aspect = self.canvas.width / self.canvas.height
|
|
154
|
+
|
|
155
|
+
zoom = 1.0
|
|
156
|
+
top = near * (np.tan(np.radians(fov) / 2)) * zoom
|
|
157
|
+
height = 2 * top
|
|
158
|
+
width = aspect * height
|
|
159
|
+
left = -0.5 * width
|
|
160
|
+
right = left + width
|
|
161
|
+
bottom = top - height
|
|
162
|
+
|
|
163
|
+
x = 2 * near / (right - left)
|
|
164
|
+
y = 2 * near / (top - bottom)
|
|
165
|
+
|
|
166
|
+
a = (right + left) / (right - left)
|
|
167
|
+
b = (top + bottom) / (top - bottom)
|
|
168
|
+
|
|
169
|
+
c = -far / (far - near)
|
|
170
|
+
d = (-far * near) / (far - near)
|
|
171
|
+
|
|
172
|
+
proj_mat = np.array(
|
|
173
|
+
[
|
|
174
|
+
[x, 0, a, 0],
|
|
175
|
+
[0, y, b, 0],
|
|
176
|
+
[0, 0, c, d],
|
|
177
|
+
[0, 0, -1, 0],
|
|
178
|
+
]
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
view_mat = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, -3], [0, 0, 0, 1]])
|
|
182
|
+
model_view = view_mat @ self.transform.mat
|
|
183
|
+
model_view_proj = proj_mat @ model_view
|
|
184
|
+
normal_mat = np.linalg.inv(model_view)
|
|
185
|
+
|
|
186
|
+
self.uniforms.model_view[:] = model_view.transpose().flatten()
|
|
187
|
+
self.uniforms.model_view_projection[:] = model_view_proj.transpose().flatten()
|
|
188
|
+
self.uniforms.normal_mat[:] = normal_mat.flatten()
|
|
189
|
+
self.uniforms.update_buffer()
|
webgpu/canvas.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from . import platform
|
|
4
|
+
from .input_handler import InputHandler
|
|
5
|
+
from .utils import get_device
|
|
6
|
+
from .webgpu_api import *
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def init_webgpu(html_canvas):
|
|
10
|
+
"""Initialize WebGPU, create device and canvas"""
|
|
11
|
+
device = get_device()
|
|
12
|
+
return Canvas(device, html_canvas)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Canvas:
|
|
16
|
+
"""Canvas management class, handles "global" state, like webgpu device, canvas, frame and depth buffer"""
|
|
17
|
+
|
|
18
|
+
device: Device
|
|
19
|
+
depth_format: TextureFormat
|
|
20
|
+
depth_texture: Texture
|
|
21
|
+
multisample_texture: Texture
|
|
22
|
+
multisample: MultisampleState
|
|
23
|
+
|
|
24
|
+
width: int = 0
|
|
25
|
+
height: int = 0
|
|
26
|
+
|
|
27
|
+
_on_resize_callbacks: list[Callable] = []
|
|
28
|
+
|
|
29
|
+
def __init__(self, device, canvas, multisample_count=4):
|
|
30
|
+
|
|
31
|
+
self._on_resize_callbacks = []
|
|
32
|
+
|
|
33
|
+
self.device = device
|
|
34
|
+
self.format = platform.js.navigator.gpu.getPreferredCanvasFormat()
|
|
35
|
+
self.color_target = ColorTargetState(
|
|
36
|
+
format=self.format,
|
|
37
|
+
blend=BlendState(
|
|
38
|
+
color=BlendComponent(
|
|
39
|
+
srcFactor=BlendFactor.one,
|
|
40
|
+
dstFactor=BlendFactor.one_minus_src_alpha,
|
|
41
|
+
operation=BlendOperation.add,
|
|
42
|
+
),
|
|
43
|
+
alpha=BlendComponent(
|
|
44
|
+
srcFactor=BlendFactor.one,
|
|
45
|
+
dstFactor=BlendFactor.one_minus_src_alpha,
|
|
46
|
+
operation=BlendOperation.add,
|
|
47
|
+
),
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
self.canvas = canvas
|
|
52
|
+
|
|
53
|
+
self.context = canvas.getContext("webgpu")
|
|
54
|
+
self.context.configure(
|
|
55
|
+
toJS(
|
|
56
|
+
{
|
|
57
|
+
"device": device.handle,
|
|
58
|
+
"format": self.format,
|
|
59
|
+
"alphaMode": "premultiplied",
|
|
60
|
+
"sampleCount": multisample_count,
|
|
61
|
+
"usage": TextureUsage.RENDER_ATTACHMENT | TextureUsage.COPY_DST,
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self.multisample = MultisampleState(count=multisample_count)
|
|
67
|
+
self.depth_format = TextureFormat.depth24plus
|
|
68
|
+
self.input_handler = InputHandler(canvas)
|
|
69
|
+
|
|
70
|
+
self.resize()
|
|
71
|
+
|
|
72
|
+
# platform.js.webgpuOnResize(canvas, create_proxy(self.resize, True))
|
|
73
|
+
|
|
74
|
+
def on_resize(self, func: Callable):
|
|
75
|
+
self._on_resize_callbacks.append(func)
|
|
76
|
+
|
|
77
|
+
def resize(self, *args):
|
|
78
|
+
canvas = self.canvas
|
|
79
|
+
rect = canvas.getBoundingClientRect()
|
|
80
|
+
width = int(rect.width)
|
|
81
|
+
height = int(rect.height)
|
|
82
|
+
|
|
83
|
+
if width == self.width and height == self.height:
|
|
84
|
+
return False
|
|
85
|
+
|
|
86
|
+
if width == 0 or height == 0:
|
|
87
|
+
return False
|
|
88
|
+
|
|
89
|
+
canvas.width = width
|
|
90
|
+
canvas.height = height
|
|
91
|
+
|
|
92
|
+
self.width = width
|
|
93
|
+
self.height = height
|
|
94
|
+
|
|
95
|
+
device = self.device
|
|
96
|
+
self.target_texture = device.createTexture(
|
|
97
|
+
size=[width, height, 1],
|
|
98
|
+
sampleCount=1,
|
|
99
|
+
format=self.format,
|
|
100
|
+
usage=TextureUsage.RENDER_ATTACHMENT | TextureUsage.COPY_SRC,
|
|
101
|
+
label="target",
|
|
102
|
+
)
|
|
103
|
+
self.multisample_texture = device.createTexture(
|
|
104
|
+
size=[width, height, 1],
|
|
105
|
+
sampleCount=self.multisample.count,
|
|
106
|
+
format=self.format,
|
|
107
|
+
usage=TextureUsage.RENDER_ATTACHMENT,
|
|
108
|
+
label="multisample",
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
self.depth_texture = device.createTexture(
|
|
112
|
+
size=[width, height, 1],
|
|
113
|
+
format=self.depth_format,
|
|
114
|
+
usage=TextureUsage.RENDER_ATTACHMENT,
|
|
115
|
+
label="depth_texture",
|
|
116
|
+
sampleCount=self.multisample.count,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
self.target_texture_view = self.target_texture.createView()
|
|
120
|
+
|
|
121
|
+
for func in self._on_resize_callbacks:
|
|
122
|
+
func()
|
|
123
|
+
|
|
124
|
+
def color_attachments(self, loadOp: LoadOp):
|
|
125
|
+
return [
|
|
126
|
+
RenderPassColorAttachment(
|
|
127
|
+
view=self.multisample_texture.createView(),
|
|
128
|
+
resolveTarget=self.target_texture_view,
|
|
129
|
+
# view=self.context.getCurrentTexture().createView(),
|
|
130
|
+
clearValue=Color(1, 1, 1, 1),
|
|
131
|
+
loadOp=loadOp,
|
|
132
|
+
)
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
def depth_stencil_attachment(self, loadOp: LoadOp):
|
|
136
|
+
return RenderPassDepthStencilAttachment(
|
|
137
|
+
self.depth_texture.createView(),
|
|
138
|
+
depthClearValue=1.0,
|
|
139
|
+
depthLoadOp=loadOp,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def __del__(self):
|
|
143
|
+
# unregister is needed to remove circular references
|
|
144
|
+
self.input_handler.unregister_callbacks()
|
webgpu/clipping.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from .render_object import BaseRenderObject
|
|
4
|
+
from .uniforms import UniformBase, ct
|
|
5
|
+
from .utils import read_shader_file
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Binding:
|
|
9
|
+
CLIPPING = 1
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ClippingUniforms(UniformBase):
|
|
13
|
+
_binding = Binding.CLIPPING
|
|
14
|
+
_fields_ = [
|
|
15
|
+
("plane", ct.c_float * 4),
|
|
16
|
+
("sphere", ct.c_float * 4),
|
|
17
|
+
("mode", ct.c_uint32),
|
|
18
|
+
("padding", ct.c_uint32 * 3),
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def __init__(self, device, mode=0, **kwargs):
|
|
22
|
+
super().__init__(device, mode=mode, **kwargs)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Clipping(BaseRenderObject):
|
|
26
|
+
class Mode:
|
|
27
|
+
DISABLED = 0
|
|
28
|
+
PLANE = 1
|
|
29
|
+
SPHERE = 2
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
mode=Mode.DISABLED,
|
|
34
|
+
center=[0.0, 0.0, 0.0],
|
|
35
|
+
normal=[0.0, -1.0, 0.0],
|
|
36
|
+
radius=1.0,
|
|
37
|
+
):
|
|
38
|
+
self.mode = mode
|
|
39
|
+
self.center = center
|
|
40
|
+
self.normal = normal
|
|
41
|
+
self.radius = radius
|
|
42
|
+
self.callbacks = []
|
|
43
|
+
|
|
44
|
+
def update(self, timestamp):
|
|
45
|
+
if timestamp == self._timestamp:
|
|
46
|
+
return
|
|
47
|
+
self._timestamp = timestamp
|
|
48
|
+
if not hasattr(self, "uniforms"):
|
|
49
|
+
self.uniforms = ClippingUniforms(self.device)
|
|
50
|
+
import numpy as np
|
|
51
|
+
|
|
52
|
+
c, n = (
|
|
53
|
+
np.array(self.center, dtype=np.float32),
|
|
54
|
+
np.array(self.normal, dtype=np.float32),
|
|
55
|
+
)
|
|
56
|
+
if np.linalg.norm(n) == 0:
|
|
57
|
+
n = np.array([0.0, 0.0, -1.0], dtype=np.float32)
|
|
58
|
+
else:
|
|
59
|
+
n = n / np.linalg.norm(n)
|
|
60
|
+
# convert to normal and distance from origin
|
|
61
|
+
d = -np.dot(c, n)
|
|
62
|
+
self.uniforms.mode = self.mode
|
|
63
|
+
for i in range(4):
|
|
64
|
+
self.uniforms.plane[i] = [*n, d][i]
|
|
65
|
+
self.uniforms.sphere[i] = [*c, self.radius][i]
|
|
66
|
+
self.update_buffer()
|
|
67
|
+
|
|
68
|
+
def update_buffer(self):
|
|
69
|
+
self.uniforms.update_buffer()
|
|
70
|
+
|
|
71
|
+
def get_bindings(self):
|
|
72
|
+
return self.uniforms.get_bindings()
|
|
73
|
+
|
|
74
|
+
def get_shader_code(self):
|
|
75
|
+
return read_shader_file("clipping.wgsl", __file__)
|
|
76
|
+
|
|
77
|
+
def get_bounding_box(self) -> tuple[list[float], list[float]] | None:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
def __del__(self):
|
|
81
|
+
if hasattr(self, "uniforms"):
|
|
82
|
+
self.uniforms._buffer.destroy()
|
|
83
|
+
|
|
84
|
+
def add_options_to_gui(self, gui):
|
|
85
|
+
folder = gui.folder("Clipping", closed=True)
|
|
86
|
+
folder.checkbox("enabled", self.mode != self.Mode.DISABLED, self.enable_clipping)
|
|
87
|
+
folder.value("x", self.center[0], self.set_x_value)
|
|
88
|
+
folder.value("y", self.center[1], self.set_y_value)
|
|
89
|
+
folder.value("z", self.center[2], self.set_z_value)
|
|
90
|
+
folder.value("nx", self.normal[0], self.set_nx_value)
|
|
91
|
+
folder.value("ny", self.normal[1], self.set_ny_value)
|
|
92
|
+
folder.value("nz", self.normal[2], self.set_nz_value)
|
|
93
|
+
|
|
94
|
+
def render(self, encoder):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def enable_clipping(self, value):
|
|
98
|
+
self.mode = self.Mode.PLANE if value else self.Mode.DISABLED
|
|
99
|
+
self.update(time.time())
|
|
100
|
+
for cb in self.callbacks:
|
|
101
|
+
cb()
|
|
102
|
+
|
|
103
|
+
def set_x_value(self, value):
|
|
104
|
+
self.center[0] = value
|
|
105
|
+
self.update(time.time())
|
|
106
|
+
for cb in self.callbacks:
|
|
107
|
+
cb()
|
|
108
|
+
|
|
109
|
+
def set_y_value(self, value):
|
|
110
|
+
self.center[1] = value
|
|
111
|
+
self.update(time.time())
|
|
112
|
+
for cb in self.callbacks:
|
|
113
|
+
cb()
|
|
114
|
+
|
|
115
|
+
def set_z_value(self, value):
|
|
116
|
+
self.center[2] = value
|
|
117
|
+
self.update(time.time())
|
|
118
|
+
for cb in self.callbacks:
|
|
119
|
+
cb()
|
|
120
|
+
|
|
121
|
+
def set_nx_value(self, value):
|
|
122
|
+
self.normal[0] = value
|
|
123
|
+
self.update(time.time())
|
|
124
|
+
for cb in self.callbacks:
|
|
125
|
+
cb()
|
|
126
|
+
|
|
127
|
+
def set_ny_value(self, value):
|
|
128
|
+
self.normal[1] = value
|
|
129
|
+
self.update(time.time())
|
|
130
|
+
for cb in self.callbacks:
|
|
131
|
+
cb()
|
|
132
|
+
|
|
133
|
+
def set_nz_value(self, value):
|
|
134
|
+
self.normal[2] = value
|
|
135
|
+
self.update(time.time())
|
|
136
|
+
for cb in self.callbacks:
|
|
137
|
+
cb()
|