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
|
@@ -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
|
+
}
|
webgpu/shaders/font.wgsl
ADDED
|
@@ -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
|
+
}
|
webgpu/shaders/text.wgsl
ADDED
|
@@ -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
|
+
)
|