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,497 @@
1
+ from dataclasses import dataclass
2
+
3
+
4
+ @dataclass
5
+ class ShaderConfig:
6
+ """Configuration for generating WGSL shaders"""
7
+
8
+ name: str
9
+ geometry_type: str # 'point', 'line', 'triangle', 'instanced', 'point_list'
10
+ colour_mode: str # 'single', 'multi'
11
+ has_lighting: bool = False
12
+ has_uv: bool = False
13
+ has_size: bool = False
14
+ has_view_matrix: bool = False
15
+ has_instance_transform: bool = False
16
+
17
+
18
+ # Shader component templates
19
+ UNIFORMS_BASE = """
20
+ @group(0) @binding(0) var<uniform> uniforms : Uniforms;
21
+ """
22
+
23
+ UNIFORMS_MVP = """
24
+ struct Uniforms
25
+ {{
26
+ MVP : mat4x4<f32>,
27
+ {additional_fields}
28
+ }};
29
+ """
30
+
31
+ UNIFORMS_MVP_VIEW = """
32
+ struct Uniforms
33
+ {{
34
+ MVP : mat4x4<f32>,
35
+ ViewMatrix : mat4x4<f32>,
36
+ {additional_fields}
37
+ }};
38
+ """
39
+
40
+ VERTEX_IN_POSITION = """
41
+ struct VertexIn {{
42
+ @location(0) position: vec3<f32>,
43
+ {colour_input}
44
+ }};
45
+ """
46
+
47
+ VERTEX_IN_INSTANCED = """
48
+ struct InstanceData {{
49
+ @location(0) position: vec3<f32>,
50
+ {colour_input}
51
+ @location(2) instance_id: f32,
52
+ }};
53
+
54
+ struct GeometryVertex {{
55
+ @location(3) geometry_position: vec3<f32>,
56
+ @location(4) geometry_normal: vec3<f32>,
57
+ @location(5) geometry_uv: vec2<f32>,
58
+ }};
59
+ """
60
+
61
+ VERTEX_OUT_BASE = """
62
+ struct VertexOut {{
63
+ @builtin(position) position: vec4<f32>,
64
+ {colour_output}
65
+ {uv_output}
66
+ {normal_output}
67
+ {world_pos_output}
68
+ }};
69
+ """
70
+
71
+ QUAD_OFFSETS = """
72
+ let quad_offsets = array<vec2<f32>, 4>(
73
+ vec2<f32>(-1.0, -1.0), // bottom-left
74
+ vec2<f32>(1.0, -1.0), // bottom-right
75
+ vec2<f32>(-1.0, 1.0), // top-left
76
+ vec2<f32>(1.0, 1.0) // top-right
77
+ );
78
+ """
79
+
80
+ CAMERA_EXTRACTION = """
81
+ // Extract camera right and up vectors from view matrix
82
+ let cameraRight = normalize(vec3<f32>(uniforms.ViewMatrix[0][0], uniforms.ViewMatrix[1][0], uniforms.ViewMatrix[2][0]));
83
+ let cameraUp = normalize(vec3<f32>(uniforms.ViewMatrix[0][1], uniforms.ViewMatrix[1][1], uniforms.ViewMatrix[2][1]));
84
+ """
85
+
86
+ LIGHTING_CALCULATION = """
87
+ // Light properties
88
+ let light_direction = normalize(vec3<f32>(0.5, 1.0, 0.3)); // World space light direction
89
+ let light_color = vec3<f32>(1.0, 1.0, 1.0); // White light
90
+ let ambient_intensity = 0.15; // Lower ambient for better contrast
91
+ let diffuse_intensity = 0.85; // Higher diffuse for stronger lighting
92
+
93
+ // Ambient component (base illumination)
94
+ let ambient = vec3<f32>(ambient_intensity);
95
+
96
+ // Diffuse component (Lambertian reflection)
97
+ let normal = normalize(fragData.fragNormal);
98
+ let n_dot_l = max(dot(normal, light_direction), 0.0);
99
+ let diffuse = light_color * n_dot_l * diffuse_intensity;
100
+
101
+ // Combine lighting components
102
+ let final_lighting = ambient + diffuse;
103
+ """
104
+
105
+ CIRCLE_DISCARD = """
106
+ let center = vec2<f32>(0.5, 0.5); // Center of the quad in UV space
107
+ let dist = distance(fragData.uv, center); // Distance from center
108
+ let radius = 0.5; // Circle radius (quad is 1.0 in UV space)
109
+
110
+ if (dist > radius)
111
+ {{
112
+ discard; // Remove pixels outside the circle
113
+ }}
114
+ """
115
+
116
+
117
+ def _build_uniforms(config: ShaderConfig) -> str:
118
+ """Build uniform structure based on configuration"""
119
+ additional_fields = []
120
+
121
+ if config.geometry_type == "point":
122
+ if config.colour_mode == "single":
123
+ additional_fields.append(" ColourSize: vec4<f32>,")
124
+ else:
125
+ additional_fields.extend([
126
+ " size: f32,",
127
+ " padding: u32,",
128
+ " padding2: u32,",
129
+ " padding3: u32,",
130
+ ])
131
+ elif config.geometry_type in ["line", "triangle", "point_list"]:
132
+ if config.colour_mode == "single":
133
+ additional_fields.extend([" Colour: vec3<f32>,", " padding: f32,"])
134
+ elif config.geometry_type == "instanced":
135
+ if config.colour_mode == "single":
136
+ additional_fields.extend([
137
+ " colour: vec3<f32>,",
138
+ " padding: f32,",
139
+ " instance_transform: mat4x4<f32>,",
140
+ ])
141
+ else:
142
+ additional_fields.append(" instance_transform: mat4x4<f32>,")
143
+
144
+ base_template = UNIFORMS_MVP_VIEW if config.has_view_matrix else UNIFORMS_MVP
145
+ return base_template.format(additional_fields="\n".join(additional_fields) if additional_fields else "")
146
+
147
+
148
+ def _build_vertex_input(config: ShaderConfig) -> str:
149
+ """Build vertex input structure based on configuration"""
150
+ if config.geometry_type == "instanced":
151
+ if config.colour_mode == "single":
152
+ colour_input = " @location(1) colour: vec3<f32>, // Provided but ignored"
153
+ else:
154
+ colour_input = " @location(1) colour: vec3<f32>,"
155
+
156
+ return VERTEX_IN_INSTANCED.format(colour_input=colour_input)
157
+ else:
158
+ # For lines, use 'pos' instead of 'position' to match original shaders
159
+ if config.geometry_type == "line":
160
+ position_input = " @location(0) pos: vec3<f32>,"
161
+ colour_input = " @location(1) color: vec3<f32>," if config.colour_mode == "multi" else ""
162
+ elif config.geometry_type == "triangle":
163
+ position_input = " @location(0) pos: vec3<f32>,"
164
+ colour_input = " @location(1) color: vec3<f32>," if config.colour_mode == "multi" else ""
165
+ else:
166
+ position_input = " @location(0) position: vec3<f32>,"
167
+ colour_input = " @location(1) colour: vec3<f32>," if config.colour_mode == "multi" else ""
168
+
169
+ return f"""
170
+ struct VertexIn {{
171
+ {position_input}
172
+ {colour_input}
173
+ }};
174
+ """
175
+
176
+
177
+ def _build_vertex_output(config: ShaderConfig) -> str:
178
+ """Build vertex output structure based on configuration"""
179
+ outputs = []
180
+
181
+ if config.colour_mode == "multi":
182
+ if config.geometry_type in ["point", "point_list"]:
183
+ outputs.append(" @location(0) fragColour: vec3<f32>,")
184
+ elif config.geometry_type in ["line", "triangle"]:
185
+ outputs.append(" @location(0) color: vec3<f32>,")
186
+ elif config.geometry_type == "instanced":
187
+ outputs.append(" @location(0) fragColour: vec3<f32>,")
188
+
189
+ if config.has_uv:
190
+ outputs.append(" @location(1) uv: vec2<f32>,")
191
+
192
+ if config.has_lighting:
193
+ if config.geometry_type == "instanced" and config.colour_mode == "single":
194
+ outputs = [
195
+ " @location(0) fragNormal: vec3<f32>,",
196
+ " @location(1) fragUV: vec2<f32>,",
197
+ " @location(2) worldPos: vec3<f32>,",
198
+ ]
199
+ else:
200
+ start_idx = len(outputs)
201
+ outputs.append(f" @location({start_idx}) fragNormal: vec3<f32>,")
202
+ outputs.append(f" @location({start_idx + 1}) fragUV: vec2<f32>,")
203
+ outputs.append(f" @location({start_idx + 2}) worldPos: vec3<f32>,")
204
+
205
+ colour_output = ""
206
+ uv_output = ""
207
+ normal_output = ""
208
+ world_pos_output = ""
209
+
210
+ for output in outputs:
211
+ if "fragColour" in output or "color" in output:
212
+ colour_output = output
213
+ elif "uv" in output and "fragUV" not in output:
214
+ uv_output = output
215
+ elif "fragNormal" in output:
216
+ normal_output = output
217
+ elif "fragUV" in output:
218
+ uv_output = output
219
+ elif "worldPos" in output:
220
+ world_pos_output = output
221
+
222
+ return VERTEX_OUT_BASE.format(
223
+ colour_output=colour_output,
224
+ uv_output=uv_output,
225
+ normal_output=normal_output,
226
+ world_pos_output=world_pos_output,
227
+ )
228
+
229
+
230
+ def _build_vertex_main(config: ShaderConfig) -> str:
231
+ """Build vertex shader main function"""
232
+ if config.geometry_type == "point":
233
+ return _build_point_vertex(config)
234
+ elif config.geometry_type in ["line", "triangle", "point_list"]:
235
+ return _build_simple_vertex(config)
236
+ elif config.geometry_type == "instanced":
237
+ return _build_instanced_vertex(config)
238
+ return ""
239
+
240
+
241
+ def _build_point_vertex(config: ShaderConfig) -> str:
242
+ """Build point sprite vertex shader"""
243
+ size_source = "uniforms.size" if config.colour_mode == "multi" else "uniforms.ColourSize.w"
244
+
245
+ return f"""
246
+ @vertex
247
+ fn vertex_main(input: VertexIn, @builtin(vertex_index) vertex_index: u32) -> VertexOut {{
248
+ var output: VertexOut;
249
+ {QUAD_OFFSETS}
250
+
251
+ {CAMERA_EXTRACTION if config.has_view_matrix else ""}
252
+
253
+ // Calculate billboard offset in world space
254
+ let offset2D = quad_offsets[vertex_index] * {size_source};
255
+ let offset3D = cameraRight * offset2D.x + cameraUp * offset2D.y;
256
+ let worldPos = input.position + offset3D;
257
+
258
+ output.position = uniforms.MVP * vec4<f32>(worldPos, 1.0);
259
+ {(" output.fragColour = input.colour;" if config.colour_mode == "multi" else "")}
260
+ // convert offset from -1 -> 1 to 0 -> 1 for uv
261
+ output.uv = quad_offsets[vertex_index] * 0.5 + 0.5;
262
+
263
+ return output;
264
+ }}
265
+ """
266
+
267
+
268
+ def _build_simple_vertex(config: ShaderConfig) -> str:
269
+ """Build simple vertex shader for lines, triangles, point lists"""
270
+ # Use the correct attribute names based on geometry type
271
+ if config.geometry_type in ["line", "triangle"]:
272
+ position_source = "input.pos"
273
+ colour_source = "input.color"
274
+ else:
275
+ position_source = "input.position"
276
+ colour_source = "input.colour" if config.geometry_type == "point_list" else "input.color"
277
+
278
+ colour_output = ""
279
+ if config.colour_mode == "multi" and config.geometry_type == "point_list":
280
+ colour_output = f" output.fragColour = {colour_source};"
281
+ elif config.colour_mode == "multi" and config.geometry_type in ["line", "triangle"]:
282
+ colour_output = f" output.color = {colour_source};"
283
+
284
+ return f"""
285
+ @vertex
286
+ fn vertex_main(input: VertexIn) -> VertexOut {{
287
+ var output: VertexOut;
288
+ output.position = uniforms.MVP * vec4<f32>({position_source}, 1.0);
289
+ {colour_output}
290
+
291
+ return output;
292
+ }}
293
+ """
294
+
295
+
296
+ def _build_instanced_vertex(config: ShaderConfig) -> str:
297
+ """Build instanced vertex shader"""
298
+ return f"""
299
+ @vertex
300
+ fn vertex_main(instance_data: InstanceData, geom_vertex: GeometryVertex, @builtin(vertex_index) vertex_index: u32) -> VertexOut {{
301
+ var output: VertexOut;
302
+
303
+ // Transform geometry vertex by instance transform and position
304
+ let transformed_vertex = uniforms.instance_transform * vec4<f32>(geom_vertex.geometry_position, 1.0);
305
+ let world_position = transformed_vertex.xyz + instance_data.position;
306
+
307
+ output.position = uniforms.MVP * vec4<f32>(world_position, 1.0);
308
+ {(" output.fragColour = instance_data.colour;" if config.colour_mode == "multi" else "")}
309
+
310
+ // Transform normal by instance transform (skip translation)
311
+ let normal_matrix = mat3x3<f32>(
312
+ uniforms.instance_transform[0].xyz,
313
+ uniforms.instance_transform[1].xyz,
314
+ uniforms.instance_transform[2].xyz
315
+ );
316
+ output.fragNormal = normalize(normal_matrix * geom_vertex.geometry_normal);
317
+ output.fragUV = geom_vertex.geometry_uv;
318
+ output.worldPos = world_position;
319
+
320
+ return output;
321
+ }}
322
+ """
323
+
324
+
325
+ def _build_fragment_main(config: ShaderConfig) -> str:
326
+ """Build fragment shader main function"""
327
+ if config.geometry_type == "point":
328
+ return _build_point_fragment(config)
329
+ elif config.geometry_type == "instanced":
330
+ return _build_instanced_fragment(config)
331
+ else:
332
+ return _build_simple_fragment(config)
333
+
334
+
335
+ def _build_point_fragment(config: ShaderConfig) -> str:
336
+ """Build point sprite fragment shader"""
337
+ colour_source = "fragData.fragColour" if config.colour_mode == "multi" else "uniforms.ColourSize.xyz"
338
+
339
+ return f"""
340
+ @fragment
341
+ fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32>
342
+ {{
343
+ {CIRCLE_DISCARD if config.has_uv else ""}
344
+
345
+ return vec4<f32>({colour_source}, 1.0);
346
+ }}
347
+ """
348
+
349
+
350
+ def _build_simple_fragment(config: ShaderConfig) -> str:
351
+ """Build simple fragment shader for lines, triangles, point lists"""
352
+ if config.colour_mode == "single":
353
+ return """
354
+ @fragment
355
+ fn fragment_main() -> @location(0) vec4<f32> {
356
+ return vec4<f32>(uniforms.Colour, 1.0);
357
+ }
358
+ """
359
+ else:
360
+ # Use the correct output attribute name based on geometry type
361
+ if config.geometry_type == "point_list":
362
+ colour_source = "fragData.fragColour"
363
+ else:
364
+ colour_source = "fragData.color"
365
+ return f"""
366
+ @fragment
367
+ fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32>
368
+ {{
369
+ return vec4<f32>({colour_source}, 1.0);
370
+ }}
371
+ """
372
+
373
+
374
+ def _build_instanced_fragment(config: ShaderConfig) -> str:
375
+ """Build instanced fragment shader with lighting"""
376
+ colour_source = "uniforms.colour" if config.colour_mode == "single" else "fragData.fragColour"
377
+
378
+ return f"""
379
+ @fragment
380
+ fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32>
381
+ {{
382
+ // Enhanced diffuse lighting calculation
383
+
384
+ {LIGHTING_CALCULATION}
385
+
386
+ // Apply lighting to the fragment color
387
+ let lit_color = {colour_source} * final_lighting;
388
+
389
+ return vec4<f32>(lit_color, 1.0);
390
+ }}
391
+ """
392
+
393
+
394
+ def generate_shader(config: ShaderConfig) -> str:
395
+ """Generate complete WGSL shader from configuration"""
396
+ uniforms = _build_uniforms(config)
397
+ vertex_in = _build_vertex_input(config)
398
+ vertex_out = _build_vertex_output(config)
399
+ vertex_main = _build_vertex_main(config)
400
+ fragment_main = _build_fragment_main(config)
401
+
402
+ return f"""
403
+ {UNIFORMS_BASE}
404
+ {uniforms}
405
+
406
+ {vertex_in}
407
+
408
+ {vertex_out}
409
+
410
+ {vertex_main}
411
+
412
+ {fragment_main}
413
+ """
414
+
415
+
416
+ # Generate all existing shaders using the new system
417
+ POINT_SHADER_MULTI_COLOURED = generate_shader(
418
+ ShaderConfig(
419
+ name="POINT_SHADER_MULTI_COLOURED",
420
+ geometry_type="point",
421
+ colour_mode="multi",
422
+ has_uv=True,
423
+ has_view_matrix=True,
424
+ )
425
+ )
426
+
427
+ POINT_SHADER_SINGLE_COLOUR = generate_shader(
428
+ ShaderConfig(
429
+ name="POINT_SHADER_SINGLE_COLOUR",
430
+ geometry_type="point",
431
+ colour_mode="single",
432
+ has_uv=True,
433
+ has_view_matrix=True,
434
+ )
435
+ )
436
+
437
+ INSTANCED_SHADER_MULTI_COLOURED = generate_shader(
438
+ ShaderConfig(
439
+ name="INSTANCED_SHADER_MULTI_COLOURED",
440
+ geometry_type="instanced",
441
+ colour_mode="multi",
442
+ has_lighting=True,
443
+ has_view_matrix=True,
444
+ has_instance_transform=True,
445
+ )
446
+ )
447
+
448
+ INSTANCED_SHADER_SINGLE_COLOUR = generate_shader(
449
+ ShaderConfig(
450
+ name="INSTANCED_SHADER_SINGLE_COLOUR",
451
+ geometry_type="instanced",
452
+ colour_mode="single",
453
+ has_lighting=True,
454
+ has_view_matrix=True,
455
+ has_instance_transform=True,
456
+ )
457
+ )
458
+
459
+ LINE_SHADER_SINGLE_COLOUR = generate_shader(
460
+ ShaderConfig(name="LINE_SHADER_SINGLE_COLOUR", geometry_type="line", colour_mode="single")
461
+ )
462
+
463
+ LINE_SHADER_MULTI_COLOURED = generate_shader(
464
+ ShaderConfig(name="LINE_SHADER_MULTI_COLOURED", geometry_type="line", colour_mode="multi")
465
+ )
466
+
467
+ POINT_LIST_SHADER_MULTI_COLOURED = generate_shader(
468
+ ShaderConfig(
469
+ name="POINT_LIST_SHADER_MULTI_COLOURED",
470
+ geometry_type="point_list",
471
+ colour_mode="multi",
472
+ )
473
+ )
474
+
475
+ POINT_LIST_SHADER_SINGLE_COLOUR = generate_shader(
476
+ ShaderConfig(
477
+ name="POINT_LIST_SHADER_SINGLE_COLOUR",
478
+ geometry_type="point_list",
479
+ colour_mode="single",
480
+ )
481
+ )
482
+
483
+ TRIANGLE_SHADER_SINGLE_COLOUR = generate_shader(
484
+ ShaderConfig(
485
+ name="TRIANGLE_SHADER_SINGLE_COLOUR",
486
+ geometry_type="triangle",
487
+ colour_mode="single",
488
+ )
489
+ )
490
+
491
+ TRIANGLE_SHADER_MULTI_COLOURED = generate_shader(
492
+ ShaderConfig(
493
+ name="TRIANGLE_SHADER_MULTI_COLOURED",
494
+ geometry_type="triangle",
495
+ colour_mode="multi",
496
+ )
497
+ )