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.
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/PKG-INFO +1 -1
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/pyproject.toml +1 -1
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/__init__.py +3 -1
- ncca_ngl-0.1.6/src/ncca/ngl/primitives.py → ncca_ngl-0.2.1/src/ncca/ngl/prim_data.py +39 -142
- ncca_ngl-0.2.1/src/ncca/ngl/primitives.py +219 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/util.py +46 -18
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/LICENSE.txt +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/.gitignore +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/0.13.0/10564494386971134025 +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/0.13.0/7783445477288392980 +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/.ruff_cache/CACHEDIR.TAG +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/Primitives.npz +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/buddah.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/bunny.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/cube.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/dodecahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/dragon.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/football.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/icosahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/octahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/pack_arrays.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/teapot.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/tetrahedron.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/PrimData/troll.npy +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/abstract_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/base_mesh.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/base_mesh.pyi +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/bbox.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/bezier_curve.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/first_person_camera.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/image.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/log.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/mat2.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/mat3.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/mat4.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/multi_buffer_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/obj.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/plane.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/pyside_event_handling_mixin.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/quaternion.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/random.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shader.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shader_lib.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shader_program.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/checker_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/checker_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/colour_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/colour_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/diffuse_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/diffuse_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/text_fragment.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/text_geometry.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/shaders/text_vertex.glsl +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/simple_index_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/simple_vao.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/text.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/texture.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/transform.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vao_factory.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec2.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec2_array.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec3.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec3_array.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/src/ncca/ngl/vec4.py +0 -0
- {ncca_ngl-0.1.6 → ncca_ngl-0.2.1}/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
|
|
|
@@ -66,61 +54,13 @@ def _circle_table(n: int) -> np.ndarray:
|
|
|
66
54
|
return cs
|
|
67
55
|
|
|
68
56
|
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
215
|
-
prim = _primitive(data_array)
|
|
216
|
-
cls._primitives[name] = prim
|
|
145
|
+
return np.array(data, dtype=np.float32)
|
|
217
146
|
|
|
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:
|
|
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
|
-
|
|
314
|
-
prim = _primitive(data_array)
|
|
315
|
-
cls._primitives[name] = prim
|
|
224
|
+
return np.array(data, dtype=np.float32)
|
|
316
225
|
|
|
317
|
-
@
|
|
318
|
-
def
|
|
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
|
-
|
|
433
|
-
prim = _primitive(data_array)
|
|
434
|
-
cls._primitives[name] = prim
|
|
338
|
+
return np.array(data, dtype=np.float32)
|
|
435
339
|
|
|
436
|
-
@
|
|
437
|
-
def
|
|
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
|
-
|
|
514
|
-
prim = _primitive(data_array)
|
|
515
|
-
cls._primitives[name] = prim
|
|
415
|
+
return np.array(data, dtype=np.float32)
|
|
516
416
|
|
|
517
|
-
@
|
|
518
|
-
def
|
|
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
|
-
|
|
572
|
-
prim = _primitive(data_array)
|
|
573
|
-
cls._primitives[name] = prim
|
|
469
|
+
return np.array(data, dtype=np.float32)
|
|
574
470
|
|
|
575
|
-
@
|
|
576
|
-
def
|
|
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
|
-
|
|
622
|
-
prim = _primitive(data_array)
|
|
623
|
-
cls._primitives[name] = prim
|
|
516
|
+
return np.array(data, dtype=np.float32)
|
|
624
517
|
|
|
625
|
-
@
|
|
626
|
-
def
|
|
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
|
-
) ->
|
|
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
|
-
|
|
705
|
-
|
|
706
|
-
|
|
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
|
-
|
|
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
|