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.
- ncca/ngl/PrimData/pack_arrays.py +2 -3
- ncca/ngl/__init__.py +3 -4
- ncca/ngl/base_mesh.py +28 -20
- ncca/ngl/image.py +1 -3
- ncca/ngl/mat2.py +79 -53
- ncca/ngl/mat3.py +104 -185
- ncca/ngl/mat4.py +144 -309
- ncca/ngl/prim_data.py +42 -36
- ncca/ngl/primitives.py +2 -2
- ncca/ngl/pyside_event_handling_mixin.py +0 -108
- ncca/ngl/quaternion.py +69 -36
- ncca/ngl/shader.py +0 -116
- ncca/ngl/shader_program.py +94 -117
- ncca/ngl/texture.py +5 -2
- ncca/ngl/util.py +7 -0
- ncca/ngl/vec2.py +58 -292
- ncca/ngl/vec2_array.py +79 -28
- ncca/ngl/vec3.py +59 -340
- ncca/ngl/vec3_array.py +76 -23
- ncca/ngl/vec4.py +90 -190
- ncca/ngl/vec4_array.py +78 -27
- ncca/ngl/vector_base.py +542 -0
- ncca/ngl/webgpu/__init__.py +20 -0
- ncca/ngl/webgpu/__main__.py +640 -0
- ncca/ngl/webgpu/__main__.py.backup +640 -0
- ncca/ngl/webgpu/base_webgpu_pipeline.py +354 -0
- ncca/ngl/webgpu/custom_shader_pipeline.py +288 -0
- ncca/ngl/webgpu/instanced_geometry_pipeline.py +594 -0
- ncca/ngl/webgpu/line_pipeline.py +405 -0
- ncca/ngl/webgpu/pipeline_factory.py +190 -0
- ncca/ngl/webgpu/pipeline_shaders.py +497 -0
- ncca/ngl/webgpu/point_list_pipeline.py +349 -0
- ncca/ngl/webgpu/point_pipeline.py +336 -0
- ncca/ngl/webgpu/triangle_pipeline.py +419 -0
- ncca/ngl/webgpu/webgpu_constants.py +31 -0
- ncca/ngl/webgpu/webgpu_widget.py +322 -0
- ncca/ngl/webgpu/wip/REFACTORING_SUMMARY.md +82 -0
- ncca/ngl/webgpu/wip/UNIFIED_SYSTEM.md +314 -0
- ncca/ngl/webgpu/wip/buffer_manager.py +396 -0
- ncca/ngl/webgpu/wip/pipeline_config.py +463 -0
- ncca/ngl/webgpu/wip/shader_constants.py +328 -0
- ncca/ngl/webgpu/wip/shader_templates.py +563 -0
- ncca/ngl/webgpu/wip/unified_examples.py +390 -0
- ncca/ngl/webgpu/wip/unified_factory.py +449 -0
- ncca/ngl/webgpu/wip/unified_pipeline.py +469 -0
- ncca/ngl/widgets/__init__.py +18 -2
- ncca/ngl/widgets/__main__.py +2 -1
- ncca/ngl/widgets/lookatwidget.py +2 -1
- ncca/ngl/widgets/mat4widget.py +2 -2
- ncca/ngl/widgets/vec2widget.py +1 -1
- ncca/ngl/widgets/vec3widget.py +1 -0
- {ncca_ngl-0.3.4.dist-info → ncca_ngl-0.5.0.dist-info}/METADATA +3 -2
- ncca_ngl-0.5.0.dist-info/RECORD +105 -0
- ncca/ngl/widgets/transformation_widget.py +0 -299
- ncca_ngl-0.3.4.dist-info/RECORD +0 -82
- {ncca_ngl-0.3.4.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())
|