ncca-ngl 0.1.6__tar.gz → 0.2.1__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.1}/PKG-INFO +1 -1
  2. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/pyproject.toml +1 -1
  3. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/__init__.py +3 -1
  4. ncca_ngl-0.1.6/src/ncca/ngl/primitives.py → ncca_ngl-0.2.1/src/ncca/ngl/prim_data.py +39 -142
  5. ncca_ngl-0.2.1/src/ncca/ngl/primitives.py +219 -0
  6. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/util.py +46 -18
  7. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/LICENSE.txt +0 -0
  8. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/.gitignore +0 -0
  9. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/0.13.0/10564494386971134025 +0 -0
  10. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/0.13.0/7783445477288392980 +0 -0
  11. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/CACHEDIR.TAG +0 -0
  12. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/Primitives.npz +0 -0
  13. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/buddah.npy +0 -0
  14. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/bunny.npy +0 -0
  15. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/cube.npy +0 -0
  16. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/dodecahedron.npy +0 -0
  17. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/dragon.npy +0 -0
  18. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/football.npy +0 -0
  19. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/icosahedron.npy +0 -0
  20. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/octahedron.npy +0 -0
  21. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/pack_arrays.py +0 -0
  22. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/teapot.npy +0 -0
  23. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/tetrahedron.npy +0 -0
  24. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/troll.npy +0 -0
  25. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/abstract_vao.py +0 -0
  26. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/base_mesh.py +0 -0
  27. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/base_mesh.pyi +0 -0
  28. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/bbox.py +0 -0
  29. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/bezier_curve.py +0 -0
  30. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/first_person_camera.py +0 -0
  31. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/image.py +0 -0
  32. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/log.py +0 -0
  33. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/mat2.py +0 -0
  34. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/mat3.py +0 -0
  35. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/mat4.py +0 -0
  36. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/multi_buffer_vao.py +0 -0
  37. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/obj.py +0 -0
  38. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/plane.py +0 -0
  39. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/pyside_event_handling_mixin.py +0 -0
  40. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/quaternion.py +0 -0
  41. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/random.py +0 -0
  42. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shader.py +0 -0
  43. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shader_lib.py +0 -0
  44. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shader_program.py +0 -0
  45. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/checker_fragment.glsl +0 -0
  46. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/checker_vertex.glsl +0 -0
  47. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/colour_fragment.glsl +0 -0
  48. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/colour_vertex.glsl +0 -0
  49. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/diffuse_fragment.glsl +0 -0
  50. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/diffuse_vertex.glsl +0 -0
  51. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/text_fragment.glsl +0 -0
  52. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/text_geometry.glsl +0 -0
  53. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/text_vertex.glsl +0 -0
  54. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/simple_index_vao.py +0 -0
  55. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/simple_vao.py +0 -0
  56. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/text.py +0 -0
  57. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/texture.py +0 -0
  58. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/transform.py +0 -0
  59. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vao_factory.py +0 -0
  60. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec2.py +0 -0
  61. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec2_array.py +0 -0
  62. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec3.py +0 -0
  63. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec3_array.py +0 -0
  64. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec4.py +0 -0
  65. {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/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.1
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.1"
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
 
@@ -66,61 +54,13 @@ def _circle_table(n: int) -> np.ndarray:
66
54
  return cs
67
55
 
68
56
 
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:
57
+ class PrimData:
58
+ @staticmethod
59
+ def line_grid(width: float, depth: float, steps: int) -> np.ndarray:
119
60
  """
120
61
  Creates a line grid primitive.
121
62
 
122
63
  Args:
123
- name: The name of the primitive to create.
124
64
  width: The width of the grid.
125
65
  depth: The depth of the grid.
126
66
  steps: The number of steps in the grid.
@@ -153,19 +93,14 @@ class Primitives:
153
93
  v2 += dstep
154
94
 
155
95
  # 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:
96
+ return np.array(data, dtype=np.float32)
97
+
98
+ @staticmethod
99
+ def triangle_plane(width: float, depth: float, w_p: int, d_p: int, v_n: Vec3) -> np.ndarray:
164
100
  """
165
101
  Creates a triangle plane primitive.
166
102
 
167
103
  Args:
168
- name: The name of the primitive.
169
104
  width: The width of the plane.
170
105
  depth: The depth of the plane.
171
106
  w_p: The number of width partitions.
@@ -191,17 +126,13 @@ class Primitives:
191
126
  # vert 1
192
127
  data.extend([w, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u, v + dv])
193
128
  # 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
- )
129
+ data.extend([w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv])
197
130
  # vert 3
198
131
  data.extend([w, 0.0, d, v_n.x, v_n.y, v_n.z, u, v])
199
132
 
200
133
  # tri 2
201
134
  # 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
- )
135
+ data.extend([w + w_step, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u + du, v + dv])
205
136
  # vert 2
206
137
  data.extend([w + w_step, 0.0, d, v_n.x, v_n.y, v_n.z, u + du, v])
207
138
  # vert 3
@@ -211,34 +142,14 @@ class Primitives:
211
142
  v += dv
212
143
  d += d_step
213
144
 
214
- data_array = np.array(data, dtype=np.float32)
215
- prim = _primitive(data_array)
216
- cls._primitives[name] = prim
145
+ return np.array(data, dtype=np.float32)
217
146
 
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:
147
+ @staticmethod
148
+ def sphere(radius: float, precision: int) -> np.ndarray:
237
149
  """
238
150
  Creates a sphere primitive.
239
151
 
240
152
  Args:
241
- name: The name of the primitive.
242
153
  radius: The radius of the sphere.
243
154
  precision: The precision of the sphere (number of slices).
244
155
  """
@@ -310,19 +221,14 @@ class Primitives:
310
221
  data.append([x1, y1, z1, nx1, ny1, nz1, u1, v1])
311
222
  data.append([x3, y3, z3, nx3, ny3, nz3, u3, v3])
312
223
 
313
- data_array = np.array(data, dtype=np.float32)
314
- prim = _primitive(data_array)
315
- cls._primitives[name] = prim
224
+ return np.array(data, dtype=np.float32)
316
225
 
317
- @classmethod
318
- def create_cone(
319
- cls, name: str, base: float, height: float, slices: int, stacks: int
320
- ) -> None:
226
+ @staticmethod
227
+ def cone(base: float, height: float, slices: int, stacks: int) -> np.ndarray:
321
228
  """
322
229
  Creates a cone primitive.
323
230
 
324
231
  Args:
325
- name: The name of the primitive.
326
232
  base: The radius of the cone's base.
327
233
  height: The height of the cone.
328
234
  slices: The number of divisions around the cone.
@@ -429,14 +335,10 @@ class Primitives:
429
335
  r0 = r1
430
336
  r1 -= r_step
431
337
 
432
- data_array = np.array(data, dtype=np.float32)
433
- prim = _primitive(data_array)
434
- cls._primitives[name] = prim
338
+ return np.array(data, dtype=np.float32)
435
339
 
436
- @classmethod
437
- def create_capsule(
438
- cls, name: str, radius: float, height: float, precision: int
439
- ) -> None:
340
+ @staticmethod
341
+ def capsule(radius: float, height: float, precision: int) -> np.ndarray:
440
342
  """
441
343
  Creates a capsule primitive.
442
344
  The capsule is aligned along the y-axis.
@@ -510,14 +412,10 @@ class Primitives:
510
412
  nx, ny, nz = sb * c1, cb, sb * s1
511
413
  data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
512
414
 
513
- data_array = np.array(data, dtype=np.float32)
514
- prim = _primitive(data_array)
515
- cls._primitives[name] = prim
415
+ return np.array(data, dtype=np.float32)
516
416
 
517
- @classmethod
518
- def create_cylinder(
519
- cls, name: str, radius: float, height: float, slices: int, stacks: int
520
- ) -> None:
417
+ @staticmethod
418
+ def cylinder(radius: float, height: float, slices: int, stacks: int) -> np.ndarray:
521
419
  """
522
420
  Creates a cylinder primitive.
523
421
  The cylinder is aligned along the y-axis.
@@ -568,17 +466,14 @@ class Primitives:
568
466
  data.extend(p_tl)
569
467
  data.extend(p_tr)
570
468
 
571
- data_array = np.array(data, dtype=np.float32)
572
- prim = _primitive(data_array)
573
- cls._primitives[name] = prim
469
+ return np.array(data, dtype=np.float32)
574
470
 
575
- @classmethod
576
- def create_disk(cls, name: str, radius: float, slices: int) -> None:
471
+ @staticmethod
472
+ def disk(radius: float, slices: int) -> np.ndarray:
577
473
  """
578
474
  Creates a disk primitive.
579
475
 
580
476
  Args:
581
- name: The name of the primitive.
582
477
  radius: The radius of the disk.
583
478
  slices: The number of slices to divide the disk into.
584
479
  """
@@ -618,24 +513,19 @@ class Primitives:
618
513
  data.extend(p2)
619
514
  data.extend(p1)
620
515
 
621
- data_array = np.array(data, dtype=np.float32)
622
- prim = _primitive(data_array)
623
- cls._primitives[name] = prim
516
+ return np.array(data, dtype=np.float32)
624
517
 
625
- @classmethod
626
- def create_torus(
627
- cls,
628
- name: str,
518
+ @staticmethod
519
+ def torus(
629
520
  minor_radius: float,
630
521
  major_radius: float,
631
522
  sides: int,
632
523
  rings: int,
633
- ) -> None:
524
+ ) -> np.ndarray:
634
525
  """
635
526
  Creates a torus primitive.
636
527
 
637
528
  Args:
638
- name: The name of the primitive.
639
529
  minor_radius: The minor radius of the torus.
640
530
  major_radius: The major radius of the torus.
641
531
  sides: The number of sides for each ring.
@@ -701,6 +591,13 @@ class Primitives:
701
591
  data.extend(p3)
702
592
  data.extend(p4)
703
593
 
704
- data_array = np.array(data, dtype=np.float32)
705
- prim = _primitive(data_array)
706
- cls._primitives[name] = prim
594
+ return np.array(data, dtype=np.float32)
595
+
596
+ @staticmethod
597
+ def primitive(name: Union[str, enum]) -> np.ndarray:
598
+ prim_folder = Path(__file__).parent / "PrimData"
599
+ prims = np.load(prim_folder / "Primitives.npz")
600
+ try:
601
+ return prims[name]
602
+ except KeyError:
603
+ raise ValueError(f"Primitive '{name}' not found")
@@ -0,0 +1,219 @@
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 load_default_primitives(cls) -> None:
52
+ """Loads the default primitives from the PrimData directory."""
53
+ logger.info("Loading default primitives...")
54
+ if not cls._loaded:
55
+ for p in Prims:
56
+ prim_data = PrimData.primitive(p.value)
57
+ prim = _primitive(prim_data)
58
+ cls._primitives[p.value] = prim
59
+ cls._loaded = True
60
+
61
+ @classmethod
62
+ def create_line_grid(cls, name: str, width: float, depth: float, steps: int) -> None:
63
+ """
64
+ Creates a line grid primitive.
65
+
66
+ Args:
67
+ name: The name of the primitive to create.
68
+ width: The width of the grid.
69
+ depth: The depth of the grid.
70
+ steps: The number of steps in the grid.
71
+ """
72
+ # Convert the list to a NumPy array
73
+ data_array = PrimData.line_grid(width, depth, steps)
74
+ prim = _primitive(data_array)
75
+ cls._primitives[name] = prim
76
+
77
+ @classmethod
78
+ def create_triangle_plane(cls, name: str, width: float, depth: float, w_p: int, d_p: int, v_n: Vec3) -> None:
79
+ """
80
+ Creates a triangle plane primitive.
81
+
82
+ Args:
83
+ name: The name of the primitive.
84
+ width: The width of the plane.
85
+ depth: The depth of the plane.
86
+ w_p: The number of width partitions.
87
+ d_p: The number of depth partitions.
88
+ v_n: The normal vector for the plane.
89
+ """
90
+
91
+ data_array = PrimData.triangle_plane(width, depth, w_p, d_p, v_n)
92
+ prim = _primitive(data_array)
93
+ cls._primitives[name] = prim
94
+
95
+ @classmethod
96
+ def draw(cls, name: Union[str, Prims]) -> None:
97
+ """
98
+ Draws the specified primitive.
99
+
100
+ Args:
101
+ name: The name of the primitive to draw, either as a string or a Prims enum.
102
+ """
103
+ key = name.value if isinstance(name, Prims) else name
104
+ try:
105
+ prim = cls._primitives[key]
106
+ with prim.vao:
107
+ prim.vao.draw()
108
+ except KeyError:
109
+ logger.error(f"Failed to draw primitive {key}")
110
+ return
111
+
112
+ @classmethod
113
+ def create_sphere(cls, name: str, radius: float, precision: int) -> None:
114
+ """
115
+ Creates a sphere primitive.
116
+
117
+ Args:
118
+ name: The name of the primitive.
119
+ radius: The radius of the sphere.
120
+ precision: The precision of the sphere (number of slices).
121
+ """
122
+
123
+ data_array = PrimData.sphere(radius, precision)
124
+ prim = _primitive(data_array)
125
+ cls._primitives[name] = prim
126
+
127
+ @classmethod
128
+ def create_cone(cls, name: str, base: float, height: float, slices: int, stacks: int) -> None:
129
+ """
130
+ Creates a cone primitive.
131
+
132
+ Args:
133
+ name: The name of the primitive.
134
+ base: The radius of the cone's base.
135
+ height: The height of the cone.
136
+ slices: The number of divisions around the cone.
137
+ stacks: The number of divisions along the cone's height.
138
+ """
139
+ data_array = PrimData.cone(base, height, slices, stacks)
140
+ prim = _primitive(data_array)
141
+ cls._primitives[name] = prim
142
+
143
+ @classmethod
144
+ def create_capsule(cls, name: str, radius: float, height: float, precision: int) -> None:
145
+ """
146
+ Creates a capsule primitive.
147
+ The capsule is aligned along the y-axis.
148
+ It is composed of a cylinder and two hemispherical caps.
149
+ based on code from here https://code.google.com/p/rgine/source/browse/trunk/RGine/opengl/src/RGLShapes.cpp
150
+ and adapted
151
+
152
+ Args:
153
+ name: The name of the primitive.
154
+ radius: The radius of the capsule.
155
+ height: The height of the capsule.
156
+ precision: The precision of the capsule.
157
+ """
158
+
159
+ data_array = PrimData.capsule(radius, height, precision)
160
+ prim = _primitive(data_array)
161
+ cls._primitives[name] = prim
162
+
163
+ @classmethod
164
+ def create_cylinder(cls, name: str, radius: float, height: float, slices: int, stacks: int) -> None:
165
+ """
166
+ Creates a cylinder primitive.
167
+ The cylinder is aligned along the y-axis.
168
+ This method generates the cylinder walls, but not the top and bottom caps.
169
+
170
+ Args:
171
+ name: The name of the primitive.
172
+ radius: The radius of the cylinder.
173
+ height: The height of the cylinder.
174
+ slices: The number of slices.
175
+ stacks: The number of stacks.
176
+ """
177
+
178
+ data_array = PrimData.cylinder(radius, height, slices, stacks)
179
+ prim = _primitive(data_array)
180
+ cls._primitives[name] = prim
181
+
182
+ @classmethod
183
+ def create_disk(cls, name: str, radius: float, slices: int) -> None:
184
+ """
185
+ Creates a disk primitive.
186
+
187
+ Args:
188
+ name: The name of the primitive.
189
+ radius: The radius of the disk.
190
+ slices: The number of slices to divide the disk into.
191
+ """
192
+
193
+ data_array = PrimData.disk(radius, slices)
194
+ prim = _primitive(data_array)
195
+ cls._primitives[name] = prim
196
+
197
+ @classmethod
198
+ def create_torus(
199
+ cls,
200
+ name: str,
201
+ minor_radius: float,
202
+ major_radius: float,
203
+ sides: int,
204
+ rings: int,
205
+ ) -> None:
206
+ """
207
+ Creates a torus primitive.
208
+
209
+ Args:
210
+ name: The name of the primitive.
211
+ minor_radius: The minor radius of the torus.
212
+ major_radius: The major radius of the torus.
213
+ sides: The number of sides for each ring.
214
+ rings: The number of rings for the torus.
215
+ """
216
+
217
+ data_array = PrimData.torus(minor_radius, major_radius, sides, rings)
218
+ prim = _primitive(data_array)
219
+ cls._primitives[name] = prim
@@ -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