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.
@@ -0,0 +1,35 @@
1
+ // general uniform structures
2
+ struct ClippingUniforms {
3
+ plane: vec4<f32>,
4
+ sphere: vec4<f32>,
5
+ mode: u32, // 0: disabled, 1: plane, 2: sphere, 3: both
6
+
7
+ padding0: u32,
8
+ padding1: u32,
9
+ padding2: u32,
10
+ };
11
+
12
+ @group(0) @binding(1) var<uniform> u_clipping : ClippingUniforms;
13
+
14
+ fn calcClipping(p: vec3<f32>) -> bool {
15
+ var result: bool = true;
16
+ if( (u_clipping.mode & 0x01u) == 1u) {
17
+ if dot(u_clipping.plane, vec4<f32>(p, 1.0)) > 0. {
18
+ result = false;
19
+ }
20
+ }
21
+ if ((u_clipping.mode & 0x02u) == 0x02u){
22
+ let d = distance(p, u_clipping.sphere.xyz);
23
+ if d > u_clipping.sphere.w {
24
+ result = false;
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+
30
+ fn checkClipping(p: vec3<f32>) {
31
+ if calcClipping(p) == false {
32
+ discard;
33
+ }
34
+ }
35
+
@@ -0,0 +1,60 @@
1
+ @group(0) @binding(6) var u_colormap_texture : texture_1d<f32>;
2
+ @group(0) @binding(7) var u_colormap_sampler : sampler;
3
+ @group(0) @binding(5) var<uniform> u_cmap_uniforms : ColormapUniforms;
4
+
5
+ struct ColormapUniforms {
6
+ min: f32,
7
+ max: f32,
8
+ position_x: f32,
9
+ position_y: f32,
10
+ discrete: u32,
11
+ n_colors: u32,
12
+ width: f32,
13
+ height: f32,
14
+ };
15
+
16
+
17
+ fn getColor(value: f32) -> vec4<f32> {
18
+ var v = (value - u_cmap_uniforms.min) / (u_cmap_uniforms.max - u_cmap_uniforms.min);
19
+ if(u_cmap_uniforms.discrete == 1u) {
20
+ v = f32(u32(f32(u_cmap_uniforms.n_colors) * v));
21
+ v = v / f32(u_cmap_uniforms.n_colors);
22
+ }
23
+ return textureSample(u_colormap_texture, u_colormap_sampler, v);
24
+ }
25
+
26
+ struct VertexOutput {
27
+ @builtin(position) position: vec4<f32>,
28
+ @location(0) val: f32,
29
+ };
30
+
31
+ @vertex
32
+ fn colormap_vertex(@builtin(vertex_index) vertId: u32,
33
+ @builtin(instance_index) trigId: u32) -> VertexOutput {
34
+ var posx = trigId / 2u;
35
+ if(vertId == 2u || (trigId % 2u == 1u && vertId == 1u))
36
+ {
37
+ posx = posx + 1u;
38
+ }
39
+ var posy = u_cmap_uniforms.position_y;
40
+ if(vertId == 0u || (trigId % 2u == 1u && vertId == 1u))
41
+ {
42
+ posy = posy + u_cmap_uniforms.height;
43
+ }
44
+ let position = vec4<f32>(u_cmap_uniforms.position_x +
45
+ f32(posx) * u_cmap_uniforms.width / f32(u_cmap_uniforms.n_colors),
46
+ posy, 0.0, 1.0);
47
+ return VertexOutput(position, f32(posx));
48
+ }
49
+
50
+ @fragment
51
+ fn colormap_fragment(vert: VertexOutput) -> @location(0) vec4<f32> {
52
+ if (u_cmap_uniforms.height == 0.) {
53
+ discard;
54
+ }
55
+ let min = u_cmap_uniforms.min;
56
+ let max = u_cmap_uniforms.max;
57
+ let v = min + (max-min) * vert.val / f32(u_cmap_uniforms.n_colors);
58
+ let color = getColor(v);
59
+ return color;
60
+ }
@@ -0,0 +1,53 @@
1
+ struct FontUniforms {
2
+ width: u32,
3
+ height: u32,
4
+
5
+ width_normalized: f32,
6
+ height_normalized: f32,
7
+ };
8
+
9
+ @group(0) @binding(2) var<uniform> u_font : FontUniforms;
10
+ @group(0) @binding(3) var u_font_texture : texture_2d<f32>;
11
+
12
+ fn fontGetTexCoord(char: u32, vertexId: u32) -> vec2<f32> {
13
+ return vec2<f32>(
14
+ f32((char - 32) * u_font.width),
15
+ f32(u_font.height)
16
+ );
17
+ }
18
+
19
+ struct FontFragmentInput {
20
+ @builtin(position) fragPosition: vec4<f32>,
21
+ @location(0) tex_coord: vec2<f32>,
22
+ };
23
+
24
+ @fragment
25
+ fn fragmentFont(@location(0) tex_coord: vec2<f32>) -> @location(0) vec4<f32> {
26
+ let alpha: f32 = textureLoad(
27
+ u_font_texture,
28
+ vec2i(floor(tex_coord)),
29
+ 0
30
+ ).x;
31
+
32
+ if alpha < 0.01 {
33
+ discard;
34
+ }
35
+
36
+ return vec4(0., 0., 0., alpha);
37
+ }
38
+
39
+ fn fontCalc(char: u32, position: vec4<f32>, vertexId: u32) -> FontFragmentInput {
40
+ var tex_coord = fontGetTexCoord(char, vertexId);
41
+ var p = position;
42
+
43
+ if vertexId == 2 || vertexId == 4 || vertexId == 5 {
44
+ p.y += u_font.height_normalized * p.w;
45
+ tex_coord.y = 0.0;
46
+ }
47
+
48
+ if vertexId == 1 || vertexId == 2 || vertexId == 4 {
49
+ p.x += u_font.width_normalized * p.w;
50
+ tex_coord.x += f32(u_font.width);
51
+ }
52
+ return FontFragmentInput(p, tex_coord);
53
+ }
@@ -0,0 +1,9 @@
1
+ fn lightCalcBrightness(n: vec3f) -> f32 {
2
+ let n4 = cameraMapNormal(n);
3
+ return clamp(dot(normalize(n4.xyz), normalize(vec3<f32>(1., 3., 3.))), .0, 1.) * 0.7 + 0.3;
4
+ }
5
+
6
+ fn lightCalcColor(n: vec3f, color: vec4f) -> vec4f {
7
+ let brightness = lightCalcBrightness(n);
8
+ return vec4f(color.xyz * brightness, color.w);
9
+ }
@@ -0,0 +1,57 @@
1
+ @group(0) @binding(30) var<storage> u_text : Texts;
2
+
3
+ struct Texts {
4
+ n_texts: u32,
5
+ data: array<u32>, // check in python code of label.py on how the data is stored
6
+ };
7
+
8
+ struct TextData {
9
+ pos: vec3f,
10
+ shift: vec2f,
11
+ length: u32,
12
+ ichar: u32,
13
+ char: u32,
14
+ apply_camera: u32,
15
+ };
16
+
17
+ fn textLoadData(i: u32) -> TextData {
18
+ let offset = u_text.n_texts * 4 + i * 2;
19
+ let itext = u_text.data[ offset ];
20
+ let char_data = u_text.data[ offset + 1 ];
21
+ let ichar = extractBits(char_data, 0u, 16u);
22
+ let char = extractBits(char_data, 16u, 8u);
23
+
24
+ let offset_text = itext * 4;
25
+ let pos = vec3f(bitcast<f32>(u_text.data[offset_text]), bitcast<f32>(u_text.data[offset_text + 1]), bitcast<f32>(u_text.data[offset_text + 2]));
26
+ let text_data = u_text.data[offset_text + 3];
27
+ let length = extractBits(text_data, 0u, 16u);
28
+ let apply_camera = extractBits(text_data, 16u, 8u);
29
+
30
+ let x_align = f32(extractBits(text_data, 24u, 2u));
31
+ let y_align = f32(extractBits(text_data, 26u, 2u));
32
+
33
+ let shift = vec2<f32>(-0.5 * x_align, -0.5 * y_align);
34
+
35
+ return TextData(pos, shift, length, ichar, char, apply_camera);
36
+ }
37
+
38
+ @vertex
39
+ fn vertexText(@builtin(vertex_index) vertexId: u32, @builtin(instance_index) charId: u32) -> FontFragmentInput {
40
+ let text = textLoadData(charId);
41
+
42
+ var position = vec4f(text.pos, 1.0);
43
+ if (text.apply_camera != 0) {
44
+ position = cameraMapPoint(text.pos);
45
+ }
46
+
47
+ let w: f32 = u_font.width_normalized * position.w;
48
+ let h: f32 = u_font.height_normalized * position.w;
49
+
50
+ position.x += f32(text.ichar) * w;
51
+
52
+ let shift = text.shift;
53
+ position.x += w * shift.x * f32(text.length);
54
+ position.y += h * shift.y;
55
+
56
+ return fontCalc(text.char, position, vertexId);
57
+ }
@@ -0,0 +1,34 @@
1
+
2
+ @group(0) @binding(90) var<storage> u_vertices : array<f32>;
3
+ @group(0) @binding(91) var<storage> u_normals : array<f32>;
4
+
5
+ struct TriangleFragmentInput {
6
+ @builtin(position) position: vec4<f32>,
7
+ @location(0) p: vec3<f32>,
8
+ @location(1) n: vec3<f32>,
9
+ @location(2) @interpolate(flat) vertId: u32,
10
+ @location(3) @interpolate(flat) trigId: u32,
11
+ };
12
+
13
+ @vertex
14
+ fn vertex_main(@builtin(vertex_index) vertId: u32, @builtin(instance_index) trigId: u32) -> TriangleFragmentInput {
15
+ let point = vec3<f32>(u_vertices[trigId * 9 + vertId * 3],
16
+ u_vertices[trigId * 9 + vertId * 3 + 1],
17
+ u_vertices[trigId * 9 + vertId * 3 + 2]);
18
+ let normal = -vec3<f32>(u_normals[trigId * 9 + vertId * 3],
19
+ u_normals[trigId * 9 + vertId * 3 + 1],
20
+ u_normals[trigId * 9 + vertId * 3 + 2]);
21
+
22
+ let position = cameraMapPoint(point);
23
+ return TriangleFragmentInput(position,
24
+ point,
25
+ normal,
26
+ vertId,
27
+ trigId,
28
+ );
29
+ }
30
+
31
+ @fragment
32
+ fn fragment_main(input: TriangleFragmentInput) -> @location(0) vec4<f32> {
33
+ return lightCalcColor(input.n, getColor(input.vertId, input.trigId));
34
+ }
@@ -0,0 +1,118 @@
1
+
2
+ @group(0) @binding(81) var<storage, read> vec_points: array<f32>;
3
+ @group(0) @binding(82) var<storage, read> vec_vectors: array<f32>;
4
+ @group(0) @binding(83) var<uniform> vec_options: VectorOptions;
5
+
6
+ struct VectorOptions {
7
+ length: f32,
8
+ scale_with_vector_length: u32,
9
+ padding2: f32,
10
+ padding3: f32
11
+ };
12
+
13
+
14
+ fn Cross(a: vec3f, b: vec3f) -> vec3f {
15
+
16
+ return vec3f(
17
+ a[1] * b[2] - a[2] * b[1],
18
+ a[2] * b[0] - a[0] * b[2],
19
+ a[0] * b[1] - a[1] * b[0]
20
+ );
21
+ }
22
+
23
+
24
+ struct VectorFragmentInput {
25
+ @builtin(position) fragPosition: vec4<f32>,
26
+ @location(0) color_val: f32,
27
+ @location(1) n: vec3<f32>
28
+ };
29
+
30
+ fn get_point(index: u32) -> vec3f {
31
+ let i = index * 3u;
32
+ return vec3f(vec_points[i], vec_points[i + 1u], vec_points[i + 2u]);
33
+ }
34
+
35
+ fn get_vector(index: u32) -> vec3f {
36
+ let i = index * 3u;
37
+ return vec3f(vec_vectors[i], vec_vectors[i + 1u], vec_vectors[i + 2u]);
38
+ }
39
+
40
+ // Draw vector cone with triangle strip
41
+
42
+ const cone_points =
43
+ array<vec3f, 4>(vec3f(-sqrt(0.5), -sqrt(0.5), 0.),
44
+ vec3f(sqrt(0.5), -sqrt(0.5), 0.),
45
+ vec3f(0., 1., 0.),
46
+ vec3f(0., 0., 5.));
47
+
48
+ const cone_strip = array<u32, 10>(0u, 1u, 2u, 2u, 3u, 1u, 3u, 0u, 3u, 2u);
49
+ const cone_strip_normals = array<u32, 10>(0u, 0u, 0u, 1u, 4u, 2u, 5u, 3u, 6u, 1u);
50
+
51
+ @vertex
52
+ fn vertex_main(@builtin(vertex_index) index: u32,
53
+ @builtin(instance_index) instance: u32) -> VectorFragmentInput {
54
+ let point = get_point(instance);
55
+ let vector = get_vector(instance);
56
+ // if length(vector) < 0.0001 {
57
+ // return VectorFragmentInput(vec4<f32>(0., 0., 0., 0.),
58
+ // 0., vec3<f32>(0., 0., 0.));
59
+ // }
60
+ let cp = cone_points[cone_strip[index]];
61
+ let v = normalize(vector);
62
+ let z_axis = vec3<f32>(0., 0., 1.);
63
+ var rotation_matrix = mat3x3<f32>(1., 0., 0.,
64
+ 0., 1., 0.,
65
+ 0., 0., 1.);
66
+ var rotation_axis = -Cross(z_axis, v);
67
+ let axis_norm = length(rotation_axis);
68
+ if axis_norm > 0.0001 {
69
+ rotation_axis = rotation_axis / axis_norm;
70
+
71
+ // Compute the angle between the z-axis and the target vector
72
+ let angle = acos(clamp(dot(z_axis, v), -1.0, 1.0));
73
+
74
+ // Compute Rodrigues' rotation formula components
75
+ let K = mat3x3<f32>(0.0, -rotation_axis.z, rotation_axis.y,
76
+ rotation_axis.z, 0.0, -rotation_axis.x,
77
+ -rotation_axis.y, rotation_axis.x, 0.0);
78
+
79
+ // Final rotation matrix
80
+ rotation_matrix += sin(angle) * K + (1.0 - cos(angle)) * (K * K);
81
+ } else {
82
+ if dot(z_axis, v) < 0. {
83
+ rotation_matrix = mat3x3<f32>(-1., 0., 0.,
84
+ 0., -1., 0.,
85
+ 0., 0., -1.);
86
+ }
87
+ }
88
+ var rotated = rotation_matrix * cp;
89
+ rotated = rotated * 0.2 * vec_options.length;
90
+ if(vec_options.scale_with_vector_length != 0u) {
91
+ rotated = rotated * length(vector);
92
+ }
93
+ let position = point + rotated;
94
+ let view_position = cameraMapPoint(position);
95
+ let cone_normals = array<vec3f, 7>(-normalize(Cross(cone_points[1] - cone_points[0],
96
+ cone_points[2] - cone_points[0])),
97
+ cone_points[2],
98
+ cone_points[1],
99
+ cone_points[0],
100
+ -normalize(Cross(cone_points[2] - cone_points[3],
101
+ cone_points[1] - cone_points[3])),
102
+ -normalize(Cross(cone_points[1] - cone_points[3],
103
+ cone_points[0] - cone_points[3])),
104
+ -normalize(Cross(cone_points[0] - cone_points[3],
105
+ cone_points[2] - cone_points[3])));
106
+
107
+ let normal = cone_normals[cone_strip_normals[index]];
108
+ return VectorFragmentInput(view_position, length(vector),
109
+ rotation_matrix * normal);
110
+ }
111
+
112
+ @fragment
113
+ fn fragment_main(input: VectorFragmentInput) -> @location(0) vec4<f32> {
114
+ if length(input.n) == 0. {
115
+ discard;
116
+ }
117
+ return lightCalcColor(-input.n, getColor(input.color_val));
118
+ }
webgpu/triangles.py ADDED
@@ -0,0 +1,66 @@
1
+ import numpy as np
2
+
3
+ from .render_object import RenderObject
4
+ from .utils import BufferBinding, buffer_from_array, read_shader_file
5
+
6
+
7
+ class Binding:
8
+ VERTICES = 90
9
+ NORMALS = 91
10
+ INDICES = 92
11
+
12
+
13
+ class TriangulationRenderer(RenderObject):
14
+ n_vertices: int = 3
15
+
16
+ def __init__(self, points, normals=None, color=(0.0, 1.0, 0.0, 1.0), label="Triangulation"):
17
+ super().__init__(label=label)
18
+ self.color = color
19
+ self.points = np.asarray(points, dtype=np.float32).reshape(-1)
20
+ assert len(self.points) % 9 == 0, "Invalid number of points"
21
+ if normals is None:
22
+ ps = self.points.reshape(-1, 3, 3)
23
+ normals = np.cross((ps[:, 1] - ps[:, 0]), (ps[:, 2] - ps[:, 0]))
24
+ normals = normals / np.linalg.norm(normals, axis=1)[:, None]
25
+ self.normals = np.concatenate([normals, normals, normals], axis=1).flatten()
26
+ else:
27
+ self.normals = np.asarray(normals, dtype=np.float32).reshape(-1)
28
+ ps = self.points.reshape(-1, 3)
29
+ self._bounding_box = ps.min(axis=0), ps.max(axis=0)
30
+ self.n_instances = len(self.points) // 9
31
+
32
+ def update(self, timestamp):
33
+ if timestamp == self._timestamp:
34
+ return
35
+ self._timestamp = timestamp
36
+
37
+ self.point_buffer = buffer_from_array(self.points)
38
+ self.normal_buffer = buffer_from_array(self.normals)
39
+ self.create_render_pipeline()
40
+
41
+ def get_bounding_box(self):
42
+ return self._bounding_box
43
+
44
+ def get_color_shader(self):
45
+ return """
46
+ fn getColor(vertId: u32, trigId: u32) -> vec4f {{
47
+ return vec4f{color};
48
+ }}""".format(
49
+ color=self.color
50
+ )
51
+
52
+ def get_shader_code(self) -> str:
53
+ return (
54
+ read_shader_file("triangulation.wgsl", __file__)
55
+ + self.options.camera.get_shader_code()
56
+ + self.options.light.get_shader_code()
57
+ + self.get_color_shader()
58
+ )
59
+
60
+ def get_bindings(self):
61
+ return [
62
+ *self.options.camera.get_bindings(),
63
+ *self.options.light.get_bindings(),
64
+ BufferBinding(Binding.VERTICES, self.point_buffer),
65
+ BufferBinding(Binding.NORMALS, self.normal_buffer),
66
+ ]
webgpu/uniforms.py ADDED
@@ -0,0 +1,111 @@
1
+ """Python equivalents to all uniforms defined in shader code
2
+
3
+ The UniformBase class are derived from ctypes.Structure to ensure correct memory layout.
4
+
5
+ CAUTION:
6
+ - The Binding numbers must match the numbers defined in the shader code.
7
+ - Uniforms structs must match exactly the memory layout defined in the shader code.
8
+ - The size of each struct must be a multiple of 16 bytes.
9
+ """
10
+
11
+ import ctypes as ct
12
+
13
+ from .utils import BaseBinding, UniformBinding
14
+ from .webgpu_api import BufferUsage, Device
15
+
16
+
17
+ class Binding:
18
+ """Binding numbers for uniforms in shader code in uniforms.wgsl"""
19
+
20
+ CAMERA = 0
21
+ CLIPPING = 1
22
+ FONT = 2
23
+ FONT_TEXTURE = 3
24
+ COLORMAP = 5
25
+ COLORMAP_TEXTURE = 6
26
+ COLORMAP_SAMPLER = 7
27
+
28
+ EDGES = 8
29
+ TRIGS = 9
30
+ TRIG_FUNCTION_VALUES = 10
31
+ SEG_FUNCTION_VALUES = 11
32
+ VERTICES = 12
33
+ TRIGS_INDEX = 13
34
+ GBUFFERLAM = 14
35
+
36
+ MESH = 20
37
+ EDGE = 21
38
+ SEG = 22
39
+ TRIG = 23
40
+ QUAD = 24
41
+ TET = 25
42
+ PYRAMID = 26
43
+ PRISM = 27
44
+ HEX = 28
45
+
46
+ TEXT = 30
47
+
48
+ LINE_INTEGRAL_CONVOLUTION = 40
49
+ LINE_INTEGRAL_CONVOLUTION_INPUT_TEXTURE = 41
50
+ LINE_INTEGRAL_CONVOLUTION_OUTPUT_TEXTURE = 42
51
+
52
+
53
+ class UniformBase(ct.Structure):
54
+
55
+ def __init__(self, device: Device, **kwargs):
56
+ super().__init__(**kwargs)
57
+
58
+ self.device = device
59
+ self._buffer = device.createBuffer(
60
+ size=len(bytes(self)),
61
+ usage=BufferUsage.UNIFORM | BufferUsage.COPY_DST,
62
+ label=type(self).__name__,
63
+ )
64
+
65
+ size = len(bytes(self))
66
+ if size % 16:
67
+ raise ValueError(
68
+ f"Size of type {type(self)} must be multiple of 16, current size: {size}"
69
+ )
70
+
71
+ def update_buffer(self):
72
+ self.device.queue.writeBuffer(self._buffer, 0, bytes(self))
73
+
74
+ def get_bindings(self) -> list[BaseBinding]:
75
+ return [UniformBinding(self._binding, self._buffer)]
76
+
77
+ def __del__(self):
78
+ self._buffer.destroy()
79
+
80
+
81
+ class MeshUniforms(UniformBase):
82
+ _binding = Binding.MESH
83
+ _fields_ = [
84
+ ("subdivision", ct.c_uint32),
85
+ ("shrink", ct.c_float),
86
+ ("padding", ct.c_float * 2),
87
+ ]
88
+
89
+ def __init__(self, device, subdivision=1, shrink=1.0, **kwargs):
90
+ super().__init__(device, subdivision=subdivision, shrink=shrink, **kwargs)
91
+
92
+
93
+ class LineIntegralConvolutionUniforms(UniformBase):
94
+ _binding = Binding.LINE_INTEGRAL_CONVOLUTION
95
+ _fields_ = [
96
+ ("width", ct.c_uint32),
97
+ ("height", ct.c_uint32),
98
+ ("kernel_length", ct.c_uint32),
99
+ ("oriented", ct.c_uint32),
100
+ ("thickness", ct.c_uint32),
101
+ ("padding", ct.c_float * 3),
102
+ ]
103
+
104
+ def __init__(self, device, kernel_length=25, oriented=0, thickness=5, **kwargs):
105
+ super().__init__(
106
+ device,
107
+ kernel_length=kernel_length,
108
+ oriented=oriented,
109
+ thickness=thickness,
110
+ **kwargs,
111
+ )