ncca-ngl 0.3.4__py3-none-any.whl → 0.5.0__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.
Files changed (56) hide show
  1. ncca/ngl/PrimData/pack_arrays.py +2 -3
  2. ncca/ngl/__init__.py +3 -4
  3. ncca/ngl/base_mesh.py +28 -20
  4. ncca/ngl/image.py +1 -3
  5. ncca/ngl/mat2.py +79 -53
  6. ncca/ngl/mat3.py +104 -185
  7. ncca/ngl/mat4.py +144 -309
  8. ncca/ngl/prim_data.py +42 -36
  9. ncca/ngl/primitives.py +2 -2
  10. ncca/ngl/pyside_event_handling_mixin.py +0 -108
  11. ncca/ngl/quaternion.py +69 -36
  12. ncca/ngl/shader.py +0 -116
  13. ncca/ngl/shader_program.py +94 -117
  14. ncca/ngl/texture.py +5 -2
  15. ncca/ngl/util.py +7 -0
  16. ncca/ngl/vec2.py +58 -292
  17. ncca/ngl/vec2_array.py +79 -28
  18. ncca/ngl/vec3.py +59 -340
  19. ncca/ngl/vec3_array.py +76 -23
  20. ncca/ngl/vec4.py +90 -190
  21. ncca/ngl/vec4_array.py +78 -27
  22. ncca/ngl/vector_base.py +542 -0
  23. ncca/ngl/webgpu/__init__.py +20 -0
  24. ncca/ngl/webgpu/__main__.py +640 -0
  25. ncca/ngl/webgpu/__main__.py.backup +640 -0
  26. ncca/ngl/webgpu/base_webgpu_pipeline.py +354 -0
  27. ncca/ngl/webgpu/custom_shader_pipeline.py +288 -0
  28. ncca/ngl/webgpu/instanced_geometry_pipeline.py +594 -0
  29. ncca/ngl/webgpu/line_pipeline.py +405 -0
  30. ncca/ngl/webgpu/pipeline_factory.py +190 -0
  31. ncca/ngl/webgpu/pipeline_shaders.py +497 -0
  32. ncca/ngl/webgpu/point_list_pipeline.py +349 -0
  33. ncca/ngl/webgpu/point_pipeline.py +336 -0
  34. ncca/ngl/webgpu/triangle_pipeline.py +419 -0
  35. ncca/ngl/webgpu/webgpu_constants.py +31 -0
  36. ncca/ngl/webgpu/webgpu_widget.py +322 -0
  37. ncca/ngl/webgpu/wip/REFACTORING_SUMMARY.md +82 -0
  38. ncca/ngl/webgpu/wip/UNIFIED_SYSTEM.md +314 -0
  39. ncca/ngl/webgpu/wip/buffer_manager.py +396 -0
  40. ncca/ngl/webgpu/wip/pipeline_config.py +463 -0
  41. ncca/ngl/webgpu/wip/shader_constants.py +328 -0
  42. ncca/ngl/webgpu/wip/shader_templates.py +563 -0
  43. ncca/ngl/webgpu/wip/unified_examples.py +390 -0
  44. ncca/ngl/webgpu/wip/unified_factory.py +449 -0
  45. ncca/ngl/webgpu/wip/unified_pipeline.py +469 -0
  46. ncca/ngl/widgets/__init__.py +18 -2
  47. ncca/ngl/widgets/__main__.py +2 -1
  48. ncca/ngl/widgets/lookatwidget.py +2 -1
  49. ncca/ngl/widgets/mat4widget.py +2 -2
  50. ncca/ngl/widgets/vec2widget.py +1 -1
  51. ncca/ngl/widgets/vec3widget.py +1 -0
  52. {ncca_ngl-0.3.4.dist-info → ncca_ngl-0.5.0.dist-info}/METADATA +3 -2
  53. ncca_ngl-0.5.0.dist-info/RECORD +105 -0
  54. ncca/ngl/widgets/transformation_widget.py +0 -299
  55. ncca_ngl-0.3.4.dist-info/RECORD +0 -82
  56. {ncca_ngl-0.3.4.dist-info → ncca_ngl-0.5.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,419 @@
1
+ """
2
+ Generic triangle rendering pipeline for WebGPU.
3
+ Handles triangle rendering with customizable colors, projection, and topology.
4
+ """
5
+
6
+ from typing import Optional, Tuple, Union
7
+
8
+ import numpy as np
9
+ import wgpu
10
+
11
+ from .base_webgpu_pipeline import BaseWebGPUPipeline
12
+ from .pipeline_shaders import (
13
+ TRIANGLE_SHADER_MULTI_COLOURED,
14
+ TRIANGLE_SHADER_SINGLE_COLOUR,
15
+ )
16
+ from .webgpu_constants import NGLToWebGPU
17
+
18
+
19
+ class BaseTrianglePipeline(BaseWebGPUPipeline):
20
+ """Base class for triangle rendering pipelines."""
21
+
22
+ def __init__(
23
+ self,
24
+ device: wgpu.GPUDevice,
25
+ data_type: str = "Vec3",
26
+ texture_format: wgpu.TextureFormat = wgpu.TextureFormat.rgba8unorm,
27
+ depth_format: wgpu.TextureFormat = wgpu.TextureFormat.depth24plus,
28
+ msaa_sample_count: int = 4,
29
+ stride: int = 0,
30
+ topology: wgpu.PrimitiveTopology = wgpu.PrimitiveTopology.triangle_list,
31
+ ):
32
+ """
33
+ Initialize the triangle rendering pipeline.
34
+
35
+ Args:
36
+ device: WebGPU device
37
+ texture_format: Color attachment format
38
+ depth_format: Depth attachment format
39
+ msaa_sample_count: Number of MSAA samples
40
+ stride: The stride of the vertex buffer. If 0, it is inferred from data_type.
41
+ topology: Triangle topology (triangle_list or triangle_strip)
42
+ """
43
+ self._topology = topology
44
+ super().__init__(
45
+ device=device,
46
+ texture_format=texture_format,
47
+ depth_format=depth_format,
48
+ msaa_sample_count=msaa_sample_count,
49
+ data_type=data_type,
50
+ stride=stride,
51
+ )
52
+
53
+ def _get_primitive_topology(self) -> wgpu.PrimitiveTopology:
54
+ """Triangle pipelines use configurable topology."""
55
+ return self._topology
56
+
57
+
58
+ class TrianglePipelineMultiColour(BaseTrianglePipeline):
59
+ """
60
+ A reusable pipeline for rendering triangles in WebGPU with per-vertex colors.
61
+
62
+ Features:
63
+ - Triangle lists or triangle strips
64
+ - Per-vertex colors
65
+ - MVP matrix support
66
+ - MSAA support
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ device: wgpu.GPUDevice,
72
+ data_type: str = "Vec3",
73
+ texture_format: wgpu.TextureFormat = wgpu.TextureFormat.rgba8unorm,
74
+ depth_format: wgpu.TextureFormat = wgpu.TextureFormat.depth24plus,
75
+ msaa_sample_count: int = 4,
76
+ stride: int = 0,
77
+ topology: wgpu.PrimitiveTopology = wgpu.PrimitiveTopology.triangle_list,
78
+ ):
79
+ """
80
+ Initialize the triangle rendering pipeline.
81
+
82
+ Args:
83
+ device: WebGPU device
84
+ texture_format: Color attachment format
85
+ depth_format: Depth attachment format
86
+ msaa_sample_count: Number of MSAA samples
87
+ stride: The stride of the vertex buffer. If 0, it is inferred from data_type.
88
+ topology: Triangle topology (triangle_list or triangle_strip)
89
+ """
90
+ # Pipeline-specific buffer tracking
91
+ self.vertex_buffer: Optional[wgpu.GPUBuffer] = None
92
+ self.color_buffer: Optional[wgpu.GPUBuffer] = None
93
+ self.num_vertices: int = 0
94
+
95
+ super().__init__(
96
+ device=device,
97
+ texture_format=texture_format,
98
+ depth_format=depth_format,
99
+ msaa_sample_count=msaa_sample_count,
100
+ data_type=data_type,
101
+ stride=stride,
102
+ topology=topology,
103
+ )
104
+
105
+ def get_dtype(self) -> np.dtype:
106
+ """Get the data type of the pipeline."""
107
+ return np.dtype(
108
+ [
109
+ ("MVP", "float32", (4, 4)),
110
+ ("padding", "float32", 4),
111
+ ]
112
+ )
113
+
114
+ def _get_shader_code(self) -> str:
115
+ """Get the WGSL shader code for this pipeline."""
116
+ return TRIANGLE_SHADER_MULTI_COLOURED
117
+
118
+ def _get_vertex_buffer_layouts(self):
119
+ """Get vertex buffer layout configurations for the pipeline."""
120
+ return [
121
+ {
122
+ "array_stride": self._stride,
123
+ "step_mode": "vertex",
124
+ "attributes": [
125
+ {
126
+ "format": NGLToWebGPU.vertex_format(self._data_type),
127
+ "offset": 0,
128
+ "shader_location": 0,
129
+ },
130
+ ],
131
+ },
132
+ {
133
+ "array_stride": NGLToWebGPU.stride_from_type("Vec3"),
134
+ "step_mode": "vertex",
135
+ "attributes": [
136
+ {
137
+ "format": NGLToWebGPU.vertex_format("Vec3"),
138
+ "offset": 0,
139
+ "shader_location": 1,
140
+ },
141
+ ],
142
+ },
143
+ ]
144
+
145
+ def _set_default_uniforms(self) -> None:
146
+ """Set default values for uniform data."""
147
+ pass # No specific defaults for triangle pipeline
148
+
149
+ def _get_pipeline_label(self) -> str:
150
+ """Get the label for the pipeline."""
151
+ topology_name = (
152
+ "list"
153
+ if self._topology == wgpu.PrimitiveTopology.triangle_list
154
+ else "strip"
155
+ )
156
+ return f"triangle_pipeline_multi_coloured_{topology_name}"
157
+
158
+ def set_data(self, positions=None, colors=None, **kwargs) -> None:
159
+ """
160
+ Set the triangle data for rendering.
161
+
162
+ Args:
163
+ positions: Nx2/Nx3 array of triangle positions or a pre-existing GPUBuffer.
164
+ colors: Nx3 array of triangle colors (RGB) or a pre-existing GPUBuffer.
165
+ """
166
+ if positions is not None:
167
+ if isinstance(positions, wgpu.GPUBuffer):
168
+ self.vertex_buffer = positions
169
+ self.num_vertices = positions.size // self._stride
170
+ else: # numpy array
171
+ self.vertex_buffer, buffer_size = self._create_or_update_buffer(
172
+ self.vertex_buffer,
173
+ positions,
174
+ wgpu.BufferUsage.VERTEX | wgpu.BufferUsage.COPY_DST,
175
+ f"triangle_pipeline_multi_coloured_position_buffer_{self._get_pipeline_label()}",
176
+ )
177
+ self.num_vertices = buffer_size // self._stride
178
+
179
+ if colors is not None:
180
+ if isinstance(colors, wgpu.GPUBuffer):
181
+ self.color_buffer = colors
182
+ else:
183
+ color_result = self._process_vertex_data(
184
+ colors,
185
+ None,
186
+ padding_size=4, # Pad to vec4 for alignment
187
+ buffer_label=f"triangle_pipeline_multi_coloured_colour_buffer_{self._get_pipeline_label()}",
188
+ )
189
+ if isinstance(color_result, wgpu.GPUBuffer):
190
+ self.color_buffer = color_result
191
+ elif color_result:
192
+ self.color_buffer = color_result[0]
193
+ else:
194
+ self.color_buffer = None
195
+
196
+ def update_uniforms(self, **kwargs) -> None:
197
+ """
198
+ Update uniform buffer values.
199
+
200
+ Args:
201
+ **kwargs: Pipeline-specific uniform parameters
202
+ - mvp: 4x4 projection matrix
203
+ """
204
+ if "mvp" in kwargs and kwargs["mvp"] is not None:
205
+ self.uniform_data["MVP"] = kwargs["mvp"]
206
+
207
+ self.device.queue.write_buffer(
208
+ self.uniform_buffer, 0, self.uniform_data.tobytes()
209
+ )
210
+
211
+ def render(self, render_pass: wgpu.GPURenderPassEncoder, **kwargs) -> None:
212
+ """
213
+ Render the triangles.
214
+
215
+ Args:
216
+ render_pass: Active render pass encoder
217
+ **kwargs: Pipeline-specific render parameters
218
+ - num_vertices: Number of vertices to render (defaults to all)
219
+ """
220
+ num_vertices = kwargs.get("num_vertices", None)
221
+
222
+ if self.vertex_buffer is None or self.color_buffer is None:
223
+ return
224
+
225
+ count = num_vertices if num_vertices is not None else self.num_vertices
226
+
227
+ render_pass.set_pipeline(self.pipeline)
228
+ if self.bind_group:
229
+ render_pass.set_bind_group(0, self.bind_group, [], 0, 999999)
230
+ render_pass.set_vertex_buffer(0, self.vertex_buffer)
231
+ render_pass.set_vertex_buffer(1, self.color_buffer)
232
+ render_pass.draw(count)
233
+
234
+ def cleanup(self) -> None:
235
+ """Release resources."""
236
+ if self.vertex_buffer:
237
+ self.vertex_buffer.destroy()
238
+ if self.color_buffer:
239
+ self.color_buffer.destroy()
240
+ super().cleanup()
241
+
242
+
243
+ class TrianglePipelineSingleColour(BaseTrianglePipeline):
244
+ """
245
+ A reusable pipeline for rendering triangles in WebGPU with single color.
246
+
247
+ Features:
248
+ - Triangle lists or triangle strips
249
+ - Single color for all triangles
250
+ - MVP matrix support
251
+ - MSAA support
252
+ """
253
+
254
+ def __init__(
255
+ self,
256
+ device: wgpu.GPUDevice,
257
+ data_type: str = "Vec3",
258
+ texture_format: wgpu.TextureFormat = wgpu.TextureFormat.rgba8unorm,
259
+ depth_format: wgpu.TextureFormat = wgpu.TextureFormat.depth24plus,
260
+ msaa_sample_count: int = 4,
261
+ stride: int = 0,
262
+ topology: wgpu.PrimitiveTopology = wgpu.PrimitiveTopology.triangle_list,
263
+ colour: Tuple[float, float, float] = (1.0, 1.0, 1.0),
264
+ ):
265
+ """
266
+ Initialize the triangle rendering pipeline.
267
+
268
+ Args:
269
+ device: WebGPU device
270
+ texture_format: Color attachment format
271
+ depth_format: Depth attachment format
272
+ msaa_sample_count: Number of MSAA samples
273
+ stride: The stride of the vertex buffer. If 0, it is inferred from data_type.
274
+ topology: Triangle topology (triangle_list or triangle_strip)
275
+ colour: RGB color tuple for triangles (default white)
276
+ """
277
+ # Pipeline-specific buffer tracking
278
+ self.vertex_buffer: Optional[wgpu.GPUBuffer] = None
279
+ self.num_vertices: int = 0
280
+ self._colour = np.array(colour, dtype=np.float32)
281
+
282
+ super().__init__(
283
+ device=device,
284
+ texture_format=texture_format,
285
+ depth_format=depth_format,
286
+ msaa_sample_count=msaa_sample_count,
287
+ data_type=data_type,
288
+ stride=stride,
289
+ topology=topology,
290
+ )
291
+
292
+ def get_dtype(self) -> np.dtype:
293
+ """Get the data type of the pipeline."""
294
+ return np.dtype(
295
+ [
296
+ ("MVP", "float32", (4, 4)),
297
+ ("Colour", "float32", 3),
298
+ ("padding", "float32", 1),
299
+ ]
300
+ )
301
+
302
+ def _get_shader_code(self) -> str:
303
+ """Get the WGSL shader code for this pipeline."""
304
+ return TRIANGLE_SHADER_SINGLE_COLOUR
305
+
306
+ def _get_vertex_buffer_layouts(self):
307
+ """Get vertex buffer layout configurations for the pipeline."""
308
+ return [
309
+ {
310
+ "array_stride": self._stride,
311
+ "step_mode": "vertex",
312
+ "attributes": [
313
+ {
314
+ "format": NGLToWebGPU.vertex_format(self._data_type),
315
+ "offset": 0,
316
+ "shader_location": 0,
317
+ },
318
+ ],
319
+ },
320
+ ]
321
+
322
+ def _set_default_uniforms(self) -> None:
323
+ """Set default values for uniform data."""
324
+ pass # No specific defaults for triangle pipeline
325
+
326
+ def _get_pipeline_label(self) -> str:
327
+ """Get the label for the pipeline."""
328
+ topology_name = (
329
+ "list"
330
+ if self._topology == wgpu.PrimitiveTopology.triangle_list
331
+ else "strip"
332
+ )
333
+ return f"triangle_pipeline_single_colour_{topology_name}"
334
+
335
+ def set_data(self, positions=None, colors=None, **kwargs) -> None:
336
+ """
337
+ Set the triangle data for rendering.
338
+
339
+ Args:
340
+ positions: Nx2/Nx3 array of triangle positions or a pre-existing GPUBuffer.
341
+ colors: Ignored for single colour pipeline
342
+ """
343
+ if positions is not None:
344
+ if isinstance(positions, wgpu.GPUBuffer):
345
+ self.vertex_buffer = positions
346
+ self.num_vertices = positions.size // self._stride
347
+ else: # numpy array
348
+ self.vertex_buffer, buffer_size = self._create_or_update_buffer(
349
+ self.vertex_buffer,
350
+ positions,
351
+ wgpu.BufferUsage.VERTEX | wgpu.BufferUsage.COPY_DST,
352
+ f"triangle_pipeline_single_colour_position_buffer_{self._get_pipeline_label()}",
353
+ )
354
+ self.num_vertices = buffer_size // self._stride
355
+
356
+ def update_uniforms(self, **kwargs) -> None:
357
+ """
358
+ Update uniform buffer values.
359
+
360
+ Args:
361
+ **kwargs: Pipeline-specific uniform parameters
362
+ - mvp: 4x4 projection matrix
363
+ - colour: RGB color tuple
364
+ """
365
+ if "mvp" in kwargs and kwargs["mvp"] is not None:
366
+ self.uniform_data["MVP"] = kwargs["mvp"]
367
+
368
+ if "colour" in kwargs and kwargs["colour"] is not None:
369
+ colour = np.array(kwargs["colour"], dtype=np.float32)
370
+ if colour.shape == (3,):
371
+ self.uniform_data["Colour"] = colour
372
+ self._colour = colour
373
+
374
+ self.device.queue.write_buffer(
375
+ self.uniform_buffer, 0, self.uniform_data.tobytes()
376
+ )
377
+
378
+ def set_color(self, colour: Tuple[float, float, float]) -> None:
379
+ """
380
+ Set the color for the triangles.
381
+
382
+ Args:
383
+ colour: RGB color tuple
384
+ """
385
+ colour_array = np.array(colour, dtype=np.float32)
386
+ if colour_array.shape == (3,):
387
+ self.uniform_data["Colour"] = colour_array
388
+ self._colour = colour_array
389
+ self.device.queue.write_buffer(
390
+ self.uniform_buffer, 0, self.uniform_data.tobytes()
391
+ )
392
+
393
+ def render(self, render_pass: wgpu.GPURenderPassEncoder, **kwargs) -> None:
394
+ """
395
+ Render the triangles.
396
+
397
+ Args:
398
+ render_pass: Active render pass encoder
399
+ **kwargs: Pipeline-specific render parameters
400
+ - num_vertices: Number of vertices to render (defaults to all)
401
+ """
402
+ num_vertices = kwargs.get("num_vertices", None)
403
+
404
+ if self.vertex_buffer is None:
405
+ return
406
+
407
+ count = num_vertices if num_vertices is not None else self.num_vertices
408
+
409
+ render_pass.set_pipeline(self.pipeline)
410
+ if self.bind_group:
411
+ render_pass.set_bind_group(0, self.bind_group, [], 0, 999999)
412
+ render_pass.set_vertex_buffer(0, self.vertex_buffer)
413
+ render_pass.draw(count)
414
+
415
+ def cleanup(self) -> None:
416
+ """Release resources."""
417
+ if self.vertex_buffer:
418
+ self.vertex_buffer.destroy()
419
+ super().cleanup()
@@ -0,0 +1,31 @@
1
+ from enum import IntEnum
2
+
3
+ import numpy as np
4
+
5
+ from ncca.ngl import Mat2, Mat3, Mat4, Vec2, Vec3, Vec4
6
+
7
+ FLOAT_SIZE = np.dtype(np.float32).itemsize
8
+
9
+
10
+ class NGLToWebGPU:
11
+ _strides = {
12
+ "vec2": 2 * FLOAT_SIZE,
13
+ "vec3": 3 * FLOAT_SIZE,
14
+ "vec4": 4 * FLOAT_SIZE,
15
+ "mat2": 4 * FLOAT_SIZE,
16
+ "mat3": 12 * FLOAT_SIZE,
17
+ "mat4": 16 * FLOAT_SIZE,
18
+ }
19
+ _vertex_format = {
20
+ "vec2": "float32x2",
21
+ "vec3": "float32x3",
22
+ "vec4": "float32x4",
23
+ }
24
+
25
+ @staticmethod
26
+ def stride_from_type(type: str):
27
+ return NGLToWebGPU._strides[type.lower()]
28
+
29
+ @staticmethod
30
+ def vertex_format(type: str):
31
+ return NGLToWebGPU._vertex_format[type.lower()]