ncca-ngl 0.1.6__py3-none-any.whl → 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ncca/ngl/__init__.py +3 -1
- ncca/ngl/prim_data.py +611 -0
- ncca/ngl/primitives.py +51 -636
- ncca/ngl/util.py +46 -18
- {ncca_ngl-0.1.6.dist-info → ncca_ngl-0.2.2.dist-info}/METADATA +1 -1
- {ncca_ngl-0.1.6.dist-info → ncca_ngl-0.2.2.dist-info}/RECORD +7 -6
- {ncca_ngl-0.1.6.dist-info → ncca_ngl-0.2.2.dist-info}/WHEEL +1 -1
ncca/ngl/primitives.py
CHANGED
|
@@ -6,66 +6,18 @@ We need to create the data first which is stored in a map as part of the class,
|
|
|
6
6
|
which will generate a pipeline for this object and draw into the current context.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import enum
|
|
10
|
-
from pathlib import Path
|
|
11
9
|
from typing import Dict, Union
|
|
12
10
|
|
|
13
11
|
import numpy as np
|
|
14
12
|
import OpenGL.GL as gl
|
|
15
13
|
|
|
16
14
|
from .log import logger
|
|
15
|
+
from .prim_data import PrimData, Prims
|
|
17
16
|
from .simple_vao import VertexData
|
|
18
17
|
from .vao_factory import VAOFactory, VAOType # noqa
|
|
19
18
|
from .vec3 import Vec3
|
|
20
19
|
|
|
21
20
|
|
|
22
|
-
class Prims(enum.Enum):
|
|
23
|
-
"""Enum for the default primitives that can be loaded."""
|
|
24
|
-
|
|
25
|
-
BUDDAH = "buddah"
|
|
26
|
-
BUNNY = "bunny"
|
|
27
|
-
CUBE = "cube"
|
|
28
|
-
DODECAHEDRON = "dodecahedron"
|
|
29
|
-
DRAGON = "dragon"
|
|
30
|
-
FOOTBALL = "football"
|
|
31
|
-
ICOSAHEDRON = "icosahedron"
|
|
32
|
-
OCTAHEDRON = "octahedron"
|
|
33
|
-
TEAPOT = "teapot"
|
|
34
|
-
TETRAHEDRON = "tetrahedron"
|
|
35
|
-
TROLL = "troll"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _circle_table(n: int) -> np.ndarray:
|
|
39
|
-
"""
|
|
40
|
-
Generates a table of sine and cosine values for a circle divided into n segments.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
n: The number of segments to divide the circle into.
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
A numpy array of shape (n+1, 2) containing the cosine and sine values.
|
|
47
|
-
"""
|
|
48
|
-
# Determine the angle between samples
|
|
49
|
-
angle = 2.0 * np.pi / (n if n != 0 else 1)
|
|
50
|
-
|
|
51
|
-
# Allocate list for n samples, plus duplicate of first entry at the end
|
|
52
|
-
cs = np.zeros((n + 1, 2), dtype=np.float32)
|
|
53
|
-
|
|
54
|
-
# Compute cos and sin around the circle
|
|
55
|
-
cs[0, 0] = 1.0 # cost
|
|
56
|
-
cs[0, 1] = 0.0 # sint
|
|
57
|
-
|
|
58
|
-
for i in range(1, n):
|
|
59
|
-
cs[i, 1] = np.sin(angle * i) # sint
|
|
60
|
-
cs[i, 0] = np.cos(angle * i) # cost
|
|
61
|
-
|
|
62
|
-
# Last sample is duplicate of the first
|
|
63
|
-
cs[n, 1] = cs[0, 1] # sint
|
|
64
|
-
cs[n, 0] = cs[0, 0] # cost
|
|
65
|
-
|
|
66
|
-
return cs
|
|
67
|
-
|
|
68
|
-
|
|
69
21
|
class _primitive:
|
|
70
22
|
"""A private class to hold VAO data for a primitive."""
|
|
71
23
|
|
|
@@ -82,12 +34,8 @@ class _primitive:
|
|
|
82
34
|
self.vao.set_data(data)
|
|
83
35
|
vert_data_size = 8 * 4 # 4 is sizeof float and 8 is x,y,z,nx,ny,nz,uv
|
|
84
36
|
self.vao.set_vertex_attribute_pointer(0, 3, gl.GL_FLOAT, vert_data_size, 0)
|
|
85
|
-
self.vao.set_vertex_attribute_pointer(
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
self.vao.set_vertex_attribute_pointer(
|
|
89
|
-
2, 2, gl.GL_FLOAT, vert_data_size, 2 * Vec3.sizeof()
|
|
90
|
-
)
|
|
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())
|
|
91
39
|
self.vao.set_num_indices(prim_data.size // 8)
|
|
92
40
|
|
|
93
41
|
|
|
@@ -100,120 +48,60 @@ class Primitives:
|
|
|
100
48
|
_loaded: bool = False
|
|
101
49
|
|
|
102
50
|
@classmethod
|
|
103
|
-
def
|
|
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:
|
|
51
|
+
def create(cls, type: str, name: str, *args: object, **kwargs: object) -> None:
|
|
119
52
|
"""
|
|
120
|
-
Creates a
|
|
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).
|
|
121
61
|
|
|
122
62
|
Args:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
data.append([v2, 0.0, -ds2])
|
|
150
|
-
|
|
151
|
-
# Now change our step value
|
|
152
|
-
v1 += wstep
|
|
153
|
-
v2 += dstep
|
|
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}")
|
|
154
89
|
|
|
155
|
-
|
|
156
|
-
data_array = np.array(data, dtype=np.float32)
|
|
157
|
-
prim = _primitive(data_array)
|
|
158
|
-
cls._primitives[name] = prim
|
|
90
|
+
cls._primitives[name] = _primitive(method(*args, **kwargs))
|
|
159
91
|
|
|
160
92
|
@classmethod
|
|
161
|
-
def
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
v_n: The normal vector for the plane.
|
|
174
|
-
"""
|
|
175
|
-
w2 = width / 2.0
|
|
176
|
-
d2 = depth / 2.0
|
|
177
|
-
w_step = width / w_p
|
|
178
|
-
d_step = depth / d_p
|
|
179
|
-
|
|
180
|
-
du = 0.9 / w_p
|
|
181
|
-
dv = 0.9 / d_p
|
|
182
|
-
|
|
183
|
-
data = []
|
|
184
|
-
v = 0.0
|
|
185
|
-
d = -d2
|
|
186
|
-
for _ in range(d_p):
|
|
187
|
-
u = 0.0
|
|
188
|
-
w = -w2
|
|
189
|
-
for _ in range(w_p):
|
|
190
|
-
# tri 1
|
|
191
|
-
# vert 1
|
|
192
|
-
data.extend([w, 0.0, d + d_step, v_n.x, v_n.y, v_n.z, u, v + dv])
|
|
193
|
-
# 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
|
-
)
|
|
197
|
-
# vert 3
|
|
198
|
-
data.extend([w, 0.0, d, v_n.x, v_n.y, v_n.z, u, v])
|
|
199
|
-
|
|
200
|
-
# tri 2
|
|
201
|
-
# 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
|
-
)
|
|
205
|
-
# vert 2
|
|
206
|
-
data.extend([w + w_step, 0.0, d, v_n.x, v_n.y, v_n.z, u + du, v])
|
|
207
|
-
# vert 3
|
|
208
|
-
data.extend([w, 0.0, d, v_n.x, v_n.y, v_n.z, u, v])
|
|
209
|
-
u += du
|
|
210
|
-
w += w_step
|
|
211
|
-
v += dv
|
|
212
|
-
d += d_step
|
|
213
|
-
|
|
214
|
-
data_array = np.array(data, dtype=np.float32)
|
|
215
|
-
prim = _primitive(data_array)
|
|
216
|
-
cls._primitives[name] = prim
|
|
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
|
|
217
105
|
|
|
218
106
|
@classmethod
|
|
219
107
|
def draw(cls, name: Union[str, Prims]) -> None:
|
|
@@ -231,476 +119,3 @@ class Primitives:
|
|
|
231
119
|
except KeyError:
|
|
232
120
|
logger.error(f"Failed to draw primitive {key}")
|
|
233
121
|
return
|
|
234
|
-
|
|
235
|
-
@classmethod
|
|
236
|
-
def create_sphere(cls, name: str, radius: float, precision: int) -> None:
|
|
237
|
-
"""
|
|
238
|
-
Creates a sphere primitive.
|
|
239
|
-
|
|
240
|
-
Args:
|
|
241
|
-
name: The name of the primitive.
|
|
242
|
-
radius: The radius of the sphere.
|
|
243
|
-
precision: The precision of the sphere (number of slices).
|
|
244
|
-
"""
|
|
245
|
-
# Sphere code based on a function Written by Paul Bourke.
|
|
246
|
-
# http://astronomy.swin.edu.au/~pbourke/opengl/sphere/
|
|
247
|
-
# the next part of the code calculates the P,N,UV of the sphere for triangles
|
|
248
|
-
|
|
249
|
-
# Disallow a negative number for radius.
|
|
250
|
-
if radius < 0.0:
|
|
251
|
-
radius = -radius
|
|
252
|
-
|
|
253
|
-
# Disallow a negative number for precision.
|
|
254
|
-
if precision < 4:
|
|
255
|
-
precision = 4
|
|
256
|
-
|
|
257
|
-
# Create a numpy array to store our verts
|
|
258
|
-
data = []
|
|
259
|
-
|
|
260
|
-
for i in range(precision // 2):
|
|
261
|
-
theta1 = i * 2.0 * np.pi / precision - np.pi / 2.0
|
|
262
|
-
theta2 = (i + 1) * 2.0 * np.pi / precision - np.pi / 2.0
|
|
263
|
-
|
|
264
|
-
for j in range(precision):
|
|
265
|
-
theta3 = j * 2.0 * np.pi / precision
|
|
266
|
-
theta4 = (j + 1) * 2.0 * np.pi / precision
|
|
267
|
-
|
|
268
|
-
# First triangle
|
|
269
|
-
nx1 = np.cos(theta2) * np.cos(theta3)
|
|
270
|
-
ny1 = np.sin(theta2)
|
|
271
|
-
nz1 = np.cos(theta2) * np.sin(theta3)
|
|
272
|
-
x1 = radius * nx1
|
|
273
|
-
y1 = radius * ny1
|
|
274
|
-
z1 = radius * nz1
|
|
275
|
-
u1 = j / precision
|
|
276
|
-
v1 = 2.0 * (i + 1) / precision
|
|
277
|
-
data.append([x1, y1, z1, nx1, ny1, nz1, u1, v1])
|
|
278
|
-
|
|
279
|
-
nx2 = np.cos(theta1) * np.cos(theta3)
|
|
280
|
-
ny2 = np.sin(theta1)
|
|
281
|
-
nz2 = np.cos(theta1) * np.sin(theta3)
|
|
282
|
-
x2 = radius * nx2
|
|
283
|
-
y2 = radius * ny2
|
|
284
|
-
z2 = radius * nz2
|
|
285
|
-
u2 = j / precision
|
|
286
|
-
v2 = 2.0 * i / precision
|
|
287
|
-
data.append([x2, y2, z2, nx2, ny2, nz2, u2, v2])
|
|
288
|
-
|
|
289
|
-
nx3 = np.cos(theta1) * np.cos(theta4)
|
|
290
|
-
ny3 = np.sin(theta1)
|
|
291
|
-
nz3 = np.cos(theta1) * np.sin(theta4)
|
|
292
|
-
x3 = radius * nx3
|
|
293
|
-
y3 = radius * ny3
|
|
294
|
-
z3 = radius * nz3
|
|
295
|
-
u3 = (j + 1) / precision
|
|
296
|
-
v3 = 2.0 * i / precision
|
|
297
|
-
data.append([x3, y3, z3, nx3, ny3, nz3, u3, v3])
|
|
298
|
-
|
|
299
|
-
# Second triangle
|
|
300
|
-
nx4 = np.cos(theta2) * np.cos(theta4)
|
|
301
|
-
ny4 = np.sin(theta2)
|
|
302
|
-
nz4 = np.cos(theta2) * np.sin(theta4)
|
|
303
|
-
x4 = radius * nx4
|
|
304
|
-
y4 = radius * ny4
|
|
305
|
-
z4 = radius * nz4
|
|
306
|
-
u4 = (j + 1) / precision
|
|
307
|
-
v4 = 2.0 * (i + 1) / precision
|
|
308
|
-
data.append([x4, y4, z4, nx4, ny4, nz4, u4, v4])
|
|
309
|
-
|
|
310
|
-
data.append([x1, y1, z1, nx1, ny1, nz1, u1, v1])
|
|
311
|
-
data.append([x3, y3, z3, nx3, ny3, nz3, u3, v3])
|
|
312
|
-
|
|
313
|
-
data_array = np.array(data, dtype=np.float32)
|
|
314
|
-
prim = _primitive(data_array)
|
|
315
|
-
cls._primitives[name] = prim
|
|
316
|
-
|
|
317
|
-
@classmethod
|
|
318
|
-
def create_cone(
|
|
319
|
-
cls, name: str, base: float, height: float, slices: int, stacks: int
|
|
320
|
-
) -> None:
|
|
321
|
-
"""
|
|
322
|
-
Creates a cone primitive.
|
|
323
|
-
|
|
324
|
-
Args:
|
|
325
|
-
name: The name of the primitive.
|
|
326
|
-
base: The radius of the cone's base.
|
|
327
|
-
height: The height of the cone.
|
|
328
|
-
slices: The number of divisions around the cone.
|
|
329
|
-
stacks: The number of divisions along the cone's height.
|
|
330
|
-
"""
|
|
331
|
-
z_step = height / (stacks if stacks > 0 else 1)
|
|
332
|
-
r_step = base / (stacks if stacks > 0 else 1)
|
|
333
|
-
|
|
334
|
-
cosn = height / np.sqrt(height * height + base * base)
|
|
335
|
-
sinn = base / np.sqrt(height * height + base * base)
|
|
336
|
-
|
|
337
|
-
cs = _circle_table(slices)
|
|
338
|
-
|
|
339
|
-
z0 = 0.0
|
|
340
|
-
z1 = z_step
|
|
341
|
-
|
|
342
|
-
r0 = base
|
|
343
|
-
r1 = r0 - r_step
|
|
344
|
-
|
|
345
|
-
du = 1.0 / stacks
|
|
346
|
-
dv = 1.0 / slices
|
|
347
|
-
|
|
348
|
-
u = 1.0
|
|
349
|
-
v = 1.0
|
|
350
|
-
|
|
351
|
-
data = []
|
|
352
|
-
|
|
353
|
-
for _ in range(stacks):
|
|
354
|
-
for j in range(slices):
|
|
355
|
-
# First triangle
|
|
356
|
-
d1 = [0] * 8
|
|
357
|
-
d1[6] = u
|
|
358
|
-
d1[7] = v
|
|
359
|
-
d1[3] = cs[j, 0] * cosn # nx
|
|
360
|
-
d1[4] = cs[j, 1] * sinn # ny
|
|
361
|
-
d1[5] = sinn # nz
|
|
362
|
-
d1[0] = cs[j, 0] * r0 # x
|
|
363
|
-
d1[1] = cs[j, 1] * r0 # y
|
|
364
|
-
d1[2] = z0 # z
|
|
365
|
-
data.append(d1)
|
|
366
|
-
|
|
367
|
-
d2 = [0] * 8
|
|
368
|
-
d2[6] = u
|
|
369
|
-
d2[7] = v - dv
|
|
370
|
-
d2[3] = cs[j, 0] * cosn # nx
|
|
371
|
-
d2[4] = cs[j, 1] * sinn # ny
|
|
372
|
-
d2[5] = sinn # nz
|
|
373
|
-
d2[0] = cs[j, 0] * r1 # x
|
|
374
|
-
d2[1] = cs[j, 1] * r1 # y
|
|
375
|
-
d2[2] = z1 # z
|
|
376
|
-
data.append(d2)
|
|
377
|
-
|
|
378
|
-
d3 = [0] * 8
|
|
379
|
-
d3[6] = u - du
|
|
380
|
-
d3[7] = v - dv
|
|
381
|
-
d3[3] = cs[j + 1, 0] * cosn # nx
|
|
382
|
-
d3[4] = cs[j + 1, 1] * sinn # ny
|
|
383
|
-
d3[5] = sinn # nz
|
|
384
|
-
d3[0] = cs[j + 1, 0] * r1 # x
|
|
385
|
-
d3[1] = cs[j + 1, 1] * r1 # y
|
|
386
|
-
d3[2] = z1 # z
|
|
387
|
-
data.append(d3)
|
|
388
|
-
|
|
389
|
-
# Second triangle
|
|
390
|
-
d4 = [0] * 8
|
|
391
|
-
d4[6] = u
|
|
392
|
-
d4[7] = v
|
|
393
|
-
d4[3] = cs[j, 0] * cosn # nx
|
|
394
|
-
d4[4] = cs[j, 1] * sinn # ny
|
|
395
|
-
d4[5] = sinn # nz
|
|
396
|
-
d4[0] = cs[j, 0] * r0 # x
|
|
397
|
-
d4[1] = cs[j, 1] * r0 # y
|
|
398
|
-
d4[2] = z0 # z
|
|
399
|
-
data.append(d4)
|
|
400
|
-
|
|
401
|
-
d5 = [0] * 8
|
|
402
|
-
d5[6] = u - du
|
|
403
|
-
d5[7] = v - dv
|
|
404
|
-
d5[3] = cs[j + 1, 0] * cosn # nx
|
|
405
|
-
d5[4] = cs[j + 1, 1] * sinn # ny
|
|
406
|
-
d5[5] = sinn # nz
|
|
407
|
-
d5[0] = cs[j + 1, 0] * r1 # x
|
|
408
|
-
d5[1] = cs[j + 1, 1] * r1 # y
|
|
409
|
-
d5[2] = z1 # z
|
|
410
|
-
data.append(d5)
|
|
411
|
-
|
|
412
|
-
d6 = [0] * 8
|
|
413
|
-
d6[6] = u - du
|
|
414
|
-
d6[7] = v
|
|
415
|
-
d6[3] = cs[j + 1, 0] * cosn # nx
|
|
416
|
-
d6[4] = cs[j + 1, 1] * sinn # ny
|
|
417
|
-
d6[5] = sinn # nz
|
|
418
|
-
d6[0] = cs[j + 1, 0] * r0 # x
|
|
419
|
-
d6[1] = cs[j + 1, 1] * r0 # y
|
|
420
|
-
d6[2] = z0 # z
|
|
421
|
-
data.append(d6)
|
|
422
|
-
|
|
423
|
-
u -= du
|
|
424
|
-
|
|
425
|
-
v -= dv
|
|
426
|
-
u = 1.0
|
|
427
|
-
z0 = z1
|
|
428
|
-
z1 += z_step
|
|
429
|
-
r0 = r1
|
|
430
|
-
r1 -= r_step
|
|
431
|
-
|
|
432
|
-
data_array = np.array(data, dtype=np.float32)
|
|
433
|
-
prim = _primitive(data_array)
|
|
434
|
-
cls._primitives[name] = prim
|
|
435
|
-
|
|
436
|
-
@classmethod
|
|
437
|
-
def create_capsule(
|
|
438
|
-
cls, name: str, radius: float, height: float, precision: int
|
|
439
|
-
) -> None:
|
|
440
|
-
"""
|
|
441
|
-
Creates a capsule primitive.
|
|
442
|
-
The capsule is aligned along the y-axis.
|
|
443
|
-
It is composed of a cylinder and two hemispherical caps.
|
|
444
|
-
based on code from here https://code.google.com/p/rgine/source/browse/trunk/RGine/opengl/src/RGLShapes.cpp
|
|
445
|
-
and adapted
|
|
446
|
-
"""
|
|
447
|
-
if radius <= 0.0:
|
|
448
|
-
raise ValueError("Radius must be positive")
|
|
449
|
-
if height < 0.0:
|
|
450
|
-
raise ValueError("Height must be non-negative")
|
|
451
|
-
if precision < 4:
|
|
452
|
-
precision = 4
|
|
453
|
-
|
|
454
|
-
data = []
|
|
455
|
-
h = height / 2.0
|
|
456
|
-
ang = np.pi / precision
|
|
457
|
-
|
|
458
|
-
# Cylinder sides
|
|
459
|
-
for i in range(2 * precision):
|
|
460
|
-
c = radius * np.cos(ang * i)
|
|
461
|
-
c1 = radius * np.cos(ang * (i + 1))
|
|
462
|
-
s = radius * np.sin(ang * i)
|
|
463
|
-
s1 = radius * np.sin(ang * (i + 1))
|
|
464
|
-
|
|
465
|
-
# normals for cylinder sides
|
|
466
|
-
nc = np.cos(ang * i)
|
|
467
|
-
ns = np.sin(ang * i)
|
|
468
|
-
nc1 = np.cos(ang * (i + 1))
|
|
469
|
-
ns1 = np.sin(ang * (i + 1))
|
|
470
|
-
|
|
471
|
-
# side top
|
|
472
|
-
data.extend([c1, h, s1, nc1, 0.0, ns1, 0.0, 0.0])
|
|
473
|
-
data.extend([c, h, s, nc, 0.0, ns, 0.0, 0.0])
|
|
474
|
-
data.extend([c, -h, s, nc, 0.0, ns, 0.0, 0.0])
|
|
475
|
-
|
|
476
|
-
# side bot
|
|
477
|
-
data.extend([c, -h, s, nc, 0.0, ns, 0.0, 0.0])
|
|
478
|
-
data.extend([c1, -h, s1, nc1, 0.0, ns1, 0.0, 0.0])
|
|
479
|
-
data.extend([c1, h, s1, nc1, 0.0, ns1, 0.0, 0.0])
|
|
480
|
-
# Hemispherical caps
|
|
481
|
-
for i in range(2 * precision):
|
|
482
|
-
# longitude
|
|
483
|
-
s = -np.sin(ang * i)
|
|
484
|
-
s1 = -np.sin(ang * (i + 1))
|
|
485
|
-
c = np.cos(ang * i)
|
|
486
|
-
c1 = np.cos(ang * (i + 1))
|
|
487
|
-
|
|
488
|
-
for j in range(precision + 1):
|
|
489
|
-
o = h if j < precision / 2 else -h
|
|
490
|
-
|
|
491
|
-
# latitude
|
|
492
|
-
sb = radius * np.sin(ang * j)
|
|
493
|
-
sb1 = radius * np.sin(ang * (j + 1))
|
|
494
|
-
cb = radius * np.cos(ang * j)
|
|
495
|
-
cb1 = radius * np.cos(ang * (j + 1))
|
|
496
|
-
|
|
497
|
-
if j != precision - 1:
|
|
498
|
-
nx, ny, nz = sb * c, cb, sb * s
|
|
499
|
-
data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
|
|
500
|
-
nx, ny, nz = sb1 * c, cb1, sb1 * s
|
|
501
|
-
data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
|
|
502
|
-
nx, ny, nz = sb1 * c1, cb1, sb1 * s1
|
|
503
|
-
data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
|
|
504
|
-
|
|
505
|
-
if j != 0:
|
|
506
|
-
nx, ny, nz = sb * c, cb, sb * s
|
|
507
|
-
data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
|
|
508
|
-
nx, ny, nz = sb1 * c1, cb1, sb1 * s1
|
|
509
|
-
data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
|
|
510
|
-
nx, ny, nz = sb * c1, cb, sb * s1
|
|
511
|
-
data.extend([nx, ny + o, nz, nx, ny, nz, 0.0, 0.0])
|
|
512
|
-
|
|
513
|
-
data_array = np.array(data, dtype=np.float32)
|
|
514
|
-
prim = _primitive(data_array)
|
|
515
|
-
cls._primitives[name] = prim
|
|
516
|
-
|
|
517
|
-
@classmethod
|
|
518
|
-
def create_cylinder(
|
|
519
|
-
cls, name: str, radius: float, height: float, slices: int, stacks: int
|
|
520
|
-
) -> None:
|
|
521
|
-
"""
|
|
522
|
-
Creates a cylinder primitive.
|
|
523
|
-
The cylinder is aligned along the y-axis.
|
|
524
|
-
This method generates the cylinder walls, but not the top and bottom caps.
|
|
525
|
-
"""
|
|
526
|
-
if radius <= 0.0:
|
|
527
|
-
raise ValueError("Radius must be positive")
|
|
528
|
-
if height < 0.0:
|
|
529
|
-
raise ValueError("Height must be non-negative")
|
|
530
|
-
if slices < 3:
|
|
531
|
-
slices = 3
|
|
532
|
-
if stacks < 1:
|
|
533
|
-
stacks = 1
|
|
534
|
-
|
|
535
|
-
data = []
|
|
536
|
-
h2 = height / 2.0
|
|
537
|
-
y_step = height / stacks
|
|
538
|
-
|
|
539
|
-
cs = _circle_table(slices)
|
|
540
|
-
|
|
541
|
-
du = 1.0 / slices
|
|
542
|
-
dv = 1.0 / stacks
|
|
543
|
-
|
|
544
|
-
for i in range(stacks):
|
|
545
|
-
y0 = -h2 + i * y_step
|
|
546
|
-
y1 = -h2 + (i + 1) * y_step
|
|
547
|
-
v = i * dv
|
|
548
|
-
for j in range(slices):
|
|
549
|
-
u = j * du
|
|
550
|
-
|
|
551
|
-
nx1, nz1 = cs[j, 0], cs[j, 1]
|
|
552
|
-
x1, z1 = radius * nx1, radius * nz1
|
|
553
|
-
|
|
554
|
-
nx2, nz2 = cs[j + 1, 0], cs[j + 1, 1]
|
|
555
|
-
x2, z2 = radius * nx2, radius * nz2
|
|
556
|
-
|
|
557
|
-
p_bl = [x1, y0, z1, nx1, 0, nz1, u, v]
|
|
558
|
-
p_br = [x2, y0, z2, nx2, 0, nz2, u + du, v]
|
|
559
|
-
p_tl = [x1, y1, z1, nx1, 0, nz1, u, v + dv]
|
|
560
|
-
p_tr = [x2, y1, z2, nx2, 0, nz2, u + du, v + dv]
|
|
561
|
-
|
|
562
|
-
# Triangle 1
|
|
563
|
-
data.extend(p_bl)
|
|
564
|
-
data.extend(p_tl)
|
|
565
|
-
data.extend(p_br)
|
|
566
|
-
# Triangle 2
|
|
567
|
-
data.extend(p_br)
|
|
568
|
-
data.extend(p_tl)
|
|
569
|
-
data.extend(p_tr)
|
|
570
|
-
|
|
571
|
-
data_array = np.array(data, dtype=np.float32)
|
|
572
|
-
prim = _primitive(data_array)
|
|
573
|
-
cls._primitives[name] = prim
|
|
574
|
-
|
|
575
|
-
@classmethod
|
|
576
|
-
def create_disk(cls, name: str, radius: float, slices: int) -> None:
|
|
577
|
-
"""
|
|
578
|
-
Creates a disk primitive.
|
|
579
|
-
|
|
580
|
-
Args:
|
|
581
|
-
name: The name of the primitive.
|
|
582
|
-
radius: The radius of the disk.
|
|
583
|
-
slices: The number of slices to divide the disk into.
|
|
584
|
-
"""
|
|
585
|
-
if radius <= 0.0:
|
|
586
|
-
raise ValueError("Radius must be positive")
|
|
587
|
-
if slices < 3:
|
|
588
|
-
slices = 3
|
|
589
|
-
|
|
590
|
-
data = []
|
|
591
|
-
cs = _circle_table(slices)
|
|
592
|
-
|
|
593
|
-
center = [0, 0, 0, 0, 1, 0, 0.5, 0.5]
|
|
594
|
-
|
|
595
|
-
for i in range(slices):
|
|
596
|
-
p1 = [
|
|
597
|
-
radius * cs[i, 0],
|
|
598
|
-
0,
|
|
599
|
-
radius * cs[i, 1],
|
|
600
|
-
0,
|
|
601
|
-
1,
|
|
602
|
-
0,
|
|
603
|
-
cs[i, 0] * 0.5 + 0.5,
|
|
604
|
-
cs[i, 1] * 0.5 + 0.5,
|
|
605
|
-
]
|
|
606
|
-
p2 = [
|
|
607
|
-
radius * cs[i + 1, 0],
|
|
608
|
-
0,
|
|
609
|
-
radius * cs[i + 1, 1],
|
|
610
|
-
0,
|
|
611
|
-
1,
|
|
612
|
-
0,
|
|
613
|
-
cs[i + 1, 0] * 0.5 + 0.5,
|
|
614
|
-
cs[i + 1, 1] * 0.5 + 0.5,
|
|
615
|
-
]
|
|
616
|
-
|
|
617
|
-
data.extend(center)
|
|
618
|
-
data.extend(p2)
|
|
619
|
-
data.extend(p1)
|
|
620
|
-
|
|
621
|
-
data_array = np.array(data, dtype=np.float32)
|
|
622
|
-
prim = _primitive(data_array)
|
|
623
|
-
cls._primitives[name] = prim
|
|
624
|
-
|
|
625
|
-
@classmethod
|
|
626
|
-
def create_torus(
|
|
627
|
-
cls,
|
|
628
|
-
name: str,
|
|
629
|
-
minor_radius: float,
|
|
630
|
-
major_radius: float,
|
|
631
|
-
sides: int,
|
|
632
|
-
rings: int,
|
|
633
|
-
) -> None:
|
|
634
|
-
"""
|
|
635
|
-
Creates a torus primitive.
|
|
636
|
-
|
|
637
|
-
Args:
|
|
638
|
-
name: The name of the primitive.
|
|
639
|
-
minor_radius: The minor radius of the torus.
|
|
640
|
-
major_radius: The major radius of the torus.
|
|
641
|
-
sides: The number of sides for each ring.
|
|
642
|
-
rings: The number of rings for the torus.
|
|
643
|
-
"""
|
|
644
|
-
if minor_radius <= 0 or major_radius <= 0:
|
|
645
|
-
raise ValueError("Radii must be positive")
|
|
646
|
-
if sides < 3 or rings < 3:
|
|
647
|
-
raise ValueError("Sides and rings must be at least 3")
|
|
648
|
-
|
|
649
|
-
d_psi = 2.0 * np.pi / rings
|
|
650
|
-
d_phi = -2.0 * np.pi / sides
|
|
651
|
-
|
|
652
|
-
psi = 0.0
|
|
653
|
-
|
|
654
|
-
vertices = []
|
|
655
|
-
normals = []
|
|
656
|
-
uvs = []
|
|
657
|
-
|
|
658
|
-
for j in range(rings + 1):
|
|
659
|
-
c_psi = np.cos(psi)
|
|
660
|
-
s_psi = np.sin(psi)
|
|
661
|
-
phi = 0.0
|
|
662
|
-
for i in range(sides + 1):
|
|
663
|
-
c_phi = np.cos(phi)
|
|
664
|
-
s_phi = np.sin(phi)
|
|
665
|
-
|
|
666
|
-
x = c_psi * (major_radius + c_phi * minor_radius)
|
|
667
|
-
z = s_psi * (major_radius + c_phi * minor_radius)
|
|
668
|
-
y = s_phi * minor_radius
|
|
669
|
-
vertices.append([x, y, z])
|
|
670
|
-
|
|
671
|
-
nx = c_psi * c_phi
|
|
672
|
-
nz = s_psi * c_phi
|
|
673
|
-
ny = s_phi
|
|
674
|
-
normals.append([nx, ny, nz])
|
|
675
|
-
|
|
676
|
-
u = i / sides
|
|
677
|
-
v = j / rings
|
|
678
|
-
uvs.append([u, v])
|
|
679
|
-
|
|
680
|
-
phi += d_phi
|
|
681
|
-
psi += d_psi
|
|
682
|
-
|
|
683
|
-
data = []
|
|
684
|
-
for j in range(rings):
|
|
685
|
-
for i in range(sides):
|
|
686
|
-
idx1 = j * (sides + 1) + i
|
|
687
|
-
idx2 = j * (sides + 1) + (i + 1)
|
|
688
|
-
idx3 = (j + 1) * (sides + 1) + i
|
|
689
|
-
idx4 = (j + 1) * (sides + 1) + (i + 1)
|
|
690
|
-
|
|
691
|
-
p1 = vertices[idx1] + normals[idx1] + uvs[idx1]
|
|
692
|
-
p2 = vertices[idx2] + normals[idx2] + uvs[idx2]
|
|
693
|
-
p3 = vertices[idx3] + normals[idx3] + uvs[idx3]
|
|
694
|
-
p4 = vertices[idx4] + normals[idx4] + uvs[idx4]
|
|
695
|
-
|
|
696
|
-
data.extend(p1)
|
|
697
|
-
data.extend(p3)
|
|
698
|
-
data.extend(p2)
|
|
699
|
-
|
|
700
|
-
data.extend(p2)
|
|
701
|
-
data.extend(p3)
|
|
702
|
-
data.extend(p4)
|
|
703
|
-
|
|
704
|
-
data_array = np.array(data, dtype=np.float32)
|
|
705
|
-
prim = _primitive(data_array)
|
|
706
|
-
cls._primitives[name] = prim
|