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.
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/PKG-INFO +1 -1
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/pyproject.toml +1 -1
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/__init__.py +3 -1
- ncca_ngl-0.1.6/src/ncca/ngl/primitives.py → ncca_ngl-0.2.2/src/ncca/ngl/prim_data.py +47 -142
- ncca_ngl-0.2.2/src/ncca/ngl/primitives.py +121 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/util.py +46 -18
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/LICENSE.txt +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/.gitignore +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/0.13.0/10564494386971134025 +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/0.13.0/7783445477288392980 +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/.ruff_cache/CACHEDIR.TAG +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/Primitives.npz +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/buddah.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/bunny.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/cube.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/dodecahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/dragon.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/football.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/icosahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/octahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/pack_arrays.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/teapot.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/tetrahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/PrimData/troll.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/abstract_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/base_mesh.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/base_mesh.pyi +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/bbox.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/bezier_curve.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/first_person_camera.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/image.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/log.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/mat2.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/mat3.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/mat4.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/multi_buffer_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/obj.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/plane.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/pyside_event_handling_mixin.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/quaternion.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/random.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shader.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shader_lib.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shader_program.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/checker_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/checker_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/colour_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/colour_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/diffuse_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/diffuse_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/text_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/text_geometry.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/shaders/text_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/simple_index_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/simple_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/text.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/texture.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/transform.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vao_factory.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec2.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec2_array.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec3.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec3_array.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec4.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.2}/src/ncca/ngl/vec4_array.py +0 -0
|
@@ -25,7 +25,8 @@ from .obj import (
|
|
|
25
25
|
ObjParseVertexError,
|
|
26
26
|
)
|
|
27
27
|
from .plane import Plane
|
|
28
|
-
from .
|
|
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
|
|
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
|
|
70
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
-
|
|
215
|
-
prim = _primitive(data_array)
|
|
216
|
-
cls._primitives[name] = prim
|
|
153
|
+
return np.array(data, dtype=np.float32)
|
|
217
154
|
|
|
218
|
-
@
|
|
219
|
-
def
|
|
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
|
-
|
|
314
|
-
prim = _primitive(data_array)
|
|
315
|
-
cls._primitives[name] = prim
|
|
232
|
+
return np.array(data, dtype=np.float32)
|
|
316
233
|
|
|
317
|
-
@
|
|
318
|
-
def
|
|
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
|
-
|
|
433
|
-
prim = _primitive(data_array)
|
|
434
|
-
cls._primitives[name] = prim
|
|
346
|
+
return np.array(data, dtype=np.float32)
|
|
435
347
|
|
|
436
|
-
@
|
|
437
|
-
def
|
|
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
|
-
|
|
514
|
-
prim = _primitive(data_array)
|
|
515
|
-
cls._primitives[name] = prim
|
|
423
|
+
return np.array(data, dtype=np.float32)
|
|
516
424
|
|
|
517
|
-
@
|
|
518
|
-
def
|
|
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
|
-
|
|
572
|
-
prim = _primitive(data_array)
|
|
573
|
-
cls._primitives[name] = prim
|
|
477
|
+
return np.array(data, dtype=np.float32)
|
|
574
478
|
|
|
575
|
-
@
|
|
576
|
-
def
|
|
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
|
-
|
|
622
|
-
prim = _primitive(data_array)
|
|
623
|
-
cls._primitives[name] = prim
|
|
524
|
+
return np.array(data, dtype=np.float32)
|
|
624
525
|
|
|
625
|
-
@
|
|
626
|
-
def
|
|
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
|
-
) ->
|
|
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
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|