basilisk-engine 0.1.0__py3-none-any.whl → 0.1.1__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.
Potentially problematic release.
This version of basilisk-engine might be problematic. Click here for more details.
- basilisk/collisions/broad/broad_aabb.py +8 -1
- basilisk/collisions/broad/broad_bvh.py +8 -1
- basilisk/collisions/collider.py +15 -6
- basilisk/collisions/collider_handler.py +70 -68
- basilisk/collisions/narrow/contact_manifold.py +9 -10
- basilisk/collisions/narrow/dataclasses.py +27 -0
- basilisk/collisions/narrow/deprecated.py +47 -0
- basilisk/collisions/narrow/epa.py +11 -10
- basilisk/collisions/narrow/gjk.py +15 -14
- basilisk/collisions/narrow/helper.py +8 -7
- basilisk/collisions/narrow/sutherland_hodgman.py +52 -0
- basilisk/draw/draw_handler.py +5 -3
- basilisk/engine.py +14 -5
- basilisk/generic/abstract_custom.py +134 -0
- basilisk/generic/collisions.py +46 -0
- basilisk/generic/quat.py +77 -66
- basilisk/generic/vec3.py +91 -67
- basilisk/mesh/cube.py +20 -2
- basilisk/mesh/mesh.py +69 -54
- basilisk/mesh/mesh_from_data.py +106 -21
- basilisk/mesh/narrow_aabb.py +10 -1
- basilisk/mesh/narrow_bvh.py +9 -1
- basilisk/nodes/node.py +169 -30
- basilisk/nodes/node_handler.py +51 -30
- basilisk/particles/__init__.py +0 -0
- basilisk/particles/particle_handler.py +55 -0
- basilisk/particles/particle_renderer.py +87 -0
- basilisk/physics/impulse.py +7 -13
- basilisk/physics/physics_body.py +10 -2
- basilisk/physics/physics_engine.py +1 -2
- basilisk/render/batch.py +2 -2
- basilisk/render/camera.py +5 -0
- basilisk/render/chunk.py +19 -4
- basilisk/render/chunk_handler.py +33 -26
- basilisk/render/image.py +1 -1
- basilisk/render/image_handler.py +4 -3
- basilisk/render/light_handler.py +16 -11
- basilisk/render/material.py +25 -1
- basilisk/render/material_handler.py +22 -13
- basilisk/render/shader.py +7 -7
- basilisk/render/shader_handler.py +13 -12
- basilisk/render/sky.py +5 -3
- basilisk/scene.py +114 -32
- basilisk/shaders/batch.frag +40 -11
- basilisk/shaders/batch.vert +14 -7
- basilisk/shaders/normal.frag +5 -5
- basilisk/shaders/normal.vert +8 -3
- basilisk/shaders/particle.frag +72 -0
- basilisk/shaders/particle.vert +85 -0
- {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.1.dist-info}/METADATA +5 -5
- basilisk_engine-0.1.1.dist-info/RECORD +95 -0
- basilisk/shaders/image.png +0 -0
- basilisk_engine-0.1.0.dist-info/RECORD +0 -88
- {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.1.dist-info}/WHEEL +0 -0
- {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.1.dist-info}/top_level.txt +0 -0
basilisk/generic/vec3.py
CHANGED
|
@@ -1,87 +1,90 @@
|
|
|
1
1
|
import glm
|
|
2
2
|
import numpy as np
|
|
3
|
+
from .abstract_custom import Custom
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
class Vec3():
|
|
5
|
+
class Vec3(Custom):
|
|
6
6
|
def __init__(self, *args, callback=None):
|
|
7
7
|
self.callback = callback
|
|
8
8
|
self.prev_data = glm.vec3(0.0)
|
|
9
|
-
self.set_data(*args)
|
|
10
|
-
|
|
11
|
-
def normalize(self):
|
|
12
|
-
"""
|
|
13
|
-
Inplace normalizes the vector
|
|
14
|
-
"""
|
|
15
|
-
self.data = glm.normalize(self.data)
|
|
16
9
|
|
|
17
|
-
def set_data(self, *args):
|
|
18
|
-
"""
|
|
19
|
-
Sets the internal vector inplace
|
|
20
|
-
"""
|
|
21
|
-
# overload constructor TODO nvernest this, definitely possible
|
|
22
10
|
if len(args) == 1:
|
|
11
|
+
|
|
23
12
|
if isinstance(args[0], Vec3):
|
|
24
13
|
self.data = glm.vec3(args[0].data)
|
|
14
|
+
self.prev_data = glm.vec3(args[0].prev_data)
|
|
25
15
|
self.callback = args[0].callback
|
|
26
|
-
|
|
16
|
+
|
|
17
|
+
elif isinstance(args[0], glm.vec3):
|
|
18
|
+
self.data = args[0]
|
|
19
|
+
|
|
27
20
|
elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
|
|
28
|
-
|
|
21
|
+
assert len(args[0]) == 3, f'Vec3: Expected 3 values from incoming vector, got {len(args[0])}'
|
|
29
22
|
self.data = glm.vec3(args[0])
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
|
|
24
|
+
else:
|
|
25
|
+
try: self.data = glm.vec3(args[0])
|
|
26
|
+
except: raise ValueError(f'Vec3: Unexpected incoming vector type {args[0]}')
|
|
27
|
+
|
|
28
|
+
elif len(args) == 3: self.data = glm.vec3(*args)
|
|
32
29
|
else: raise ValueError(f'Vec3: Expected either 1 vector or 3 numbers, got {len(args)} values')
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
elif isinstance(other, Vec3): self.data += other.data
|
|
41
|
-
else: raise ValueError(f'Vec3: Not an accepted type for addition, got {type(other)}')
|
|
42
|
-
return self
|
|
43
|
-
|
|
44
|
-
def __isub__(self, other):
|
|
45
|
-
if isinstance(other, glm.vec3): self.data -= other
|
|
30
|
+
|
|
31
|
+
def apply_function(self, other, func, func_name):
|
|
32
|
+
vec = glm.vec3(self.data)
|
|
33
|
+
|
|
34
|
+
if isinstance(other, (glm.vec3, glm.quat)):
|
|
35
|
+
vec = func(vec, other)
|
|
36
|
+
|
|
46
37
|
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
self.data
|
|
61
|
-
return self
|
|
38
|
+
assert len(other) == 3, f'Vec3: Number of added values must be 3, got {len(other)}'
|
|
39
|
+
vec = func(vec, other)
|
|
40
|
+
|
|
41
|
+
elif isinstance(other, Custom): # perserve self.callback over other.callback. this should never be done by the user
|
|
42
|
+
vec = func(vec, other.data)
|
|
43
|
+
|
|
44
|
+
else:
|
|
45
|
+
try: vec = func(vec, other)
|
|
46
|
+
except: raise ValueError(f'Vec3: Not an accepted type for {func_name}, got {type(other)}')
|
|
47
|
+
return Vec3(vec)
|
|
48
|
+
|
|
49
|
+
# unary operators
|
|
50
|
+
def __neg__(self):
|
|
51
|
+
return Vec3(-self.data, callback=self.callback)
|
|
62
52
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
self.data //= other
|
|
66
|
-
return self
|
|
53
|
+
def __abs__(self):
|
|
54
|
+
return Vec3(abs(self.data), callback=self.callback)
|
|
67
55
|
|
|
68
|
-
#
|
|
56
|
+
# accessor functions
|
|
69
57
|
def __getitem__(self, index):
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
assert int(index) == index, f'Vec3: index must be an int, got {type(index)}' # check if index is a float
|
|
59
|
+
assert 0 <= index <= 2, f'Vec3: index out of bounds, got {index}'
|
|
72
60
|
return self.data[index]
|
|
73
61
|
|
|
74
62
|
def __setitem__(self, index, value):
|
|
75
|
-
|
|
76
|
-
|
|
63
|
+
assert int(index) == index, f'Vec3: index must be an int, got {type(index)}' # check if index is a float
|
|
64
|
+
assert 0 <= index <= 2, f'Vec3: index out of bounds, got {index}'
|
|
77
65
|
try: self.data[index] = value
|
|
78
66
|
except: raise ValueError(f'Vec3: Invalid element type, got {type(value)}')
|
|
79
67
|
|
|
80
|
-
def
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
def __delitem__(self, index): # index in a vec cannot be deleted, so we default to zero
|
|
69
|
+
assert int(index) == index, f'Vec3: index must be an int, got {type(index)}' # check if index is a float
|
|
70
|
+
assert 0 <= index <= 2, f'Vec3: index out of bounds, got {index}'
|
|
71
|
+
self.data[index] = 0
|
|
72
|
+
|
|
73
|
+
def __len__(self):
|
|
74
|
+
return 3
|
|
75
|
+
|
|
83
76
|
def __iter__(self):
|
|
84
|
-
return iter(
|
|
77
|
+
return iter(self.data)
|
|
78
|
+
|
|
79
|
+
def __contains__(self, item):
|
|
80
|
+
return item in self.data
|
|
81
|
+
|
|
82
|
+
# override str operators
|
|
83
|
+
def __repr__(self):
|
|
84
|
+
return 'bsk ' + str(self.data)
|
|
85
|
+
|
|
86
|
+
def __str__(self):
|
|
87
|
+
return 'bsk ' + str(self.data)
|
|
85
88
|
|
|
86
89
|
@property
|
|
87
90
|
def data(self): return self._data
|
|
@@ -94,8 +97,7 @@ class Vec3():
|
|
|
94
97
|
|
|
95
98
|
@data.setter
|
|
96
99
|
def data(self, value: glm.vec3):
|
|
97
|
-
self._data = value
|
|
98
|
-
|
|
100
|
+
self._data = glm.vec3(value)
|
|
99
101
|
cur = self._data
|
|
100
102
|
prev = self.prev_data
|
|
101
103
|
thresh = 1e-6
|
|
@@ -106,15 +108,37 @@ class Vec3():
|
|
|
106
108
|
|
|
107
109
|
@x.setter
|
|
108
110
|
def x(self, value):
|
|
109
|
-
self.
|
|
110
|
-
if self.callback
|
|
111
|
+
self._data.x = value
|
|
112
|
+
if self.callback and abs(value - self.prev_data.x) > 1e-6:
|
|
113
|
+
self.prev_data.x = value
|
|
114
|
+
self.callback()
|
|
111
115
|
|
|
112
116
|
@y.setter
|
|
113
117
|
def y(self, value):
|
|
114
|
-
self.
|
|
115
|
-
if self.callback
|
|
118
|
+
self._data.y = value
|
|
119
|
+
if self.callback and abs(value - self.prev_data.y) > 1e-6:
|
|
120
|
+
self.prev_data.y = value
|
|
121
|
+
self.callback()
|
|
116
122
|
|
|
117
123
|
@z.setter
|
|
118
124
|
def z(self, value):
|
|
119
|
-
self.
|
|
120
|
-
if self.callback
|
|
125
|
+
self._data.z = value
|
|
126
|
+
if self.callback and abs(value - self.prev_data.z) > 1e-6:
|
|
127
|
+
self.prev_data.z = value
|
|
128
|
+
self.callback()
|
|
129
|
+
|
|
130
|
+
class Node():
|
|
131
|
+
|
|
132
|
+
def __init__(self, pos):
|
|
133
|
+
|
|
134
|
+
def callback(): print('calling back')
|
|
135
|
+
self.pos_callback = callback
|
|
136
|
+
self._pos = Vec3(pos, callback=self.pos_callback)
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def pos(self): return self._pos
|
|
140
|
+
|
|
141
|
+
@pos.setter
|
|
142
|
+
def pos(self, value):
|
|
143
|
+
if isinstance(value, Vec3): self._pos.data = value.data
|
|
144
|
+
else: self._pos.data = value
|
basilisk/mesh/cube.py
CHANGED
|
@@ -8,9 +8,27 @@ class Cube(Mesh):
|
|
|
8
8
|
# built-in cube mesh with custom functions
|
|
9
9
|
path = engine.root + '/bsk_assets/cube.obj'
|
|
10
10
|
super().__init__(path)
|
|
11
|
+
|
|
12
|
+
self.dot_indices = [0 for _ in range(8)]
|
|
13
|
+
for i, point in enumerate(self.points):
|
|
14
|
+
index = 0
|
|
15
|
+
if point[0] > 0: index += 4
|
|
16
|
+
if point[1] > 0: index += 2
|
|
17
|
+
if point[2] > 0: index += 1
|
|
18
|
+
self.dot_indices[index] = i
|
|
11
19
|
|
|
12
|
-
def get_best_dot(self, vec: glm.vec3) ->
|
|
20
|
+
def get_best_dot(self, vec: glm.vec3) -> int:
|
|
13
21
|
"""
|
|
14
22
|
Gets the best dot point of a cube
|
|
15
23
|
"""
|
|
16
|
-
|
|
24
|
+
index = 0
|
|
25
|
+
if vec[0] > 0: index += 4
|
|
26
|
+
if vec[1] > 0: index += 2
|
|
27
|
+
if vec[2] > 0: index += 1
|
|
28
|
+
return self.dot_indices[index]
|
|
29
|
+
|
|
30
|
+
def get_line_collided(self, position: glm.vec3, forward: glm.vec3) -> list[int]:
|
|
31
|
+
"""
|
|
32
|
+
Returns all the faces on the cube since the AABB degenerates on the cube mesh
|
|
33
|
+
"""
|
|
34
|
+
return [i for i in range(12)]
|
basilisk/mesh/mesh.py
CHANGED
|
@@ -7,6 +7,7 @@ from .narrow_bvh import NarrowBVH
|
|
|
7
7
|
from ..generic.matrices import compute_inertia_moment, compute_inertia_product
|
|
8
8
|
from ..generic.meshes import get_extreme_points_np, moller_trumbore
|
|
9
9
|
from .mesh_from_data import from_data
|
|
10
|
+
import time
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class Mesh():
|
|
@@ -40,31 +41,44 @@ class Mesh():
|
|
|
40
41
|
# Verify the path type
|
|
41
42
|
if isinstance(data, str) or isinstance(data, os.PathLike): # Load the model from file
|
|
42
43
|
model = load_model(data, calculate_tangents=True)
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
|
|
45
|
+
# Get the vertex data
|
|
46
|
+
if len(model.vertex_data[0]) == 8:
|
|
47
|
+
self.data = model.vertex_data.copy()
|
|
48
|
+
else:
|
|
49
|
+
self.data = np.zeros(shape=(len(model.vertex_data), 8))
|
|
50
|
+
self.data[:,:3] = model.vertex_data[:,:3]
|
|
51
|
+
self.data[:,5:] = model.vertex_data[:,3:]
|
|
52
|
+
|
|
53
|
+
# Get tangent data
|
|
54
|
+
if len(model.tangent_data[0]) == 6:
|
|
55
|
+
self.data = np.hstack([self.data, model.tangent_data])
|
|
56
|
+
else:
|
|
57
|
+
tangents = np.zeros(shape=(len(self.data), 6))
|
|
58
|
+
tangents[:,:] += [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
|
|
59
|
+
self.data = np.hstack([self.data, tangents])
|
|
60
|
+
|
|
61
|
+
elif isinstance(data, np.ndarray):
|
|
62
|
+
model = from_data(data)
|
|
63
|
+
self.data = model.vertex_data
|
|
64
|
+
|
|
45
65
|
else: # Invalid data type
|
|
46
66
|
raise TypeError(f'Invalid path type: {type(data)}. Expected a string or os.path')
|
|
47
|
-
|
|
48
|
-
# Get the vertex data
|
|
49
|
-
if len(model.vertex_data[0]) == 8:
|
|
50
|
-
self.data = model.vertex_data.copy()
|
|
51
|
-
else:
|
|
52
|
-
self.data = np.zeros(shape=(len(model.vertex_data), 8))
|
|
53
|
-
self.data[:,:3] = model.vertex_data[:,:3]
|
|
54
|
-
self.data[:,5:] = model.vertex_data[:,3:]
|
|
55
67
|
|
|
56
|
-
# Get tangent data
|
|
57
|
-
if len(model.tangent_data[0]) == 6:
|
|
58
|
-
self.data = np.hstack([self.data, model.tangent_data])
|
|
59
|
-
else:
|
|
60
|
-
tangents = np.zeros(shape=(len(self.data), 6))
|
|
61
|
-
tangents[:,:] += [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
|
|
62
|
-
self.data = np.hstack([self.data, tangents])
|
|
63
|
-
|
|
64
68
|
# Mesh points and triangles used for physics/collisions
|
|
65
69
|
self.points = model.vertex_points.copy()
|
|
66
70
|
self.indices = model.point_indices.copy()
|
|
67
71
|
|
|
72
|
+
self.hash = hash(str(self.data))
|
|
73
|
+
|
|
74
|
+
# generate edges from faces
|
|
75
|
+
edges = [set() for _ in range(len(self.points))]
|
|
76
|
+
for face in self.indices:
|
|
77
|
+
|
|
78
|
+
# add points to the dictionary since each point on a face is adjacent to the others
|
|
79
|
+
for i in range(3): edges[face[i]].update([int(face[(i + 1) % 3]), int(face[(i + 2) % 3])])
|
|
80
|
+
self.edges = [tuple(adjacent) for adjacent in edges]
|
|
81
|
+
|
|
68
82
|
# Model will no longer be used
|
|
69
83
|
del model
|
|
70
84
|
|
|
@@ -127,6 +141,12 @@ class Mesh():
|
|
|
127
141
|
-icp, -iap, ic
|
|
128
142
|
)
|
|
129
143
|
|
|
144
|
+
# return glm.mat3x3(
|
|
145
|
+
# ic, -iap, -icp,
|
|
146
|
+
# -iap, ib, -ibp,
|
|
147
|
+
# -ibp, -iap, ia
|
|
148
|
+
# )
|
|
149
|
+
|
|
130
150
|
def get_best_triangle(self, point: glm.vec3, vec: glm.vec3) -> int:
|
|
131
151
|
"""
|
|
132
152
|
Gets the triangle with the closest intersection, -1 if no intersection is found
|
|
@@ -155,51 +175,43 @@ class Mesh():
|
|
|
155
175
|
best_index = triangle
|
|
156
176
|
|
|
157
177
|
return best_index
|
|
178
|
+
|
|
179
|
+
def get_best_dot(self, vec: glm.vec3) -> int:
|
|
180
|
+
"""
|
|
181
|
+
Gets the point with the highest normalized dot product to the given vector
|
|
182
|
+
"""
|
|
183
|
+
triangle = self.bvh.get_best_dot(vec)
|
|
184
|
+
if triangle == -1: return None
|
|
185
|
+
index = max(self.indices[triangle], key=lambda t: glm.dot(glm.normalize(self.points[t]), vec))
|
|
186
|
+
return index
|
|
158
187
|
|
|
159
|
-
def
|
|
188
|
+
def get_best_dot_hill_climbing(self, vec: glm.vec3) -> int:
|
|
160
189
|
"""
|
|
161
|
-
Gets the
|
|
190
|
+
Gets the point with the highest dot product to the given vector using a hill climbing algorithm. This function is only effective for convex models.
|
|
162
191
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
point = glm.vec3(point)
|
|
167
|
-
vec = glm.vec3(vec)
|
|
192
|
+
best_index = 0
|
|
193
|
+
best_dot = glm.dot(self.points[best_index], vec)
|
|
168
194
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
# check if triangle intersects
|
|
172
|
-
intersection = moller_trumbore(point, vec, [self.points[t] for t in triangle])
|
|
173
|
-
if not intersection: continue
|
|
195
|
+
while True:
|
|
174
196
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if glm.dot(difference, vec) < 0: continue
|
|
178
|
-
|
|
179
|
-
# determine best distance
|
|
180
|
-
distance = glm.length(difference)
|
|
181
|
-
if best_distance < 0 or distance < best_distance:
|
|
182
|
-
best_distance = distance
|
|
183
|
-
best_index = index
|
|
197
|
+
best_changed = False
|
|
198
|
+
for index in self.edges[best_index]:
|
|
184
199
|
|
|
185
|
-
|
|
200
|
+
dot = glm.dot(self.points[index], vec)
|
|
201
|
+
if dot > best_dot:
|
|
202
|
+
best_dot = dot
|
|
203
|
+
best_changed = True
|
|
204
|
+
best_index = index
|
|
205
|
+
|
|
206
|
+
if not best_changed: break
|
|
186
207
|
|
|
187
|
-
|
|
208
|
+
return best_index
|
|
209
|
+
|
|
210
|
+
def get_line_collided(self, position: glm.vec3, forward: glm.vec3) -> list[tuple[int, int, int]]:
|
|
188
211
|
"""
|
|
189
|
-
|
|
212
|
+
Determines which triangles are intersecting with the given line segment. Returns the indices of the triangle contained in the mesh points list
|
|
190
213
|
"""
|
|
191
|
-
|
|
192
|
-
if triangle == -1: return None
|
|
193
|
-
index = max(self.indices[triangle], key=lambda t: glm.dot(glm.normalize(self.points[t]), vec))
|
|
194
|
-
return glm.vec3(self.points[index])
|
|
195
|
-
|
|
196
|
-
def get_best_dot_old(self, vec):
|
|
197
|
-
best_dot = -1e10
|
|
198
|
-
best = None
|
|
199
|
-
for point in self.points:
|
|
200
|
-
dot = glm.dot(glm.normalize(point), vec)
|
|
201
|
-
if dot > best_dot: best_dot, best = dot, glm.vec3(point)
|
|
202
|
-
return best
|
|
214
|
+
return self.bvh.get_line_collided(position, forward)
|
|
203
215
|
|
|
204
216
|
def __repr__(self) -> str:
|
|
205
217
|
size = (self.data.nbytes + self.points.nbytes + self.indices.nbytes) / 1024 / 1024
|
|
@@ -214,3 +226,6 @@ class Mesh():
|
|
|
214
226
|
x1, y1, z1 = self.top_right
|
|
215
227
|
x2, y2, z2 = self.bottom_left
|
|
216
228
|
return [glm.vec3(x, y, z) for z in (z1, z2) for y in (y1, y2) for x in (x1, x2)]
|
|
229
|
+
|
|
230
|
+
def __hash__(self):
|
|
231
|
+
return self.hash
|
basilisk/mesh/mesh_from_data.py
CHANGED
|
@@ -1,48 +1,133 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
from .model import Model
|
|
3
|
+
import glm
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
def from_data(data: np.ndarray) -> np.ndarray:
|
|
5
|
+
def from_data(data: np.ndarray) -> Model:
|
|
5
6
|
"""
|
|
6
7
|
Converts data given to a format compatable with basilisk models
|
|
7
8
|
"""
|
|
8
9
|
|
|
10
|
+
# Create an empty model
|
|
11
|
+
model = Model()
|
|
12
|
+
# Get the shape of the given data
|
|
13
|
+
|
|
14
|
+
# Get the shape of the given data and check for a valid shape
|
|
9
15
|
shape = data.shape
|
|
16
|
+
if len(shape) == 2: pass
|
|
17
|
+
elif len(shape) == 3: data = np.reshape(data, (shape[0] * 3, shape[1] * shape[2] // 3)); shape = data.shape
|
|
18
|
+
else: raise ValueError(f"Could not find valid format for the given model data of shape {shape}")
|
|
19
|
+
|
|
20
|
+
# Data to be retraived/generated
|
|
21
|
+
positions = None
|
|
22
|
+
uvs = None
|
|
23
|
+
normals = None
|
|
24
|
+
tangents = None
|
|
10
25
|
|
|
11
26
|
if shape[1] == 3: # Just given position
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
27
|
+
positions = data[:,:]
|
|
28
|
+
uvs = get_uvs(positions)
|
|
29
|
+
normals = get_normals(positions)
|
|
30
|
+
tangents = get_tangents(normals)
|
|
31
|
+
|
|
32
|
+
elif shape[1] == 5: # Given position and uv, but no normals
|
|
33
|
+
positions = data[:,:3]
|
|
34
|
+
uvs = data[:,3:5]
|
|
35
|
+
normals = get_normals(positions)
|
|
36
|
+
tangents = get_tangents(normals)
|
|
17
37
|
|
|
18
38
|
elif shape[1] == 6: # Given position and normals, but no UV
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
data[:
|
|
22
|
-
|
|
39
|
+
positions = data[:,:3]
|
|
40
|
+
uvs = get_uvs(positions)
|
|
41
|
+
normals = data[:,3:6]
|
|
42
|
+
tangents = get_tangents(normals)
|
|
23
43
|
|
|
24
44
|
elif shape[1] == 8: # Given position, normals and UV
|
|
25
|
-
|
|
45
|
+
positions = data[:,:3]
|
|
46
|
+
uvs = data[:,3:5]
|
|
47
|
+
normals = data[:,5:8]
|
|
48
|
+
tangents = get_tangents(normals)
|
|
26
49
|
|
|
27
50
|
elif shape[1] == 14: #Given position, normals, UV, bitangents, and tangents, no change needed
|
|
28
|
-
|
|
51
|
+
positions = data[:,:3]
|
|
52
|
+
uvs = data[:,3:5]
|
|
53
|
+
normals = data[:,5:8]
|
|
54
|
+
tangents = data[:,8:14]
|
|
55
|
+
|
|
56
|
+
else:
|
|
57
|
+
raise ValueError(f"Could not find valid format for the given model data of shape {shape}")
|
|
58
|
+
|
|
59
|
+
model.vertex_data = np.zeros(shape=(shape[0], 14))
|
|
60
|
+
model.vertex_data[:,:3] = positions
|
|
61
|
+
model.vertex_data[:,3:5] = uvs
|
|
62
|
+
model.vertex_data[:,5:8] = normals
|
|
63
|
+
model.vertex_data[:,8:14] = tangents
|
|
64
|
+
model.vertex_points, model.point_indices = get_points_and_indices(positions)
|
|
29
65
|
|
|
30
|
-
|
|
66
|
+
print(model.vertex_points, model.point_indices)
|
|
67
|
+
|
|
68
|
+
return model
|
|
31
69
|
|
|
32
70
|
|
|
33
71
|
def get_normals(positions: np.ndarray) -> np.ndarray:
|
|
34
72
|
"""
|
|
35
|
-
Gets the normals
|
|
73
|
+
Gets the normals from the position data
|
|
74
|
+
Returns a numpy array
|
|
36
75
|
"""
|
|
37
76
|
|
|
38
77
|
# Create empty array for the normals
|
|
39
|
-
normals = np.zeros(shape=positions.shape)
|
|
78
|
+
normals = np.zeros(shape=positions.shape, dtype='f4')
|
|
40
79
|
|
|
41
80
|
# Loop through each triangle and calculate the normal of the surface
|
|
42
81
|
for tri in range(positions.shape[0] // 3):
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
normals[tri
|
|
82
|
+
v1 = glm.vec3(positions[tri * 3]) - glm.vec3(positions[tri * 3 + 1])
|
|
83
|
+
v2 = glm.vec3(positions[tri * 3]) - glm.vec3(positions[tri * 3 + 2])
|
|
84
|
+
normal = glm.normalize(glm.cross(v1, v2))
|
|
85
|
+
normals[tri * 3 ] = list(normal.xyz)
|
|
86
|
+
normals[tri * 3 + 1] = list(normal.xyz)
|
|
87
|
+
normals[tri * 3 + 2] = list(normal.xyz)
|
|
88
|
+
|
|
89
|
+
return normals
|
|
90
|
+
|
|
91
|
+
def get_uvs(positions: np.ndarray) -> np.ndarray:
|
|
92
|
+
"""
|
|
93
|
+
Gets the uvs from the position array.
|
|
94
|
+
Currently assigns each triangle arbitrarily, since there is no standard
|
|
95
|
+
"""
|
|
96
|
+
uvs = np.array([*[[0, 0], [0, 1], [1, 0]] * (positions.shape[0]//3)])
|
|
97
|
+
return uvs
|
|
98
|
+
|
|
99
|
+
def get_tangents(normals: np.array):
|
|
100
|
+
"""
|
|
101
|
+
Gets the uvs from the normal array.
|
|
102
|
+
Currently just fills with arbitrary data, since there is no standard
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
# Get linearly independent vectors
|
|
106
|
+
tangent = np.cross(normals, [1, 1, 0])
|
|
107
|
+
bitangent = np.cross(normals, tangent)
|
|
108
|
+
|
|
109
|
+
# Combine to a single array
|
|
110
|
+
all_tangents = np.hstack([tangent, bitangent], dtype='f4')
|
|
111
|
+
|
|
112
|
+
return all_tangents
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_points_and_indices(positions: np.ndarray) -> tuple[np.ndarray]:
|
|
116
|
+
"""
|
|
117
|
+
Gets the unique points and indices from the position data.
|
|
118
|
+
Returns a tuple of numpy arrays: (points, indices)
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
points = {}
|
|
122
|
+
indices = [[] for i in range(len(positions) // 3)]
|
|
123
|
+
|
|
124
|
+
for i, point in enumerate(positions):
|
|
125
|
+
point = tuple(point)
|
|
126
|
+
if point not in points: points[point] = []
|
|
127
|
+
points[point].append(i // 3)
|
|
128
|
+
|
|
129
|
+
for i, index_mapping in enumerate(points.values()):
|
|
130
|
+
for triangle in index_mapping:
|
|
131
|
+
indices[triangle].append(i)
|
|
47
132
|
|
|
48
|
-
return np.
|
|
133
|
+
return np.array(list(points.keys()), dtype='f4'), np.array(indices, dtype='i')
|
basilisk/mesh/narrow_aabb.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import glm
|
|
2
|
+
|
|
3
|
+
from basilisk.generic.collisions import collide_aabb_line
|
|
2
4
|
from .narrow_primative import NarrowPrimative
|
|
3
5
|
from ..generic.abstract_bvh import AbstractAABB as AABB
|
|
4
6
|
from ..generic.meshes import get_aabb_line_collision
|
|
@@ -78,4 +80,11 @@ class NarrowAABB(AABB):
|
|
|
78
80
|
aabbs += self.b.get_all_aabbs(layer + 1)
|
|
79
81
|
else: aabbs.append((self.b.top_right, self.b.bottom_left, layer + 1))
|
|
80
82
|
|
|
81
|
-
return aabbs
|
|
83
|
+
return aabbs
|
|
84
|
+
|
|
85
|
+
def get_line_collided(self, position: glm.vec3, forward: glm.vec3) -> list[int]:
|
|
86
|
+
"""
|
|
87
|
+
Returns the colliders that may intersect with the given line
|
|
88
|
+
"""
|
|
89
|
+
if not collide_aabb_line(self.top_right, self.bottom_left, position, forward): return []
|
|
90
|
+
return (self.a.get_line_collided(position, forward) if isinstance(self.a, NarrowAABB) else [self.a.index]) + (self.b.get_line_collided(position, forward) if isinstance(self.b, NarrowAABB) else [self.b.index])
|
basilisk/mesh/narrow_bvh.py
CHANGED
|
@@ -81,4 +81,12 @@ class NarrowBVH(BVH):
|
|
|
81
81
|
Returns all AABBs, their extreme points, and their layer
|
|
82
82
|
"""
|
|
83
83
|
if isinstance(self.root, NarrowAABB): return self.root.get_all_aabbs(0)
|
|
84
|
-
return [(self.root.top_right, self.root.bottom_left, 0)]
|
|
84
|
+
return [(self.root.top_right, self.root.bottom_left, 0)]
|
|
85
|
+
|
|
86
|
+
def get_line_collided(self, position: glm.vec3, forward: glm.vec3) -> list[tuple[int, int, int]]:
|
|
87
|
+
"""
|
|
88
|
+
Determines which triangles are intersecting with the given line segment. Returns the indices of the triangle contained in the mesh points list
|
|
89
|
+
"""
|
|
90
|
+
if isinstance(self.root, NarrowAABB): return self.root.get_line_collided(position, forward)
|
|
91
|
+
return self.root.index
|
|
92
|
+
|