ncca-ngl 0.3.5__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 +0 -2
  16. ncca/ngl/vec2.py +59 -302
  17. ncca/ngl/vec2_array.py +79 -28
  18. ncca/ngl/vec3.py +60 -350
  19. ncca/ngl/vec3_array.py +76 -23
  20. ncca/ngl/vec4.py +90 -200
  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.5.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.5.dist-info/RECORD +0 -82
  56. {ncca_ngl-0.3.5.dist-info → ncca_ngl-0.5.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,563 @@
1
+ """
2
+ Shader template system for WebGPU pipelines.
3
+ Provides configurable shader templates that can generate single/multi-color variants
4
+ and support user-defined shaders.
5
+ """
6
+
7
+ from abc import ABC, abstractmethod
8
+ from enum import Enum
9
+ from typing import Dict, List, Optional, Union
10
+ import dataclasses
11
+
12
+
13
+ class ColorMode(Enum):
14
+ """Color mode for shader generation."""
15
+
16
+ SINGLE = "single"
17
+ MULTI = "multi"
18
+
19
+
20
+ class PrimitiveType(Enum):
21
+ """Primitive type for shader generation."""
22
+
23
+ POINT = "point"
24
+ LINE = "line"
25
+ TRIANGLE = "triangle"
26
+
27
+
28
+ class RenderMode(Enum):
29
+ """Render mode specialization."""
30
+
31
+ STANDARD = "standard"
32
+ BILLBOARDED = "billboarded"
33
+ INSTANCED = "instanced"
34
+
35
+
36
+ @dataclasses.dataclass
37
+ class UniformField:
38
+ """Definition of a uniform field in the shader."""
39
+
40
+ name: str
41
+ data_type: str
42
+ array_size: Optional[int] = None
43
+ default_value: Optional[Union[float, List[float]]] = None
44
+
45
+
46
+ @dataclasses.dataclass
47
+ class VertexAttribute:
48
+ """Definition of a vertex attribute."""
49
+
50
+ name: str
51
+ data_type: str
52
+ location: int
53
+ step_mode: str = "vertex"
54
+
55
+
56
+ @dataclasses.dataclass
57
+ class ShaderConfig:
58
+ """Configuration for shader generation."""
59
+
60
+ primitive_type: PrimitiveType
61
+ color_mode: ColorMode
62
+ render_mode: RenderMode
63
+ custom_uniforms: Optional[List[UniformField]] = None
64
+ custom_attributes: Optional[List[VertexAttribute]] = None
65
+ custom_vertex_shader: Optional[str] = None
66
+ custom_fragment_shader: Optional[str] = None
67
+
68
+
69
+ class ShaderTemplate(ABC):
70
+ """Abstract base class for shader templates."""
71
+
72
+ @abstractmethod
73
+ def generate_vertex_shader(self, config: ShaderConfig) -> str:
74
+ """Generate vertex shader code from configuration."""
75
+ pass
76
+
77
+ @abstractmethod
78
+ def generate_fragment_shader(self, config: ShaderConfig) -> str:
79
+ """Generate fragment shader code from configuration."""
80
+ pass
81
+
82
+ def generate_shader_code(self, config: ShaderConfig) -> str:
83
+ """Generate complete WGSL shader code."""
84
+ vertex_shader = self.generate_vertex_shader(config)
85
+ fragment_shader = self.generate_fragment_shader(config)
86
+
87
+ # Ensure we have valid shader code
88
+ if not vertex_shader or not fragment_shader:
89
+ raise ValueError("Generated vertex or fragment shader is empty")
90
+
91
+ return f"{vertex_shader}\n{fragment_shader}"
92
+
93
+ def _generate_uniform_struct(self, config: ShaderConfig) -> str:
94
+ """Generate uniform buffer structure definition."""
95
+ uniforms = self._get_base_uniforms(config)
96
+
97
+ if config.custom_uniforms:
98
+ uniforms.extend(config.custom_uniforms)
99
+
100
+ lines = ["struct Uniforms {"]
101
+ for uniform in uniforms:
102
+ if uniform.array_size:
103
+ line = f" {uniform.name}: array<{uniform.data_type}, {uniform.array_size}>,"
104
+ else:
105
+ line = f" {uniform.name}: {uniform.data_type},"
106
+ lines.append(line)
107
+ lines.append("};")
108
+
109
+ return "\n".join(lines)
110
+
111
+ def _get_base_uniforms(self, config: ShaderConfig) -> List[UniformField]:
112
+ """Get base uniform fields for the configuration."""
113
+ uniforms = [
114
+ UniformField("MVP", "mat4x4<f32>"),
115
+ ]
116
+
117
+ # Add view matrix for billboarding
118
+ if config.render_mode == RenderMode.BILLBOARDED:
119
+ uniforms.append(UniformField("ViewMatrix", "mat4x4<f32>"))
120
+
121
+ # Add color/size based on color mode and primitive type
122
+ if config.color_mode == ColorMode.SINGLE:
123
+ if config.primitive_type == PrimitiveType.POINT:
124
+ uniforms.append(UniformField("ColourSize", "vec4<f32>"))
125
+ else:
126
+ uniforms.append(UniformField("colour", "vec3<f32>"))
127
+ uniforms.append(UniformField("padding", "f32"))
128
+
129
+ # Add size for points in multi-color mode
130
+ elif config.primitive_type == PrimitiveType.POINT:
131
+ uniforms.append(UniformField("size", "f32"))
132
+ uniforms.extend([UniformField(f"padding{i}", "u32") for i in range(3)])
133
+ else:
134
+ # Add padding for alignment
135
+ uniforms.extend([UniformField(f"padding{i}", "f32") for i in range(4)])
136
+
137
+ return uniforms
138
+
139
+ def _generate_vertex_input_struct(self, config: ShaderConfig) -> str:
140
+ """Generate vertex input structure definition."""
141
+ attributes = []
142
+
143
+ # Always include position
144
+ attributes.append(VertexAttribute("position", "vec3<f32>", 0))
145
+
146
+ # Add color for multi-color mode
147
+ if config.color_mode == ColorMode.MULTI:
148
+ attributes.append(VertexAttribute("colour", "vec3<f32>", 1))
149
+
150
+ # Add custom attributes
151
+ if config.custom_attributes:
152
+ attributes.extend(config.custom_attributes)
153
+
154
+ lines = ["struct VertexIn {"]
155
+ for attr in attributes:
156
+ lines.append(
157
+ f" @location({attr.location}) {attr.name}: {attr.data_type},"
158
+ )
159
+ lines.append("};")
160
+
161
+ return "\n".join(lines)
162
+
163
+ def _generate_vertex_output_struct(self, config: ShaderConfig) -> str:
164
+ """Generate vertex output structure definition."""
165
+ lines = ["struct VertexOut {"]
166
+ lines.append(" @builtin(position) position: vec4<f32>,")
167
+
168
+ # Add color for multi-color mode
169
+ if config.color_mode == ColorMode.MULTI:
170
+ lines.append(" @location(0) fragColour: vec3<f32>,")
171
+
172
+ # Add UV for billboarding points
173
+ if (
174
+ config.primitive_type == PrimitiveType.POINT
175
+ and config.render_mode == RenderMode.BILLBOARDED
176
+ ):
177
+ location = 1 if config.color_mode == ColorMode.MULTI else 0
178
+ lines.append(f" @location({location}) uv: vec2<f32>,")
179
+
180
+ # Add custom outputs from custom vertex shader analysis
181
+ if config.custom_vertex_shader:
182
+ # Simple parsing for @location annotations in custom vertex shader
183
+ for line in config.custom_vertex_shader.split("\n"):
184
+ if "@location(" in line and "output:" in line:
185
+ lines.append(f" {line.strip()},")
186
+
187
+ lines.append("};")
188
+
189
+ return "\n".join(lines)
190
+
191
+
192
+ class PointShaderTemplate(ShaderTemplate):
193
+ """Shader template for point rendering."""
194
+
195
+ def generate_vertex_shader(self, config: ShaderConfig) -> str:
196
+ """Generate point vertex shader."""
197
+ uniform_struct = self._generate_uniform_struct(config)
198
+ vertex_input = self._generate_vertex_input_struct(config)
199
+ vertex_output = self._generate_vertex_output_struct(config)
200
+
201
+ # Custom vertex shader takes precedence
202
+ if config.custom_vertex_shader:
203
+ return f"{uniform_struct}\n{vertex_input}\n{vertex_output}\n{config.custom_vertex_shader}"
204
+
205
+ # Generate billboarding vertex shader
206
+ if config.render_mode == RenderMode.BILLBOARDED:
207
+ return self._generate_billboard_vertex_shader(
208
+ config, uniform_struct, vertex_input, vertex_output
209
+ )
210
+ else:
211
+ return self._generate_basic_vertex_shader(
212
+ config, uniform_struct, vertex_input, vertex_output
213
+ )
214
+
215
+ def generate_fragment_shader(self, config: ShaderConfig) -> str:
216
+ """Generate point fragment shader."""
217
+ # Custom fragment shader takes precedence
218
+ if config.custom_fragment_shader:
219
+ return config.custom_fragment_shader
220
+
221
+ # Generate circular point fragment shader for billboarding
222
+ if config.render_mode == RenderMode.BILLBOARDED:
223
+ return self._generate_circle_fragment_shader(config)
224
+ else:
225
+ return self._generate_basic_fragment_shader(config)
226
+
227
+ def _generate_billboard_vertex_shader(
228
+ self,
229
+ config: ShaderConfig,
230
+ uniform_struct: str,
231
+ vertex_input: str,
232
+ vertex_output: str,
233
+ ) -> str:
234
+ """Generate billboarding vertex shader for points."""
235
+ return f"""
236
+ {uniform_struct}
237
+
238
+ {vertex_input}
239
+
240
+ {vertex_output}
241
+
242
+ @binding(0) @group(0) var<uniform> uniforms : Uniforms;
243
+
244
+ @vertex
245
+ fn vertex_main(input: VertexIn, @builtin(vertex_index) vertex_index: u32) -> VertexOut {{
246
+ var output: VertexOut;
247
+ let quad_offsets = array<vec2<f32>, 4>(
248
+ vec2<f32>(-1.0, -1.0), // bottom-left
249
+ vec2<f32>(1.0, -1.0), // bottom-right
250
+ vec2<f32>(-1.0, 1.0), // top-left
251
+ vec2<f32>(1.0, 1.0) // top-right
252
+ );
253
+
254
+ // Extract camera right and up vectors from view matrix
255
+ let cameraRight = normalize(vec3<f32>(uniforms.ViewMatrix[0][0], uniforms.ViewMatrix[1][0], uniforms.ViewMatrix[2][0]));
256
+ let cameraUp = normalize(vec3<f32>(uniforms.ViewMatrix[0][1], uniforms.ViewMatrix[1][1], uniforms.ViewMatrix[2][1]));
257
+
258
+ // Calculate billboard offset in world space
259
+ let size = {self._get_size_expression(config)};
260
+ let offset2D = quad_offsets[vertex_index] * size;
261
+ let offset3D = cameraRight * offset2D.x + cameraUp * offset2D.y;
262
+ let worldPos = input.position + offset3D;
263
+
264
+ output.position = uniforms.MVP * vec4<f32>(worldPos, 1.0);
265
+ {self._get_color_passthrough(config, "output")}
266
+ // convert offset from -1 -> 1 to 0 -> 1 for uv
267
+ output.uv = quad_offsets[vertex_index] * 0.5 + 0.5;
268
+
269
+ return output;
270
+ }}
271
+ """
272
+
273
+ def _generate_basic_vertex_shader(
274
+ self,
275
+ config: ShaderConfig,
276
+ uniform_struct: str,
277
+ vertex_input: str,
278
+ vertex_output: str,
279
+ ) -> str:
280
+ """Generate basic vertex shader for points."""
281
+ return f"""
282
+ {uniform_struct}
283
+
284
+ {vertex_input}
285
+
286
+ {vertex_output}
287
+
288
+ @binding(0) @group(0) var<uniform> uniforms : Uniforms;
289
+
290
+ @vertex
291
+ fn vertex_main(input: VertexIn) -> VertexOut {{
292
+ var output: VertexOut;
293
+ output.position = uniforms.MVP * vec4<f32>(input.position, 1.0);
294
+ {self._get_color_passthrough(config, "output")}
295
+ return output;
296
+ }}
297
+ """
298
+
299
+ def _generate_circle_fragment_shader(self, config: ShaderConfig) -> str:
300
+ """Generate fragment shader for circular points."""
301
+ color_expression = self._get_color_expression(config)
302
+
303
+ return f"""
304
+ @fragment
305
+ fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32>
306
+ {{
307
+ let center = vec2<f32>(0.5, 0.5); // Center of the quad in UV space
308
+ let dist = distance(fragData.uv, center); // Distance from center
309
+ let radius = 0.5; // Circle radius (quad is 1.0 in UV space)
310
+
311
+ if (dist > radius)
312
+ {{
313
+ discard; // Remove pixels outside the circle
314
+ }}
315
+
316
+ return {color_expression};
317
+ }}
318
+ """
319
+
320
+ def _generate_basic_fragment_shader(self, config: ShaderConfig) -> str:
321
+ """Generate basic fragment shader for points."""
322
+ color_expression = self._get_color_expression(config)
323
+
324
+ return f"""
325
+ @fragment
326
+ fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32>
327
+ {{
328
+ return {color_expression};
329
+ }}
330
+ """
331
+
332
+ def _get_size_expression(self, config: ShaderConfig) -> str:
333
+ """Get size expression based on color mode."""
334
+ if config.color_mode == ColorMode.SINGLE:
335
+ return "uniforms.ColourSize.w"
336
+ else:
337
+ return "uniforms.size"
338
+
339
+ def _get_color_passthrough(self, config: ShaderConfig, output_var: str) -> str:
340
+ """Get color passthrough expression for vertex shader."""
341
+ if config.color_mode == ColorMode.MULTI:
342
+ return f"{output_var}.fragColour = input.colour;"
343
+ else:
344
+ return ""
345
+
346
+ def _get_color_expression(self, config: ShaderConfig) -> str:
347
+ """Get color expression for fragment shader."""
348
+ # DEBUG: Always return white to test point visibility
349
+ return "vec4<f32>(1.0, 1.0, 1.0, 1.0)"
350
+
351
+ # Original code (commented out for debugging)
352
+ # if config.color_mode == ColorMode.MULTI:
353
+ # return "vec4<f32>(fragData.fragColour, 1.0)"
354
+ # else:
355
+ # return "vec4<f32>(uniforms.colour, 1.0)"
356
+
357
+
358
+ class LineShaderTemplate(ShaderTemplate):
359
+ """Shader template for line rendering."""
360
+
361
+ def generate_vertex_shader(self, config: ShaderConfig) -> str:
362
+ """Generate line vertex shader."""
363
+ uniform_struct = self._generate_uniform_struct(config)
364
+ vertex_input = self._generate_vertex_input_struct(config)
365
+ vertex_output = self._generate_vertex_output_struct(config)
366
+
367
+ if config.custom_vertex_shader:
368
+ return f"{uniform_struct}\n{vertex_input}\n{vertex_output}\n{config.custom_vertex_shader}"
369
+
370
+ return f"""
371
+ {uniform_struct}
372
+
373
+ {vertex_input}
374
+
375
+ {vertex_output}
376
+
377
+ @binding(0) @group(0) var<uniform> uniforms : Uniforms;
378
+
379
+ @vertex
380
+ fn vertex_main(input: VertexIn) -> VertexOut {{
381
+ var output: VertexOut;
382
+ output.position = uniforms.MVP * vec4<f32>(input.position, 1.0);
383
+ {self._get_color_passthrough(config, "output")}
384
+ return output;
385
+ }}
386
+ """
387
+
388
+ def generate_fragment_shader(self, config: ShaderConfig) -> str:
389
+ """Generate line fragment shader."""
390
+ if config.custom_fragment_shader:
391
+ return config.custom_fragment_shader
392
+
393
+ color_expression = self._get_color_expression(config)
394
+
395
+ return f"""
396
+ @fragment
397
+ fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32>
398
+ {{
399
+ return {color_expression};
400
+ }}
401
+ """
402
+
403
+ def _get_color_passthrough(self, config: ShaderConfig, output_var: str) -> str:
404
+ """Get color passthrough expression for vertex shader."""
405
+ if config.color_mode == ColorMode.MULTI:
406
+ return f"{output_var}.fragColour = input.colour;"
407
+ else:
408
+ return ""
409
+
410
+ def _get_color_expression(self, config: ShaderConfig) -> str:
411
+ """Get color expression for fragment shader."""
412
+ # DEBUG: Always return white to test line visibility
413
+ return "vec4<f32>(1.0, 1.0, 1.0, 1.0)"
414
+
415
+ # Original code (commented out for debugging)
416
+ # if config.color_mode == ColorMode.MULTI:
417
+ # return "vec4<f32>(fragData.fragColour, 1.0)"
418
+ # else:
419
+ # return "vec4<f32>(uniforms.colour, 1.0)"
420
+
421
+
422
+ class TriangleShaderTemplate(ShaderTemplate):
423
+ """Shader template for triangle rendering."""
424
+
425
+ def generate_vertex_shader(self, config: ShaderConfig) -> str:
426
+ """Generate triangle vertex shader."""
427
+ uniform_struct = self._generate_uniform_struct(config)
428
+ vertex_input = self._generate_vertex_input_struct(config)
429
+ vertex_output = self._generate_vertex_output_struct(config)
430
+
431
+ if config.custom_vertex_shader:
432
+ return f"{uniform_struct}\n{vertex_input}\n{vertex_output}\n{config.custom_vertex_shader}"
433
+
434
+ return f"""
435
+ {uniform_struct}
436
+
437
+ {vertex_input}
438
+
439
+ {vertex_output}
440
+
441
+ @binding(0) @group(0) var<uniform> uniforms : Uniforms;
442
+
443
+ @vertex
444
+ fn vertex_main(input: VertexIn) -> VertexOut {{
445
+ var output: VertexOut;
446
+ output.position = uniforms.MVP * vec4<f32>(input.position, 1.0);
447
+ {self._get_color_passthrough(config, "output")}
448
+ return output;
449
+ }}
450
+ """
451
+
452
+ def generate_fragment_shader(self, config: ShaderConfig) -> str:
453
+ """Generate triangle fragment shader."""
454
+ if config.custom_fragment_shader:
455
+ return config.custom_fragment_shader
456
+
457
+ color_expression = self._get_color_expression(config)
458
+
459
+ return f"""
460
+ @fragment
461
+ fn fragment_main(fragData: VertexOut) -> @location(0) vec4<f32>
462
+ {{
463
+ return {color_expression};
464
+ }}
465
+ """
466
+
467
+ def _get_color_passthrough(self, config: ShaderConfig, output_var: str) -> str:
468
+ """Get color passthrough expression for vertex shader."""
469
+ if config.color_mode == ColorMode.MULTI:
470
+ return f"{output_var}.fragColour = input.colour;"
471
+ else:
472
+ return ""
473
+
474
+ def _get_color_expression(self, config: ShaderConfig) -> str:
475
+ """Get color expression for fragment shader."""
476
+ # DEBUG: Always return white to test triangle visibility
477
+ return "vec4<f32>(1.0, 1.0, 1.0, 1.0)"
478
+
479
+ # Original code (commented out for debugging)
480
+ # if config.color_mode == ColorMode.MULTI:
481
+ # return "vec4<f32>(fragData.fragColour, 1.0)"
482
+ # else:
483
+ # return "vec4<f32>(uniforms.colour, 1.0)"
484
+
485
+
486
+ class ShaderTemplateRegistry:
487
+ """Registry for shader templates."""
488
+
489
+ def __init__(self):
490
+ """Initialize registry with default templates."""
491
+ # Move template instantiation to separate method to avoid forward reference issues
492
+ self._templates: Dict[PrimitiveType, ShaderTemplate] = {}
493
+
494
+ def _initialize_templates(self):
495
+ """Initialize templates after all classes are defined."""
496
+ if not self._templates:
497
+ self._templates = {
498
+ PrimitiveType.POINT: PointShaderTemplate(),
499
+ PrimitiveType.LINE: LineShaderTemplate(),
500
+ PrimitiveType.TRIANGLE: TriangleShaderTemplate(),
501
+ }
502
+
503
+ def register_template(
504
+ self, primitive_type: PrimitiveType, template: ShaderTemplate
505
+ ) -> None:
506
+ """Register a custom shader template."""
507
+ self._templates[primitive_type] = template
508
+
509
+ def get_template(self, primitive_type: PrimitiveType) -> ShaderTemplate:
510
+ """Get shader template for primitive type."""
511
+ self._initialize_templates() # Ensure templates are initialized
512
+ if primitive_type not in self._templates:
513
+ raise ValueError(
514
+ f"No template registered for primitive type: {primitive_type}"
515
+ )
516
+ return self._templates[primitive_type]
517
+
518
+ def generate_shader(self, config: ShaderConfig) -> str:
519
+ """Generate shader code from configuration."""
520
+ template = self.get_template(config.primitive_type)
521
+ return template.generate_shader_code(config)
522
+
523
+
524
+ # Global registry instance
525
+ shader_registry = ShaderTemplateRegistry()
526
+
527
+
528
+ def generate_shader_code(config: ShaderConfig) -> str:
529
+ """Generate shader code from configuration using global registry."""
530
+ return shader_registry.generate_shader(config)
531
+
532
+
533
+ def create_shader_config(
534
+ primitive_type: PrimitiveType,
535
+ color_mode: ColorMode,
536
+ render_mode: RenderMode = RenderMode.STANDARD,
537
+ custom_vertex_shader: Optional[str] = None,
538
+ custom_fragment_shader: Optional[str] = None,
539
+ custom_uniforms: Optional[List[UniformField]] = None,
540
+ custom_attributes: Optional[List[VertexAttribute]] = None,
541
+ ) -> ShaderConfig:
542
+ """Create a shader configuration."""
543
+ return ShaderConfig(
544
+ primitive_type=primitive_type,
545
+ color_mode=color_mode,
546
+ render_mode=render_mode,
547
+ custom_vertex_shader=custom_vertex_shader,
548
+ custom_fragment_shader=custom_fragment_shader,
549
+ custom_uniforms=custom_uniforms,
550
+ custom_attributes=custom_attributes,
551
+ )
552
+
553
+
554
+ # Register default templates
555
+ shader_registry.register_template(PrimitiveType.POINT, PointShaderTemplate())
556
+ shader_registry.register_template(PrimitiveType.LINE, LineShaderTemplate())
557
+ shader_registry.register_template(PrimitiveType.TRIANGLE, TriangleShaderTemplate())
558
+
559
+
560
+ # Register default templates
561
+ shader_registry.register_template(PrimitiveType.POINT, PointShaderTemplate())
562
+ shader_registry.register_template(PrimitiveType.LINE, LineShaderTemplate())
563
+ shader_registry.register_template(PrimitiveType.TRIANGLE, TriangleShaderTemplate())