ncca-ngl 0.1.6__tar.gz → 0.2.2__tar.gz

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 (65) hide show
  1. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/PKG-INFO +1 -1
  2. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/pyproject.toml +1 -1
  3. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/__init__.py +3 -1
  4. ncca_ngl-0.1.6/src/ncca/ngl/primitives.py → ncca_ngl-0.2.2/src/ncca/ngl/prim_data.py +47 -142
  5. ncca_ngl-0.2.2/src/ncca/ngl/primitives.py +121 -0
  6. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/util.py +46 -18
  7. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/LICENSE.txt +0 -0
  8. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/.gitignore +0 -0
  9. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/0.13.0/10564494386971134025 +0 -0
  10. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/0.13.0/7783445477288392980 +0 -0
  11. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/CACHEDIR.TAG +0 -0
  12. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/Primitives.npz +0 -0
  13. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/buddah.npy +0 -0
  14. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/bunny.npy +0 -0
  15. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/cube.npy +0 -0
  16. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/dodecahedron.npy +0 -0
  17. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/dragon.npy +0 -0
  18. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/football.npy +0 -0
  19. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/icosahedron.npy +0 -0
  20. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/octahedron.npy +0 -0
  21. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/pack_arrays.py +0 -0
  22. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/teapot.npy +0 -0
  23. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/tetrahedron.npy +0 -0
  24. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/troll.npy +0 -0
  25. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/abstract_vao.py +0 -0
  26. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/base_mesh.py +0 -0
  27. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/base_mesh.pyi +0 -0
  28. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/bbox.py +0 -0
  29. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/bezier_curve.py +0 -0
  30. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/first_person_camera.py +0 -0
  31. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/image.py +0 -0
  32. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/log.py +0 -0
  33. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/mat2.py +0 -0
  34. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/mat3.py +0 -0
  35. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/mat4.py +0 -0
  36. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/multi_buffer_vao.py +0 -0
  37. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/obj.py +0 -0
  38. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/plane.py +0 -0
  39. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/pyside_event_handling_mixin.py +0 -0
  40. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/quaternion.py +0 -0
  41. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/random.py +0 -0
  42. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shader.py +0 -0
  43. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shader_lib.py +0 -0
  44. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shader_program.py +0 -0
  45. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/checker_fragment.glsl +0 -0
  46. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/checker_vertex.glsl +0 -0
  47. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/colour_fragment.glsl +0 -0
  48. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/colour_vertex.glsl +0 -0
  49. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/diffuse_fragment.glsl +0 -0
  50. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/diffuse_vertex.glsl +0 -0
  51. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/text_fragment.glsl +0 -0
  52. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/text_geometry.glsl +0 -0
  53. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/text_vertex.glsl +0 -0
  54. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/simple_index_vao.py +0 -0
  55. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/simple_vao.py +0 -0
  56. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/text.py +0 -0
  57. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/texture.py +0 -0
  58. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/transform.py +0 -0
  59. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vao_factory.py +0 -0
  60. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec2.py +0 -0
  61. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec2_array.py +0 -0
  62. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec3.py +0 -0
  63. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec3_array.py +0 -0
  64. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec4.py +0 -0
  65. {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec4_array.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ncca-ngl
3
- Version: 0.1.6
3
+ Version: 0.2.2
4
4
  Summary: A Python version of the NGL graphics library.
5
5
  Author: Jon Macey
6
6
  Author-email: Jon Macey <jmacey@bournemouth.ac.uk>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ncca-ngl"
3
- version = "0.1.6"
3
+ version = "0.2.2"
4
4
  description = "A Python version of the NGL graphics library."
5
5
  authors = [{ name = "Jon Macey", email = "jmacey@bournemouth.ac.uk" }]
6
6
  requires-python = ">=3.13"
@@ -25,7 +25,8 @@ from .obj import (
25
25
  ObjParseVertexError,
26
26
  )
27
27
  from .plane import Plane
28
- from .primitives import Primitives, Prims
28
+ from .prim_data import PrimData, Prims
29
+ from .primitives import Primitives
29
30
  from .pyside_event_handling_mixin import PySideEventHandlingMixin
30
31
  from .quaternion import Quaternion
31
32
  from .random import Random
@@ -103,6 +104,7 @@ all = [
103
104
  logger,
104
105
  Primitives,
105
106
  Prims,
107
+ PrimData,
106
108
  FirstPersonCamera,
107
109
  PySideEventHandlingMixin,
108
110
  ]
@@ -1,21 +1,9 @@
1
- """
2
- OpenGL primitive generation and drawing functions
3
- In this class we can generate a pipeline for drawing our data for the most part it will be
4
- x,y,z nx,ny,nz and u,v data in a flat numpy array.
5
- We need to create the data first which is stored in a map as part of the class, we can then call draw
6
- which will generate a pipeline for this object and draw into the current context.
7
- """
8
-
9
1
  import enum
10
2
  from pathlib import Path
11
- from typing import Dict, Union
3
+ from typing import Union
12
4
 
13
5
  import numpy as np
14
- import OpenGL.GL as gl
15
6
 
16
- from .log import logger
17
- from .simple_vao import VertexData
18
- from .vao_factory import VAOFactory, VAOType # noqa
19
7
  from .vec3 import Vec3
20
8
 
21
9
 
@@ -33,6 +21,14 @@ class Prims(enum.Enum):
33
21
  TEAPOT = "teapot"
34
22
  TETRAHEDRON = "tetrahedron"
35
23
  TROLL = "troll"
24
+ SPHERE = "sphere"
25
+ TORUS = "torus"
26
+ LINE_GRID = "line_grid"
27
+ TRIANGLE_PLANE = "triangle_plane"
28
+ CONE = "cone"
29
+ CAPSULE = "capsule"
30
+ CYLINDER = "cylinder"
31
+ DISK = "disk"
36
32
 
37
33
 
38
34
  def _circle_table(n: int) -> np.ndarray:
@@ -66,61 +62,13 @@ def _circle_table(n: int) -> np.ndarray:
66
62
  return cs
67
63
 
68
64
 
69
- class _primitive:
70
- """A private class to hold VAO data for a primitive."""
71
-
72
- def __init__(self, prim_data: np.ndarray):
73
- """
74
- Initializes the primitive with the given data.
75
-
76
- Args:
77
- prim_data: A numpy array containing the vertex data (x,y,z,nx,ny,nz,u,v).
78
- """
79
- self.vao = VAOFactory.create_vao(VAOType.SIMPLE, gl.GL_TRIANGLES)
80
- with self.vao:
81
- data = VertexData(data=prim_data.data, size=prim_data.size)
82
- self.vao.set_data(data)
83
- vert_data_size = 8 * 4 # 4 is sizeof float and 8 is x,y,z,nx,ny,nz,uv
84
- self.vao.set_vertex_attribute_pointer(0, 3, gl.GL_FLOAT, vert_data_size, 0)
85
- self.vao.set_vertex_attribute_pointer(
86
- 1, 3, gl.GL_FLOAT, vert_data_size, Vec3.sizeof()
87
- )
88
- self.vao.set_vertex_attribute_pointer(
89
- 2, 2, gl.GL_FLOAT, vert_data_size, 2 * Vec3.sizeof()
90
- )
91
- self.vao.set_num_indices(prim_data.size // 8)
92
-
93
-
94
- class Primitives:
95
- """A static class for creating and drawing primitives."""
96
-
97
- # this is effectively a static class so we can use it to store data
98
- # and generate pipelines for drawing
99
- _primitives: Dict[str, _primitive] = {}
100
- _loaded: bool = False
101
-
102
- @classmethod
103
- def load_default_primitives(cls) -> None:
104
- """Loads the default primitives from the PrimData directory."""
105
- logger.info("Loading default primitives...")
106
- if not cls._loaded:
107
- prim_folder = Path(__file__).parent / "PrimData"
108
- prims = np.load(prim_folder / "Primitives.npz")
109
- for p in prims.items():
110
- prim_data = p[1]
111
- prim = _primitive(prim_data)
112
- cls._primitives[p[0]] = prim
113
- cls._loaded = True
114
-
115
- @classmethod
116
- def create_line_grid(
117
- cls, name: str, width: float, depth: float, steps: int
118
- ) -> None:
65
+ class PrimData:
66
+ @staticmethod
67
+ def line_grid(width: float, depth: float, steps: int) -> np.ndarray:
119
68
  """
120
69
  Creates a line grid primitive.
121
70
 
122
71
  Args:
123
- name: The name of the primitive to create.
124
72
  width: The width of the grid.
125
73
  depth: The depth of the grid.
126
74
  steps: The number of steps in the grid.
@@ -153,19 +101,14 @@ class Primitives:
153
101
  v2 += dstep
154
102
 
155
103
  # Convert the list to a NumPy array
156
- data_array = np.array(data, dtype=np.float32)
157
- prim = _primitive(data_array)
158
- cls._primitives[name] = prim
159
-
160
- @classmethod
161
- def create_triangle_plane(
162
- cls, name: str, width: float, depth: float, w_p: int, d_p: int, v_n: Vec3
163
- ) -> None:
104
+ return np.array(data, dtype=np.float32)
105
+
106
+ @staticmethod
107
+ def triangle_plane(width: float, depth: float, w_p: int, d_p: int, v_n: Vec3) -> np.ndarray:
164
108
  """
165
109
  Creates a triangle plane primitive.
166
110
 
167
111
  Args:
168
- name: The name of the primitive.
169
112
  width: The width of the plane.
170
113
  depth: The depth of the plane.
171
114
  w_p: The number of width partitions.
@@ -191,17 +134,13 @@ class Primitives:
191
134
  # vert 1
192
135
  data.extend([w, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u, v + dv])
193
136
  # vert 2
194
- data.extend(
195
- [w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv]
196
- )
137
+ data.extend([w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv])
197
138
  # vert 3
198
139
  data.extend([w, 0.0, d, v_n.x, v_n.y, v_n.z, u, v])
199
140
 
200
141
  # tri 2
201
142
  # vert 1
202
- data.extend(
203
- [w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv]
204
- )
143
+ data.extend([w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv])
205
144
  # vert 2
206
145
  data.extend([w + w_step, 0.0, d, v_n.x, v_n.y, v_n.z, u + du, v])
207
146
  # vert 3
@@ -211,34 +150,14 @@ class Primitives:
211
150
  v += dv
212
151
  d += d_step
213
152
 
214
- data_array = np.array(data, dtype=np.float32)
215
- prim = _primitive(data_array)
216
- cls._primitives[name] = prim
153
+ return np.array(data, dtype=np.float32)
217
154
 
218
- @classmethod
219
- def draw(cls, name: Union[str, Prims]) -> None:
220
- """
221
- Draws the specified primitive.
222
-
223
- Args:
224
- name: The name of the primitive to draw, either as a string or a Prims enum.
225
- """
226
- key = name.value if isinstance(name, Prims) else name
227
- try:
228
- prim = cls._primitives[key]
229
- with prim.vao:
230
- prim.vao.draw()
231
- except KeyError:
232
- logger.error(f"Failed to draw primitive {key}")
233
- return
234
-
235
- @classmethod
236
- def create_sphere(cls, name: str, radius: float, precision: int) -> None:
155
+ @staticmethod
156
+ def sphere(radius: float, precision: int) -> np.ndarray:
237
157
  """
238
158
  Creates a sphere primitive.
239
159
 
240
160
  Args:
241
- name: The name of the primitive.
242
161
  radius: The radius of the sphere.
243
162
  precision: The precision of the sphere (number of slices).
244
163
  """
@@ -310,19 +229,14 @@ class Primitives:
310
229
  data.append([x1, y1, z1, nx1, ny1, nz1, u1, v1])
311
230
  data.append([x3, y3, z3, nx3, ny3, nz3, u3, v3])
312
231
 
313
- data_array = np.array(data, dtype=np.float32)
314
- prim = _primitive(data_array)
315
- cls._primitives[name] = prim
232
+ return np.array(data, dtype=np.float32)
316
233
 
317
- @classmethod
318
- def create_cone(
319
- cls, name: str, base: float, height: float, slices: int, stacks: int
320
- ) -> None:
234
+ @staticmethod
235
+ def cone(base: float, height: float, slices: int, stacks: int) -> np.ndarray:
321
236
  """
322
237
  Creates a cone primitive.
323
238
 
324
239
  Args:
325
- name: The name of the primitive.
326
240
  base: The radius of the cone's base.
327
241
  height: The height of the cone.
328
242
  slices: The number of divisions around the cone.
@@ -429,14 +343,10 @@ class Primitives:
429
343
  r0 = r1
430
344
  r1 -= r_step
431
345
 
432
- data_array = np.array(data, dtype=np.float32)
433
- prim = _primitive(data_array)
434
- cls._primitives[name] = prim
346
+ return np.array(data, dtype=np.float32)
435
347
 
436
- @classmethod
437
- def create_capsule(
438
- cls, name: str, radius: float, height: float, precision: int
439
- ) -> None:
348
+ @staticmethod
349
+ def capsule(radius: float, height: float, precision: int) -> np.ndarray:
440
350
  """
441
351
  Creates a capsule primitive.
442
352
  The capsule is aligned along the y-axis.
@@ -510,14 +420,10 @@ class Primitives:
510
420
  nx, ny, nz = sb * c1, cb, sb * s1
511
421
  data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
512
422
 
513
- data_array = np.array(data, dtype=np.float32)
514
- prim = _primitive(data_array)
515
- cls._primitives[name] = prim
423
+ return np.array(data, dtype=np.float32)
516
424
 
517
- @classmethod
518
- def create_cylinder(
519
- cls, name: str, radius: float, height: float, slices: int, stacks: int
520
- ) -> None:
425
+ @staticmethod
426
+ def cylinder(radius: float, height: float, slices: int, stacks: int) -> np.ndarray:
521
427
  """
522
428
  Creates a cylinder primitive.
523
429
  The cylinder is aligned along the y-axis.
@@ -568,17 +474,14 @@ class Primitives:
568
474
  data.extend(p_tl)
569
475
  data.extend(p_tr)
570
476
 
571
- data_array = np.array(data, dtype=np.float32)
572
- prim = _primitive(data_array)
573
- cls._primitives[name] = prim
477
+ return np.array(data, dtype=np.float32)
574
478
 
575
- @classmethod
576
- def create_disk(cls, name: str, radius: float, slices: int) -> None:
479
+ @staticmethod
480
+ def disk(radius: float, slices: int) -> np.ndarray:
577
481
  """
578
482
  Creates a disk primitive.
579
483
 
580
484
  Args:
581
- name: The name of the primitive.
582
485
  radius: The radius of the disk.
583
486
  slices: The number of slices to divide the disk into.
584
487
  """
@@ -618,24 +521,19 @@ class Primitives:
618
521
  data.extend(p2)
619
522
  data.extend(p1)
620
523
 
621
- data_array = np.array(data, dtype=np.float32)
622
- prim = _primitive(data_array)
623
- cls._primitives[name] = prim
524
+ return np.array(data, dtype=np.float32)
624
525
 
625
- @classmethod
626
- def create_torus(
627
- cls,
628
- name: str,
526
+ @staticmethod
527
+ def torus(
629
528
  minor_radius: float,
630
529
  major_radius: float,
631
530
  sides: int,
632
531
  rings: int,
633
- ) -> None:
532
+ ) -> np.ndarray:
634
533
  """
635
534
  Creates a torus primitive.
636
535
 
637
536
  Args:
638
- name: The name of the primitive.
639
537
  minor_radius: The minor radius of the torus.
640
538
  major_radius: The major radius of the torus.
641
539
  sides: The number of sides for each ring.
@@ -701,6 +599,13 @@ class Primitives:
701
599
  data.extend(p3)
702
600
  data.extend(p4)
703
601
 
704
- data_array = np.array(data, dtype=np.float32)
705
- prim = _primitive(data_array)
706
- cls._primitives[name] = prim
602
+ return np.array(data, dtype=np.float32)
603
+
604
+ @staticmethod
605
+ def primitive(name: Union[str, enum]) -> np.ndarray:
606
+ prim_folder = Path(__file__).parent / "PrimData"
607
+ prims = np.load(prim_folder / "Primitives.npz")
608
+ try:
609
+ return prims[name]
610
+ except KeyError:
611
+ raise ValueError(f"Primitive '{name}' not found")
@@ -0,0 +1,121 @@
1
+ """
2
+ OpenGL primitive generation and drawing functions
3
+ In this class we can generate a pipeline for drawing our data for the most part it will be
4
+ x,y,z nx,ny,nz and u,v data in a flat numpy array.
5
+ We need to create the data first which is stored in a map as part of the class, we can then call draw
6
+ which will generate a pipeline for this object and draw into the current context.
7
+ """
8
+
9
+ from typing import Dict, Union
10
+
11
+ import numpy as np
12
+ import OpenGL.GL as gl
13
+
14
+ from .log import logger
15
+ from .prim_data import PrimData, Prims
16
+ from .simple_vao import VertexData
17
+ from .vao_factory import VAOFactory, VAOType # noqa
18
+ from .vec3 import Vec3
19
+
20
+
21
+ class _primitive:
22
+ """A private class to hold VAO data for a primitive."""
23
+
24
+ def __init__(self, prim_data: np.ndarray):
25
+ """
26
+ Initializes the primitive with the given data.
27
+
28
+ Args:
29
+ prim_data: A numpy array containing the vertex data (x,y,z,nx,ny,nz,u,v).
30
+ """
31
+ self.vao = VAOFactory.create_vao(VAOType.SIMPLE, gl.GL_TRIANGLES)
32
+ with self.vao:
33
+ data = VertexData(data=prim_data.data, size=prim_data.size)
34
+ self.vao.set_data(data)
35
+ vert_data_size = 8 * 4 # 4 is sizeof float and 8 is x,y,z,nx,ny,nz,uv
36
+ self.vao.set_vertex_attribute_pointer(0, 3, gl.GL_FLOAT, vert_data_size, 0)
37
+ self.vao.set_vertex_attribute_pointer(1, 3, gl.GL_FLOAT, vert_data_size, Vec3.sizeof())
38
+ self.vao.set_vertex_attribute_pointer(2, 2, gl.GL_FLOAT, vert_data_size, 2 * Vec3.sizeof())
39
+ self.vao.set_num_indices(prim_data.size // 8)
40
+
41
+
42
+ class Primitives:
43
+ """A static class for creating and drawing primitives."""
44
+
45
+ # this is effectively a static class so we can use it to store data
46
+ # and generate pipelines for drawing
47
+ _primitives: Dict[str, _primitive] = {}
48
+ _loaded: bool = False
49
+
50
+ @classmethod
51
+ def create(cls, type: str, name: str, *args: object, **kwargs: object) -> None:
52
+ """
53
+ Creates and stores a primitive object of the specified type from :-
54
+ Prims.SPHERE : (radius: float, precision: int).
55
+ Prims.TORUS : (radius: float, tube_radius: float, precision: int).
56
+ Prims.LINE_GRID : (width: float, depth: float, steps: int).
57
+ Prims.TRIANGLE_PLANE : ( width: float, depth: float, w_p: int, d_p: int, v_n: Vec3).
58
+ Prims.CYLINDER : (radius: float, height: float, slices: int, stacks: int).
59
+ Prims.CAPSULE : (radius: float, height: float, slices: int, stacks: int).
60
+ Prims.CONE : (radius: float, height: float, slices: int, stacks: int).
61
+
62
+ Args:
63
+ type (str): The primitive type, typically from the Prims enum (e.g., Prims.SPHERE).
64
+ name (str): The name to associate with the created primitive.
65
+ *args: Positional arguments to pass to the primitive creation function (e.g., radius, precision).
66
+ **kwargs: Keyword arguments to pass to the primitive creation function.
67
+
68
+ Raises:
69
+ ValueError: If the primitive type is not recognized.
70
+
71
+ Example:
72
+ Primitives.create(Prims.SPHERE, "sphere", 0.3, 32)
73
+ Primitives.create(Prims.SPHERE, "sphere", radius=0.3, precision=32)
74
+ """
75
+ prim_methods = {
76
+ Prims.SPHERE: PrimData.sphere,
77
+ Prims.TORUS: PrimData.torus,
78
+ Prims.LINE_GRID: PrimData.line_grid,
79
+ Prims.TRIANGLE_PLANE: PrimData.triangle_plane,
80
+ Prims.CYLINDER: PrimData.cylinder,
81
+ Prims.DISK: PrimData.disk,
82
+ Prims.CAPSULE: PrimData.capsule,
83
+ Prims.CONE: PrimData.cone,
84
+ }
85
+ try:
86
+ method = prim_methods[type]
87
+ except KeyError:
88
+ raise ValueError(f"Unknown primitive: {name}")
89
+
90
+ cls._primitives[name] = _primitive(method(*args, **kwargs))
91
+
92
+ @classmethod
93
+ def load_default_primitives(cls) -> None:
94
+ """Loads the default primitives from the PrimData directory."""
95
+ logger.info("Loading default primitives...")
96
+ if not cls._loaded:
97
+ for p in Prims:
98
+ try:
99
+ prim_data = PrimData.primitive(p.value)
100
+ prim = _primitive(prim_data)
101
+ cls._primitives[p.value] = prim
102
+ except Exception:
103
+ pass
104
+ cls._loaded = True
105
+
106
+ @classmethod
107
+ def draw(cls, name: Union[str, Prims]) -> None:
108
+ """
109
+ Draws the specified primitive.
110
+
111
+ Args:
112
+ name: The name of the primitive to draw, either as a string or a Prims enum.
113
+ """
114
+ key = name.value if isinstance(name, Prims) else name
115
+ try:
116
+ prim = cls._primitives[key]
117
+ with prim.vao:
118
+ prim.vao.draw()
119
+ except KeyError:
120
+ logger.error(f"Failed to draw primitive {key}")
121
+ return
@@ -3,6 +3,7 @@
3
3
  Most of these functions are based on functions found in other libraries such as GLM, NGL or GLU
4
4
  """
5
5
 
6
+ import enum
6
7
  import math
7
8
 
8
9
  from .mat4 import Mat4
@@ -44,7 +45,32 @@ def look_at(eye, look, up):
44
45
  return result
45
46
 
46
47
 
47
- def perspective(fov, aspect, near, far):
48
+ class PerspMode(enum.Enum):
49
+ OpenGL = "OpenGL"
50
+ WebGPU = "WebGPU"
51
+ Vulkan = "Vulkan"
52
+
53
+
54
+ def perspective(
55
+ fov: float,
56
+ aspect: float,
57
+ near: float,
58
+ far: float,
59
+ mode: PerspMode = PerspMode.OpenGL,
60
+ ) -> Mat4:
61
+ """
62
+ Calculate a perspective matrix for various 3D graphics API's default mode is OpenGL but will covert for Vulkan and Web GPU if
63
+ required.
64
+
65
+ Args :
66
+ fov : float - Field of view angle in degrees.
67
+ aspect : float - Aspect ratio of the viewport.
68
+ near : float - Near clipping plane distance.
69
+ far : float - Far clipping plane distance.
70
+
71
+ Returns:
72
+ Mat4 - The perspective matrix.
73
+ """
48
74
  m = Mat4.zero() # as per glm
49
75
  _range = math.tan(math.radians(fov / 2.0)) * near
50
76
  left = -_range * aspect
@@ -53,34 +79,36 @@ def perspective(fov, aspect, near, far):
53
79
  top = _range
54
80
  m.m[0][0] = (2.0 * near) / (right - left)
55
81
  m.m[1][1] = (2.0 * near) / (top - bottom)
56
- m.m[2][2] = -(far + near) / (far - near)
57
- m.m[2][3] = -1.0
58
- m.m[3][2] = -(2.0 * far * near) / (far - near)
82
+ match mode:
83
+ case PerspMode.OpenGL:
84
+ m.m[2][2] = -(far + near) / (far - near)
85
+ m.m[2][3] = -1.0
86
+ m.m[3][2] = -(2.0 * far * near) / (far - near)
87
+
88
+ # This ensures the clip space Z range is [0, 1] as required by Vulkan and WebGPU.
89
+ case PerspMode.WebGPU | PerspMode.Vulkan:
90
+ m.m[2][2] = -far / (far - near)
91
+ m.m[2][3] = -1.0
92
+ m.m[3][2] = -(far * near) / (far - near)
59
93
  return m
60
94
 
61
95
 
62
- def ortho(left, right, bottom, top, near, far):
63
- """Create an orthographic projection matrix."""
96
+ def ortho(left, right, bottom, top, near, far, mode=PerspMode.OpenGL):
64
97
  m = Mat4.identity()
65
98
  m.m[0][0] = 2.0 / (right - left)
66
99
  m.m[1][1] = 2.0 / (top - bottom)
67
- m.m[2][2] = -2.0 / (far - near)
100
+ match mode:
101
+ case PerspMode.OpenGL:
102
+ m.m[2][2] = -2.0 / (far - near)
103
+ m.m[3][2] = -(far + near) / (far - near)
104
+ case PerspMode.WebGPU | PerspMode.Vulkan:
105
+ m.m[2][2] = -1.0 / (far - near)
106
+ m.m[3][2] = -near / (far - near)
68
107
  m.m[3][0] = -(right + left) / (right - left)
69
108
  m.m[3][1] = -(top + bottom) / (top - bottom)
70
- m.m[3][2] = -(far + near) / (far - near)
71
109
  return m
72
110
 
73
111
 
74
- # Mat4 result(1.0f);
75
- # result.m_00= 2.0f / (_right - _left);
76
- # result.m_11= 2.0f / (_top - _bottom);
77
- # result.m_22= - 2.0f / (_zFar - _zNear);
78
- # result.m_30= - (_right + _left) / (_right - _left);
79
- # result.m_31= - (_top + _bottom) / (_top - _bottom);
80
- # result.m_32= - (_zFar + _zNear) / (_zFar - _zNear);
81
- # return result;
82
-
83
-
84
112
  def frustum(left, right, bottom, top, near, far):
85
113
  """Create a frustum projection matrix."""
86
114
  m = Mat4.zero()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes