ncca-ngl 0.1.1__py3-none-any.whl → 0.1.4__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 (68) hide show
  1. ncca/ngl/.ruff_cache/.gitignore +2 -0
  2. ncca/ngl/.ruff_cache/0.13.0/10564494386971134025 +0 -0
  3. ncca/ngl/.ruff_cache/0.13.0/7783445477288392980 +0 -0
  4. ncca/ngl/.ruff_cache/CACHEDIR.TAG +1 -0
  5. ncca/ngl/PrimData/Primitives.npz +0 -0
  6. ncca/ngl/PrimData/buddah.npy +0 -0
  7. ncca/ngl/PrimData/bunny.npy +0 -0
  8. ncca/ngl/PrimData/cube.npy +0 -0
  9. ncca/ngl/PrimData/dodecahedron.npy +0 -0
  10. ncca/ngl/PrimData/dragon.npy +0 -0
  11. ncca/ngl/PrimData/football.npy +0 -0
  12. ncca/ngl/PrimData/icosahedron.npy +0 -0
  13. ncca/ngl/PrimData/octahedron.npy +0 -0
  14. ncca/ngl/PrimData/pack_arrays.py +20 -0
  15. ncca/ngl/PrimData/teapot.npy +0 -0
  16. ncca/ngl/PrimData/tetrahedron.npy +0 -0
  17. ncca/ngl/PrimData/troll.npy +0 -0
  18. ncca/ngl/__init__.py +100 -0
  19. ncca/ngl/abstract_vao.py +85 -0
  20. ncca/ngl/base_mesh.py +170 -0
  21. ncca/ngl/base_mesh.pyi +11 -0
  22. ncca/ngl/bbox.py +224 -0
  23. ncca/ngl/bezier_curve.py +75 -0
  24. ncca/ngl/first_person_camera.py +174 -0
  25. ncca/ngl/image.py +94 -0
  26. ncca/ngl/log.py +44 -0
  27. ncca/ngl/mat2.py +128 -0
  28. ncca/ngl/mat3.py +466 -0
  29. ncca/ngl/mat4.py +456 -0
  30. ncca/ngl/multi_buffer_vao.py +49 -0
  31. ncca/ngl/obj.py +416 -0
  32. ncca/ngl/plane.py +47 -0
  33. ncca/ngl/primitives.py +706 -0
  34. ncca/ngl/pyside_event_handling_mixin.py +318 -0
  35. ncca/ngl/quaternion.py +112 -0
  36. ncca/ngl/random.py +167 -0
  37. ncca/ngl/shader.py +229 -0
  38. ncca/ngl/shader_lib.py +536 -0
  39. ncca/ngl/shader_program.py +785 -0
  40. ncca/ngl/shaders/checker_fragment.glsl +35 -0
  41. ncca/ngl/shaders/checker_vertex.glsl +19 -0
  42. ncca/ngl/shaders/colour_fragment.glsl +8 -0
  43. ncca/ngl/shaders/colour_vertex.glsl +11 -0
  44. ncca/ngl/shaders/diffuse_fragment.glsl +21 -0
  45. ncca/ngl/shaders/diffuse_vertex.glsl +24 -0
  46. ncca/ngl/shaders/text_fragment.glsl +10 -0
  47. ncca/ngl/shaders/text_geometry.glsl +53 -0
  48. ncca/ngl/shaders/text_vertex.glsl +18 -0
  49. ncca/ngl/simple_index_vao.py +65 -0
  50. ncca/ngl/simple_vao.py +42 -0
  51. ncca/ngl/text.py +342 -0
  52. ncca/ngl/texture.py +75 -0
  53. ncca/ngl/transform.py +95 -0
  54. ncca/ngl/util.py +128 -0
  55. ncca/ngl/vao_factory.py +34 -0
  56. ncca/ngl/vec2.py +350 -0
  57. ncca/ngl/vec2_array.py +124 -0
  58. ncca/ngl/vec3.py +401 -0
  59. ncca/ngl/vec3_array.py +128 -0
  60. ncca/ngl/vec4.py +229 -0
  61. ncca/ngl/vec4_array.py +124 -0
  62. ncca_ngl-0.1.4.dist-info/METADATA +22 -0
  63. ncca_ngl-0.1.4.dist-info/RECORD +64 -0
  64. ncca_ngl-0.1.4.dist-info/WHEEL +4 -0
  65. ncca_ngl-0.1.1.dist-info/METADATA +0 -23
  66. ncca_ngl-0.1.1.dist-info/RECORD +0 -4
  67. ncca_ngl-0.1.1.dist-info/WHEEL +0 -4
  68. ncca_ngl-0.1.1.dist-info/licenses/LICENSE.txt +0 -7
ncca/ngl/shader_lib.py ADDED
@@ -0,0 +1,536 @@
1
+ from __future__ import annotations
2
+
3
+ import enum
4
+ from pathlib import Path
5
+
6
+ import OpenGL.GL as gl
7
+
8
+ from .log import logger
9
+ from .shader import Shader, ShaderType
10
+ from .shader_program import ShaderProgram
11
+
12
+
13
+ class DefaultShader(enum.Enum):
14
+ """
15
+ Enum representing the default shaders available in the library.
16
+ """
17
+
18
+ COLOUR = "nglColourShader"
19
+ TEXT = "nglTextShader"
20
+ DIFFUSE = "nglDiffuseShader"
21
+ CHECKER = "nglCheckerShader"
22
+
23
+
24
+ class _ShaderLib:
25
+ """
26
+ Shader library for managing OpenGL shader programs and shaders.
27
+ Provides methods to load, compile, link, and use shaders, as well as manage uniforms and uniform blocks.
28
+ """
29
+
30
+ def __init__(self):
31
+ """
32
+ Initialize the shader library with empty registries for shader programs, shaders, and uniform blocks.
33
+ """
34
+ self._shader_programs: dict[str, ShaderProgram] = {}
35
+ self._shaders: dict[str, Shader] = {}
36
+ self._current_shader: str | None = None
37
+ self._default_shaders_loaded: bool = False
38
+ self._registered_uniform_blocks: dict[str, dict] = {}
39
+
40
+ def load_shader(
41
+ self,
42
+ name: str,
43
+ vert: str,
44
+ frag: str,
45
+ geo: str = None,
46
+ exit_on_error: bool = True,
47
+ ) -> bool:
48
+ """
49
+ Load, compile, and link a shader program from vertex, fragment, and optionally geometry shader sources.
50
+
51
+ Args:
52
+ name: Name of the shader program.
53
+ vert: Path to the vertex shader source file.
54
+ frag: Path to the fragment shader source file.
55
+ geo: Optional path to the geometry shader source file.
56
+ exit_on_error: Whether to exit on shader compilation/linking error.
57
+
58
+ Returns:
59
+ bool: True if the shader program was successfully created, False otherwise.
60
+ """
61
+ program = ShaderProgram(name, exit_on_error)
62
+
63
+ # Load and compile vertex shader
64
+ vert_shader = Shader(f"{name}Vertex", ShaderType.VERTEX.value, exit_on_error)
65
+ vert_shader.load(vert)
66
+ if not vert_shader.compile():
67
+ logger.error(f"Failed to compile vertex shader for {name}")
68
+ return False
69
+
70
+ # Load and compile fragment shader
71
+ frag_shader = Shader(
72
+ f"{name}Fragment", ShaderType.FRAGMENT.value, exit_on_error
73
+ )
74
+ frag_shader.load(frag)
75
+ if not frag_shader.compile():
76
+ logger.error(f"Failed to compile fragment shader for {name}")
77
+ return False
78
+
79
+ # Attach compiled shaders to the program
80
+ program.attach_shader(vert_shader)
81
+ program.attach_shader(frag_shader)
82
+
83
+ # Optionally load and compile geometry shader
84
+ if geo:
85
+ geo_shader = Shader(
86
+ f"{name}Geometry", ShaderType.GEOMETRY.value, exit_on_error
87
+ )
88
+ geo_shader.load(geo)
89
+ if not geo_shader.compile():
90
+ logger.error(f"Failed to compile geometry shader for {name}")
91
+ return False
92
+ program.attach_shader(geo_shader)
93
+
94
+ # Link the shader program
95
+ if not program.link():
96
+ logger.error(f"Failed to link shader program for {name}")
97
+ return False
98
+
99
+ self._shader_programs[name] = program
100
+ logger.info(f"Shader program '{name}' created")
101
+ return True
102
+
103
+ def use(self, name: str | None) -> None:
104
+ """
105
+ Activate the specified shader program by name, or deactivate shaders if name is None.
106
+
107
+ Args:
108
+ name: Name of the shader program to use, or None to clear the current shader.
109
+ """
110
+ # Handle None to clear current shader
111
+ if name is None:
112
+ gl.glUseProgram(0)
113
+ self._current_shader = None
114
+ return
115
+
116
+ # Lazy load default shaders on request
117
+ if not self._default_shaders_loaded and name not in self._shader_programs:
118
+ logger.warning("Default shaders not loaded loading now")
119
+ self._load_default_shaders()
120
+
121
+ if name in self._shader_programs:
122
+ self._shader_programs[name].use()
123
+ self._current_shader = name
124
+ else:
125
+ logger.error(f"Shader '{name}' not found")
126
+ gl.glUseProgram(0)
127
+ self._current_shader = None
128
+
129
+ def get_current_shader_name(self) -> str | None:
130
+ """
131
+ Get the name of the currently active shader program.
132
+
133
+ Returns:
134
+ str | None: Name of the current shader, or None if no shader is active.
135
+ """
136
+ return self._current_shader
137
+
138
+ def get_program_id(self, name: str) -> int | None:
139
+ """
140
+ Get the OpenGL program ID for a shader program by name.
141
+
142
+ Args:
143
+ name: Name of the shader program.
144
+
145
+ Returns:
146
+ int | None: OpenGL program ID, or None if not found.
147
+ """
148
+ if name in self._shader_programs:
149
+ return self._shader_programs[name].get_id()
150
+ return None
151
+
152
+ def create_shader_program(self, name: str, exit_on_error: bool = True) -> None:
153
+ """
154
+ Create a new ShaderProgram and register it by name.
155
+
156
+ Args:
157
+ name: Name of the shader program.
158
+ exit_on_error: Whether to exit on error.
159
+ """
160
+ self._shader_programs[name] = ShaderProgram(name, exit_on_error)
161
+
162
+ def attach_shader(
163
+ self, name: str, type: ShaderType, exit_on_error: bool = True
164
+ ) -> None:
165
+ """
166
+ Create and register a Shader object by name and type.
167
+
168
+ Args:
169
+ name: Name of the shader.
170
+ type: ShaderType (VERTEX, FRAGMENT, GEOMETRY).
171
+ exit_on_error: Whether to exit on error.
172
+ """
173
+ self._shaders[name] = Shader(name, type.value, exit_on_error)
174
+
175
+ def load_shader_source(self, name: str, source_file: str) -> None:
176
+ """
177
+ Load shader source code from a file into a registered Shader.
178
+
179
+ Args:
180
+ name: Name of the shader.
181
+ source_file: Path to the shader source file.
182
+ """
183
+ if name in self._shaders:
184
+ self._shaders[name].load(source_file)
185
+ else:
186
+ logger.error(f"Error: shader {name} not found")
187
+
188
+ def load_shader_source_from_string(self, name: str, source_string: str) -> None:
189
+ """
190
+ Load shader source code from a string into a registered Shader.
191
+
192
+ Args:
193
+ name: Name of the shader.
194
+ source_string: Shader source code as a string.
195
+ """
196
+ if name in self._shaders:
197
+ self._shaders[name].load_shader_source_from_string(source_string)
198
+ else:
199
+ logger.error(f"Error: shader {name} not found")
200
+
201
+ def compile_shader(self, name: str) -> bool:
202
+ """
203
+ Compile a registered Shader by name.
204
+
205
+ Args:
206
+ name: Name of the shader.
207
+
208
+ Returns:
209
+ bool: True if compilation succeeded, False otherwise.
210
+ """
211
+ if name in self._shaders:
212
+ return self._shaders[name].compile()
213
+ else:
214
+ logger.error(f"Error: shader {name} not found")
215
+ return False
216
+
217
+ def attach_shader_to_program(self, program_name: str, shader_name: str) -> None:
218
+ """
219
+ Attach a registered Shader to a registered ShaderProgram.
220
+
221
+ Args:
222
+ program_name: Name of the shader program.
223
+ shader_name: Name of the shader.
224
+ """
225
+ if program_name in self._shader_programs and shader_name in self._shaders:
226
+ self._shader_programs[program_name].attach_shader(
227
+ self._shaders[shader_name]
228
+ )
229
+ else:
230
+ logger.error(
231
+ f"Error: program {program_name} or shader {shader_name} not found"
232
+ )
233
+
234
+ def link_program_object(self, name: str) -> bool:
235
+ """
236
+ Link a registered ShaderProgram by name.
237
+
238
+ Args:
239
+ name: Name of the shader program.
240
+
241
+ Returns:
242
+ bool: True if linking succeeded, False otherwise.
243
+ """
244
+ if name in self._shader_programs:
245
+ return self._shader_programs[name].link()
246
+ else:
247
+ logger.error(f"Error: program {name} not found")
248
+ return False
249
+
250
+ def set_uniform(self, name: str, *value) -> None:
251
+ """
252
+ Set a uniform variable in the currently active shader program.
253
+
254
+ Args:
255
+ name: Name of the uniform variable.
256
+ *value: Values to set for the uniform.
257
+ """
258
+ if self._current_shader:
259
+ self._shader_programs[self._current_shader].set_uniform(name, *value)
260
+
261
+ def set_uniform_buffer(self, uniform_block_name: str, size: int, data) -> bool:
262
+ """
263
+ Set uniform buffer data for the specified uniform block in the current shader.
264
+
265
+ Args:
266
+ uniform_block_name: Name of the uniform block.
267
+ size: Size of the data in bytes.
268
+ data: Data to upload (can be ctypes array, bytes, or buffer-like object).
269
+
270
+ Returns:
271
+ bool: True if successful, False otherwise.
272
+ """
273
+ if self._current_shader:
274
+ return self._shader_programs[self._current_shader].set_uniform_buffer(
275
+ uniform_block_name, size, data
276
+ )
277
+ else:
278
+ logger.error("No current shader active")
279
+ return False
280
+
281
+ def get_uniform_1f(self, name: str) -> float:
282
+ """
283
+ Get the value of a float uniform variable from the current shader.
284
+
285
+ Args:
286
+ name: Name of the uniform variable.
287
+
288
+ Returns:
289
+ float: Value of the uniform, or 0.0 if not found.
290
+ """
291
+ if self._current_shader:
292
+ return self._shader_programs[self._current_shader].get_uniform_1f(name)
293
+ return 0.0
294
+
295
+ def get_uniform_2f(self, name: str) -> list[float]:
296
+ """
297
+ Get the value of a vec2 uniform variable from the current shader.
298
+
299
+ Args:
300
+ name: Name of the uniform variable.
301
+
302
+ Returns:
303
+ list[float]: List of 2 float values, or [0.0, 0.0] if not found.
304
+ """
305
+ if self._current_shader:
306
+ return self._shader_programs[self._current_shader].get_uniform_2f(name)
307
+ return [0.0, 0.0]
308
+
309
+ def get_uniform_3f(self, name: str) -> list[float]:
310
+ """
311
+ Get the value of a vec3 uniform variable from the current shader.
312
+
313
+ Args:
314
+ name: Name of the uniform variable.
315
+
316
+ Returns:
317
+ list[float]: List of 3 float values, or [0.0, 0.0, 0.0] if not found.
318
+ """
319
+ if self._current_shader:
320
+ return self._shader_programs[self._current_shader].get_uniform_3f(name)
321
+ return [0.0, 0.0, 0.0]
322
+
323
+ def get_uniform_4f(self, name: str) -> list[float]:
324
+ """
325
+ Get the value of a vec4 uniform variable from the current shader.
326
+
327
+ Args:
328
+ name: Name of the uniform variable.
329
+
330
+ Returns:
331
+ list[float]: List of 4 float values, or [0.0, 0.0, 0.0, 0.0] if not found.
332
+ """
333
+ if self._current_shader:
334
+ return self._shader_programs[self._current_shader].get_uniform_4f(name)
335
+ return [0.0, 0.0, 0.0, 0.0]
336
+
337
+ def get_uniform_mat2(self, name: str) -> list[float]:
338
+ """
339
+ Get the value of a mat2 uniform variable from the current shader.
340
+
341
+ Args:
342
+ name: Name of the uniform variable.
343
+
344
+ Returns:
345
+ list[float]: List of 4 float values, or [0.0]*4 if not found.
346
+ """
347
+ if self._current_shader:
348
+ return self._shader_programs[self._current_shader].get_uniform_mat2(name)
349
+ return [0.0] * 4
350
+
351
+ def get_uniform_mat3(self, name: str) -> list[float]:
352
+ """
353
+ Get the value of a mat3 uniform variable from the current shader.
354
+
355
+ Args:
356
+ name: Name of the uniform variable.
357
+
358
+ Returns:
359
+ list[float]: List of 9 float values, or [0.0]*9 if not found.
360
+ """
361
+ if self._current_shader:
362
+ return self._shader_programs[self._current_shader].get_uniform_mat3(name)
363
+ return [0.0] * 9
364
+
365
+ def get_uniform_mat4(self, name: str) -> list[float]:
366
+ """
367
+ Get the value of a mat4 uniform variable from the current shader.
368
+
369
+ Args:
370
+ name: Name of the uniform variable.
371
+
372
+ Returns:
373
+ list[float]: List of 16 float values, or [0.0]*16 if not found.
374
+ """
375
+ if self._current_shader:
376
+ return self._shader_programs[self._current_shader].get_uniform_mat4(name)
377
+ return [0.0] * 16
378
+
379
+ def edit_shader(self, shader_name: str, to_find: str, replace_with: str) -> bool:
380
+ """
381
+ Edit the source code of a registered shader by replacing a substring.
382
+
383
+ Args:
384
+ shader_name: Name of the shader.
385
+ to_find: Substring to find.
386
+ replace_with: Substring to replace with.
387
+
388
+ Returns:
389
+ bool: True if edit succeeded, False otherwise.
390
+ """
391
+ if shader_name in self._shaders:
392
+ return self._shaders[shader_name].edit_shader(to_find, replace_with)
393
+ return False
394
+
395
+ def reset_edits(self, shader_name: str) -> None:
396
+ """
397
+ Reset any edits made to a registered shader's source code.
398
+
399
+ Args:
400
+ shader_name: Name of the shader.
401
+ """
402
+ if shader_name in self._shaders:
403
+ self._shaders[shader_name].reset_edits()
404
+
405
+ def _load_default_shaders(self) -> None:
406
+ """
407
+ Load the default shaders from the 'shaders' directory and register them.
408
+ """
409
+ shader_folder = Path(__file__).parent / "shaders"
410
+
411
+ # Define which default shaders to load and their corresponding files
412
+ to_load = {
413
+ DefaultShader.COLOUR: {
414
+ "vertex": shader_folder / "colour_vertex.glsl",
415
+ "fragment": shader_folder / "colour_fragment.glsl",
416
+ },
417
+ DefaultShader.DIFFUSE: {
418
+ "vertex": shader_folder / "diffuse_vertex.glsl",
419
+ "fragment": shader_folder / "diffuse_fragment.glsl",
420
+ },
421
+ DefaultShader.CHECKER: {
422
+ "vertex": shader_folder / "checker_vertex.glsl",
423
+ "fragment": shader_folder / "checker_fragment.glsl",
424
+ },
425
+ }
426
+
427
+ # Load each default shader program
428
+ for shader_name, shader_data in to_load.items():
429
+ if self.load_shader(
430
+ shader_name, shader_data["vertex"], shader_data["fragment"]
431
+ ):
432
+ logger.info(f"{shader_name} shader loaded successfully")
433
+
434
+ # Text shader has geometry shader as well
435
+ if self.load_shader(
436
+ DefaultShader.TEXT,
437
+ vert=shader_folder / "text_vertex.glsl",
438
+ frag=shader_folder / "text_fragment.glsl",
439
+ geo=shader_folder / "text_geometry.glsl",
440
+ ):
441
+ logger.info("DefaultShader.TEXT shader loaded successfully")
442
+
443
+ self._default_shaders_loaded = True
444
+
445
+ def print_registered_uniforms(self, shader_name: str = None) -> None:
446
+ """
447
+ Print the registered uniforms for a shader program.
448
+
449
+ Args:
450
+ shader_name: Name of the shader program. If None, uses the current shader.
451
+ """
452
+ if shader_name is None:
453
+ shader_name = self._current_shader
454
+
455
+ if shader_name in self._shader_programs:
456
+ self._shader_programs[shader_name].print_registered_uniforms()
457
+ else:
458
+ logger.error(f"Shader '{shader_name}' not found")
459
+
460
+ def print_properties(self) -> None:
461
+ """
462
+ Print properties of the currently active shader program.
463
+ """
464
+ if self._current_shader in self._shader_programs:
465
+ logger.info(
466
+ "_______________________________________________________________________________________________________________________"
467
+ )
468
+ logger.info(
469
+ f"Printing Properties for ShaderProgram {self._current_shader} "
470
+ )
471
+ logger.info(
472
+ "_______________________________________________________________________________________________________________________"
473
+ )
474
+ self._shader_programs[self._current_shader].print_properties()
475
+ logger.info(
476
+ "_______________________________________________________________________________________________________________________"
477
+ )
478
+ else:
479
+ logger.warning(
480
+ f"Warning no currently active shader to print properties for {self._current_shader} "
481
+ )
482
+
483
+ def auto_register_uniform_blocks(self, shader_name: str = None) -> None:
484
+ """
485
+ Auto-register uniform blocks for the specified shader program.
486
+ If no shader_name is provided, uses the current shader.
487
+
488
+ Args:
489
+ shader_name: Name of the shader program. If None, uses the current shader.
490
+ """
491
+ if shader_name is None:
492
+ shader_name = self._current_shader
493
+
494
+ if shader_name not in self._shader_programs:
495
+ logger.error(f"Shader program '{shader_name}' not found")
496
+ return
497
+
498
+ # Delegate to the ShaderProgram's auto_register_uniform_blocks method
499
+ program = self._shader_programs[shader_name]
500
+ program.auto_register_uniform_blocks()
501
+
502
+ # Copy the uniform blocks to our registry
503
+ if shader_name not in self._registered_uniform_blocks:
504
+ self._registered_uniform_blocks[shader_name] = {}
505
+
506
+ self._registered_uniform_blocks[shader_name] = (
507
+ program.get_registered_uniform_blocks()
508
+ )
509
+
510
+ def get_uniform_block_data(self, shader_name: str = None, block_name: str = None):
511
+ """
512
+ Get uniform block data for the specified shader and block name.
513
+ If shader_name is None, uses current shader.
514
+ If block_name is None, returns all blocks for the shader.
515
+
516
+ Args:
517
+ shader_name: Name of the shader program. If None, uses the current shader.
518
+ block_name: Name of the uniform block. If None, returns all blocks.
519
+
520
+ Returns:
521
+ dict or None: Uniform block data, or None if not found.
522
+ """
523
+ if shader_name is None:
524
+ shader_name = self._current_shader
525
+
526
+ if shader_name not in self._registered_uniform_blocks:
527
+ return None
528
+
529
+ if block_name is None:
530
+ return self._registered_uniform_blocks[shader_name]
531
+ else:
532
+ return self._registered_uniform_blocks[shader_name].get(block_name)
533
+
534
+
535
+ # Singleton instance of the shader library for use throughout the application.
536
+ ShaderLib = _ShaderLib()