basilisk-engine 0.0.1__py3-none-any.whl → 0.0.3__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/bsk_assets/__init__.py +0 -0
- basilisk/collisions/__init__.py +0 -0
- basilisk/collisions/broad/__init__.py +0 -0
- basilisk/collisions/broad/broad_aabb.py +96 -0
- basilisk/collisions/broad/broad_bvh.py +102 -0
- basilisk/collisions/collider.py +75 -0
- basilisk/collisions/collider_handler.py +163 -0
- basilisk/collisions/narrow/__init__.py +0 -0
- basilisk/collisions/narrow/epa.py +86 -0
- basilisk/collisions/narrow/gjk.py +66 -0
- basilisk/collisions/narrow/helper.py +23 -0
- basilisk/draw/__init__.py +0 -0
- basilisk/draw/draw.py +101 -0
- basilisk/draw/draw_handler.py +208 -0
- basilisk/draw/font_renderer.py +28 -0
- basilisk/generic/__init__.py +0 -0
- basilisk/generic/abstract_bvh.py +16 -0
- basilisk/generic/collisions.py +26 -0
- basilisk/generic/input_validation.py +28 -0
- basilisk/generic/math.py +7 -0
- basilisk/generic/matrices.py +34 -0
- basilisk/generic/meshes.py +73 -0
- basilisk/generic/quat.py +119 -0
- basilisk/generic/quat_methods.py +8 -0
- basilisk/generic/vec3.py +112 -0
- basilisk/input/__init__.py +0 -0
- basilisk/input/mouse.py +60 -0
- basilisk/mesh/__init__.py +0 -0
- basilisk/mesh/built-in/__init__.py +0 -0
- basilisk/mesh/cube.py +20 -0
- basilisk/mesh/mesh.py +216 -0
- basilisk/mesh/mesh_from_data.py +48 -0
- basilisk/mesh/model.py +272 -0
- basilisk/mesh/narrow_aabb.py +81 -0
- basilisk/mesh/narrow_bvh.py +84 -0
- basilisk/mesh/narrow_primative.py +24 -0
- basilisk/nodes/__init__.py +0 -0
- basilisk/nodes/node.py +508 -0
- basilisk/nodes/node_handler.py +94 -0
- basilisk/physics/__init__.py +0 -0
- basilisk/physics/physics_body.py +36 -0
- basilisk/physics/physics_engine.py +37 -0
- basilisk/render/__init__.py +0 -0
- basilisk/render/batch.py +85 -0
- basilisk/render/camera.py +166 -0
- basilisk/render/chunk.py +85 -0
- basilisk/render/chunk_handler.py +139 -0
- basilisk/render/frame.py +182 -0
- basilisk/render/image.py +76 -0
- basilisk/render/image_handler.py +119 -0
- basilisk/render/light.py +97 -0
- basilisk/render/light_handler.py +54 -0
- basilisk/render/material.py +196 -0
- basilisk/render/material_handler.py +123 -0
- basilisk/render/shader_handler.py +95 -0
- basilisk/render/sky.py +118 -0
- basilisk/shaders/__init__.py +0 -0
- {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/METADATA +1 -1
- basilisk_engine-0.0.3.dist-info/RECORD +65 -0
- basilisk_engine-0.0.1.dist-info/RECORD +0 -8
- {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/WHEEL +0 -0
- {basilisk_engine-0.0.1.dist-info → basilisk_engine-0.0.3.dist-info}/top_level.txt +0 -0
basilisk/mesh/model.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import glm
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Model:
|
|
6
|
+
"""
|
|
7
|
+
Instance of a loaded model. Contains all objects, groups, and vertex data
|
|
8
|
+
model.vertex_data contains all vertex data
|
|
9
|
+
Objects stored in the model.objects dictionary, where keys are the object names (marked by 'o') in the .obj
|
|
10
|
+
Default object key is '0'
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self) -> None:
|
|
14
|
+
self.objects = {0 : VertexObject()}
|
|
15
|
+
|
|
16
|
+
self.vertex_data = []
|
|
17
|
+
"""All vertex data in the obj. Use this for buffer data"""
|
|
18
|
+
self.tangent_data = []
|
|
19
|
+
"""Tangents and bitangents"""
|
|
20
|
+
self.format = None
|
|
21
|
+
self.attribs = None
|
|
22
|
+
|
|
23
|
+
self.vertex_points = []
|
|
24
|
+
"""The unique points given by the file"""
|
|
25
|
+
self.vertex_uv = []
|
|
26
|
+
"""The unique texture coordinates given by the file"""
|
|
27
|
+
self.vertex_normals = []
|
|
28
|
+
"""The unique normals given by the file"""
|
|
29
|
+
|
|
30
|
+
self.point_indices = []
|
|
31
|
+
"""Indices of to vertex_points to construct triangles. Grouped in three."""
|
|
32
|
+
self.uv_indices = []
|
|
33
|
+
"""Indices of to vertex_uv to construct triangles. Grouped in three."""
|
|
34
|
+
self.normal_indices = []
|
|
35
|
+
"""Indices of to vertex_normals to construct triangles. Grouped in three."""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def __repr__(self) -> str:
|
|
39
|
+
return_string = '<Model | objects: {'
|
|
40
|
+
for vertex_object in self.objects.keys():
|
|
41
|
+
return_string += str(vertex_object) + ', '
|
|
42
|
+
return_string = return_string[:-2] + '}>'
|
|
43
|
+
return return_string
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class VertexObject:
|
|
47
|
+
"""
|
|
48
|
+
Object conataining groups of vertices.
|
|
49
|
+
Groups stored in the vertex_object.groups dictionary, where keys are the group names (marked by 'g') in the .obj
|
|
50
|
+
Default group key is '0'
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self) -> None:
|
|
54
|
+
self.groups = {0 : VertexGroup()}
|
|
55
|
+
|
|
56
|
+
def __repr__(self) -> str:
|
|
57
|
+
return_string = '<Vertex Object | groups: {'
|
|
58
|
+
for vertex_group in self.groups.keys():
|
|
59
|
+
return_string += str(vertex_group) + ', '
|
|
60
|
+
return_string = return_string[:-2] + '}>'
|
|
61
|
+
return return_string
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class VertexGroup:
|
|
65
|
+
"""
|
|
66
|
+
Groups containing the vertex data
|
|
67
|
+
vertex_group.vertex_data will be a numpy array of vertices
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self) -> None:
|
|
71
|
+
self.vertex_data = []
|
|
72
|
+
self.tangent_data = []
|
|
73
|
+
|
|
74
|
+
def __repr__(self) -> str:
|
|
75
|
+
return f'<Vertex Group | {self.vertex_data}>'
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_model(obj_file: str, calculate_tangents=False) -> Model:
|
|
79
|
+
"""
|
|
80
|
+
Loads an obj model. Returns a model class instance
|
|
81
|
+
model.vertex_data contains all vertex data combined in a single numpy array
|
|
82
|
+
Args:
|
|
83
|
+
file:
|
|
84
|
+
Path to the .obj file to load
|
|
85
|
+
calculate_tangents:
|
|
86
|
+
Calculates the tangent and bitangent vectors for normal mapping
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
model = Model()
|
|
90
|
+
current_object = 0
|
|
91
|
+
current_group = 0
|
|
92
|
+
|
|
93
|
+
vertex_format = None
|
|
94
|
+
vertex_attribs = None
|
|
95
|
+
|
|
96
|
+
with open(obj_file, 'r') as file:
|
|
97
|
+
line = file.readline()
|
|
98
|
+
while line:
|
|
99
|
+
line = line.strip()
|
|
100
|
+
|
|
101
|
+
# Add object
|
|
102
|
+
if line.startswith('o '):
|
|
103
|
+
if line[2:].strip() not in model.objects:
|
|
104
|
+
model.objects[line[2:].strip()] = VertexObject()
|
|
105
|
+
current_object = line[2:].strip()
|
|
106
|
+
|
|
107
|
+
# Add group
|
|
108
|
+
elif line.startswith('g '):
|
|
109
|
+
if line[2:].strip() not in model.objects[current_object].groups:
|
|
110
|
+
model.objects[current_object].groups[line[2:].strip()] = VertexGroup()
|
|
111
|
+
current_group = line[2:].strip()
|
|
112
|
+
|
|
113
|
+
# Add vertex point
|
|
114
|
+
elif line.startswith('v '):
|
|
115
|
+
points = list(map(float, line[2:].strip().split(' ')))
|
|
116
|
+
model.vertex_points.append(points)
|
|
117
|
+
|
|
118
|
+
# Add vertex UV
|
|
119
|
+
elif line.startswith('vt '):
|
|
120
|
+
uvs = list(map(float, line[3:].strip().split(' ')))
|
|
121
|
+
model.vertex_uv.append(uvs[:2])
|
|
122
|
+
|
|
123
|
+
# Add vertex normals
|
|
124
|
+
elif line.startswith('vn '):
|
|
125
|
+
normals = list(map(float, line[3:].strip().split(' ')))
|
|
126
|
+
model.vertex_normals.append(normals)
|
|
127
|
+
|
|
128
|
+
# Create faces
|
|
129
|
+
elif line.startswith('f '):
|
|
130
|
+
corners = line[2:].strip().split(' ')
|
|
131
|
+
# The index of the position, uv, and normal in each vertex
|
|
132
|
+
vertex_indices = [[0, 0, 0] for i in range(len(corners))]
|
|
133
|
+
for corner_index, corner in enumerate(corners):
|
|
134
|
+
corner = corner.split('/')
|
|
135
|
+
|
|
136
|
+
if not vertex_format:
|
|
137
|
+
if len(corner) == 1:
|
|
138
|
+
vertex_format = '3f'
|
|
139
|
+
vertex_attribs = ['in_position']
|
|
140
|
+
elif not corner[1]:
|
|
141
|
+
vertex_format = '3f 3f'
|
|
142
|
+
vertex_attribs = ['in_position', 'in_normal']
|
|
143
|
+
else:
|
|
144
|
+
vertex_format = '3f 2f 3f'
|
|
145
|
+
vertex_attribs = ['in_position', 'in_uv', 'in_normal']
|
|
146
|
+
|
|
147
|
+
vertex = []
|
|
148
|
+
|
|
149
|
+
# Add each attribute to the vertex
|
|
150
|
+
for attribute, index in enumerate(corner):
|
|
151
|
+
if attribute == 0 and index:
|
|
152
|
+
vertex += model.vertex_points[int(index) - 1]
|
|
153
|
+
vertex_indices[corner_index][0] = int(index) - 1
|
|
154
|
+
if attribute == 1 and index:
|
|
155
|
+
vertex += model.vertex_uv[int(index) - 1]
|
|
156
|
+
vertex_indices[corner_index][1] = int(index) - 1
|
|
157
|
+
if attribute == 2 and index:
|
|
158
|
+
vertex += model.vertex_normals[int(index) - 1]
|
|
159
|
+
vertex_indices[corner_index][2] = int(index) - 1
|
|
160
|
+
|
|
161
|
+
# Replace the vertex data
|
|
162
|
+
corners[corner_index] = vertex
|
|
163
|
+
|
|
164
|
+
# Add each triangle to the objects vertex array
|
|
165
|
+
for triangle in range(len(corners) - 2):
|
|
166
|
+
if 'in_normal' not in vertex_attribs: # If the model doesnt have normals, calculate face normals
|
|
167
|
+
p1 = glm.vec3(corners[0])
|
|
168
|
+
p2 = glm.vec3(corners[1 + triangle])
|
|
169
|
+
p3 = glm.vec3(corners[2 + triangle])
|
|
170
|
+
normal = glm.normalize(glm.cross(p2 - p1, p3 - p1))
|
|
171
|
+
normal = list(normal.xyz)
|
|
172
|
+
model.vertex_normals.append(normal)
|
|
173
|
+
model.objects[current_object].groups[current_group].vertex_data.append(corners[0] + normal)
|
|
174
|
+
model.objects[current_object].groups[current_group].vertex_data.append(corners[1 + triangle] + normal)
|
|
175
|
+
model.objects[current_object].groups[current_group].vertex_data.append(corners[2 + triangle] + normal)
|
|
176
|
+
|
|
177
|
+
# Add the triangle to the indices
|
|
178
|
+
model.point_indices.append([vertex_indices[0][0], vertex_indices[1 + triangle][0], vertex_indices[2 + triangle][0]])
|
|
179
|
+
model.normal_indices.append([len(model.vertex_normals) - 1] * 3)
|
|
180
|
+
else: # Standard reading
|
|
181
|
+
model.objects[current_object].groups[current_group].vertex_data.append(corners[0])
|
|
182
|
+
model.objects[current_object].groups[current_group].vertex_data.append(corners[1 + triangle])
|
|
183
|
+
model.objects[current_object].groups[current_group].vertex_data.append(corners[2 + triangle])
|
|
184
|
+
|
|
185
|
+
# Add the triangle to the indices
|
|
186
|
+
model.point_indices.append([vertex_indices[0][0], vertex_indices[1 + triangle][0], vertex_indices[2 + triangle][0]])
|
|
187
|
+
model.uv_indices.append([vertex_indices[0][1], vertex_indices[1 + triangle][1], vertex_indices[2 + triangle][1]])
|
|
188
|
+
model.normal_indices.append([vertex_indices[0][2], vertex_indices[1 + triangle][2], vertex_indices[2 + triangle][2]])
|
|
189
|
+
|
|
190
|
+
# Calculate the tangents and bitangents
|
|
191
|
+
if calculate_tangents and 'in_uv' in vertex_attribs:
|
|
192
|
+
v1 = corners[0]
|
|
193
|
+
v2 = corners[1 + triangle]
|
|
194
|
+
v3 = corners[2 + triangle]
|
|
195
|
+
|
|
196
|
+
x1 = v2[0] - v1[0]
|
|
197
|
+
x2 = v3[0] - v1[0]
|
|
198
|
+
y1 = v2[1] - v1[1]
|
|
199
|
+
y2 = v3[1] - v1[1]
|
|
200
|
+
z1 = v2[2] - v1[2]
|
|
201
|
+
z2 = v3[2] - v1[2]
|
|
202
|
+
|
|
203
|
+
s1 = v2[3] - v1[3]
|
|
204
|
+
s2 = v3[3] - v1[3]
|
|
205
|
+
t1 = v2[4] - v1[4]
|
|
206
|
+
t2 = v3[4] - v1[4]
|
|
207
|
+
|
|
208
|
+
if (s1 * t2 - s2 * t1): r = 1.0 / (s1 * t2 - s2 * t1)
|
|
209
|
+
else: r = 1
|
|
210
|
+
|
|
211
|
+
tangent = glm.normalize(((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r))
|
|
212
|
+
bitangent = glm.normalize(((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r))
|
|
213
|
+
|
|
214
|
+
T = np.array(tangent)
|
|
215
|
+
U = np.array(bitangent)
|
|
216
|
+
N1 = np.array(corners[0][5:8])
|
|
217
|
+
N2 = np.array(corners[1 + triangle][5:8])
|
|
218
|
+
N3 = np.array(corners[2 + triangle][5:8])
|
|
219
|
+
|
|
220
|
+
T1 = T - np.dot(N1, T) * N1
|
|
221
|
+
T2 = T - np.dot(N2, T) * N2
|
|
222
|
+
T3 = T - np.dot(N3, T) * N3
|
|
223
|
+
U1 = U - np.dot(N1, U) * N1 - np.dot(T1, U) * T1
|
|
224
|
+
U2 = U - np.dot(N2, U) * N2 - np.dot(T2, U) * T2
|
|
225
|
+
U3 = U - np.dot(N3, U) * N3 - np.dot(T3, U) * T3
|
|
226
|
+
|
|
227
|
+
data = [[*T1, *U1], [*T2, *U2], [*T3, *U3]]
|
|
228
|
+
|
|
229
|
+
model.objects[current_object].groups[current_group].tangent_data.extend(data)
|
|
230
|
+
|
|
231
|
+
line = file.readline()
|
|
232
|
+
|
|
233
|
+
vertex_groups = []
|
|
234
|
+
tangent_groups = []
|
|
235
|
+
|
|
236
|
+
# Loop through all vertex objects and groups in the model
|
|
237
|
+
for object in model.objects.values():
|
|
238
|
+
for group in object.groups.values():
|
|
239
|
+
# Ignore empty groups
|
|
240
|
+
if not len(group.vertex_data): continue
|
|
241
|
+
# Convert to a numpy array
|
|
242
|
+
group.vertex_data = np.array(group.vertex_data, dtype='f4')
|
|
243
|
+
# Add to the vertex_groups list to be stacked
|
|
244
|
+
vertex_groups.append(group.vertex_data)
|
|
245
|
+
tangent_groups.append(group.tangent_data)
|
|
246
|
+
|
|
247
|
+
# Array of all vertices from all the model's groups combined
|
|
248
|
+
vertices = np.vstack(vertex_groups, dtype='f4')
|
|
249
|
+
tangents = np.vstack(tangent_groups, dtype='f4')
|
|
250
|
+
|
|
251
|
+
# Save the model's combined vertices
|
|
252
|
+
model.vertex_data = vertices
|
|
253
|
+
model.tangent_data = tangents
|
|
254
|
+
|
|
255
|
+
# Convert the points and indices to array for convenience with C-like buffers
|
|
256
|
+
model.vertex_points = np.array(model.vertex_points, dtype="f4")
|
|
257
|
+
model.vertex_uv = np.array(model.vertex_uv, dtype="f4")
|
|
258
|
+
model.vertex_normals = np.array(model.vertex_normals, dtype="f4")
|
|
259
|
+
model.point_indices = np.array(model.point_indices, dtype="i4")
|
|
260
|
+
model.uv_indices = np.array(model.uv_indices, dtype="i4")
|
|
261
|
+
model.normal_indices = np.array(model.normal_indices, dtype="i4")
|
|
262
|
+
|
|
263
|
+
# Add normals to position only models
|
|
264
|
+
if vertex_format == '3f':
|
|
265
|
+
vertex_format = '3f 3f'
|
|
266
|
+
vertex_attribs = ['in_position', 'in_normal']
|
|
267
|
+
|
|
268
|
+
# Save the model vertex format and attribs
|
|
269
|
+
model.format = vertex_format
|
|
270
|
+
model.attribs = vertex_attribs
|
|
271
|
+
|
|
272
|
+
return model
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import glm
|
|
2
|
+
from .narrow_primative import NarrowPrimative
|
|
3
|
+
from ..generic.abstract_bvh import AbstractAABB as AABB
|
|
4
|
+
from ..generic.meshes import get_aabb_line_collision
|
|
5
|
+
|
|
6
|
+
class NarrowAABB(AABB):
|
|
7
|
+
top_right: glm.vec3
|
|
8
|
+
"""The furthest positive corner of the AABB"""
|
|
9
|
+
bottom_left: glm.vec3
|
|
10
|
+
"""The furthest negative corner of the AABB"""
|
|
11
|
+
geometric_center: glm.vec3
|
|
12
|
+
"""The center of the object calculated from its extreme points"""
|
|
13
|
+
a: AABB | NarrowPrimative
|
|
14
|
+
"""Child AABB or Collider 1"""
|
|
15
|
+
b: AABB | NarrowPrimative
|
|
16
|
+
"""Child AABB or Collider 2"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, top_right:glm.vec3, bottom_left:glm.vec3, a: AABB, b: AABB) -> None:
|
|
19
|
+
self.top_right = top_right
|
|
20
|
+
self.bottom_left = bottom_left
|
|
21
|
+
self.geometric_center = (top_right + bottom_left) / 2
|
|
22
|
+
self.a = a
|
|
23
|
+
self.b = b
|
|
24
|
+
|
|
25
|
+
def get_possible_triangles(self, point: glm.vec3, vec: glm.vec3) -> list[int]:
|
|
26
|
+
"""
|
|
27
|
+
Determines the closest intersecting on the bvh
|
|
28
|
+
"""
|
|
29
|
+
indices = []
|
|
30
|
+
if not get_aabb_line_collision(self.top_right, self.bottom_left, point, vec): return indices
|
|
31
|
+
|
|
32
|
+
for child in (self.a, self.b):
|
|
33
|
+
|
|
34
|
+
# if child is another AABB
|
|
35
|
+
if isinstance(child, NarrowAABB):
|
|
36
|
+
indices += child.get_possible_triangles(point, vec)
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
# if child is a primative
|
|
40
|
+
index = child.is_possible_triangle(point, vec)
|
|
41
|
+
if index == -1: continue
|
|
42
|
+
indices.append(index)
|
|
43
|
+
|
|
44
|
+
return indices
|
|
45
|
+
|
|
46
|
+
def get_best_dot(self, vec: glm.vec3) -> int:
|
|
47
|
+
"""
|
|
48
|
+
Returns the best triangle with the highest dot product with the vector from the geometric center to its AABB
|
|
49
|
+
"""
|
|
50
|
+
c = max(self.a, self.b, key=lambda x: glm.dot(x.geometric_center, vec))
|
|
51
|
+
if isinstance(c, NarrowAABB): return c.get_best_dot(vec)
|
|
52
|
+
return c.index
|
|
53
|
+
|
|
54
|
+
def get_all_aabbs(self, layer: int) -> list[tuple[glm.vec3, glm.vec3, int]]:
|
|
55
|
+
"""
|
|
56
|
+
Returns all AABBs, their extreme points, and their layer
|
|
57
|
+
"""
|
|
58
|
+
aabbs = [(self.top_right, self.bottom_left, layer)]
|
|
59
|
+
if isinstance(self.a, NarrowAABB): aabbs += self.a.get_all_aabbs(layer + 1)
|
|
60
|
+
else: aabbs.append((self.a.top_right, self.a.bottom_left, layer + 1))
|
|
61
|
+
if isinstance(self.b, NarrowAABB): aabbs += self.b.get_all_aabbs(layer + 1)
|
|
62
|
+
else: aabbs.append((self.b.top_right, self.b.bottom_left, layer + 1))
|
|
63
|
+
return aabbs
|
|
64
|
+
|
|
65
|
+
def get_tested_aabbs(self, point: glm.vec3, vec: glm.vec3, layer: int) -> list[tuple[glm.vec3, glm.vec3, int]]:
|
|
66
|
+
"""
|
|
67
|
+
Returns all AABBs, their extreme points, and their layer
|
|
68
|
+
"""
|
|
69
|
+
aabbs = [(self.top_right, self.bottom_left, layer)]
|
|
70
|
+
|
|
71
|
+
if isinstance(self.a, NarrowAABB):
|
|
72
|
+
|
|
73
|
+
aabbs += self.a.get_all_aabbs(layer + 1)
|
|
74
|
+
else: aabbs.append((self.a.top_right, self.a.bottom_left, layer + 1))
|
|
75
|
+
|
|
76
|
+
if isinstance(self.b, NarrowAABB):
|
|
77
|
+
|
|
78
|
+
aabbs += self.b.get_all_aabbs(layer + 1)
|
|
79
|
+
else: aabbs.append((self.b.top_right, self.b.bottom_left, layer + 1))
|
|
80
|
+
|
|
81
|
+
return aabbs
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import glm
|
|
2
|
+
from .narrow_aabb import NarrowAABB
|
|
3
|
+
from .narrow_primative import NarrowPrimative
|
|
4
|
+
from ..generic.abstract_bvh import AbstractAABB as BVH
|
|
5
|
+
from ..generic.meshes import get_extreme_points_np, get_aabb_surface_area
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class NarrowBVH(BVH):
|
|
9
|
+
root: NarrowAABB | NarrowPrimative
|
|
10
|
+
"""Root aabb used for the start of all collisions"""
|
|
11
|
+
primatives: list[NarrowPrimative]
|
|
12
|
+
"""All of the primatives in the BVH associated with triangles in the mesh"""
|
|
13
|
+
mesh: ...
|
|
14
|
+
"""Back reference to the parent mesh"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, mesh) -> None:
|
|
17
|
+
self.mesh = mesh
|
|
18
|
+
self.primatives = []
|
|
19
|
+
for index, triangle in enumerate(self.mesh.indices):
|
|
20
|
+
points = [self.mesh.points[t] for t in triangle] # TODO check np array accessing
|
|
21
|
+
top_right, bottom_left = get_extreme_points_np(points)
|
|
22
|
+
self.primatives.append(NarrowPrimative(top_right, bottom_left, index))
|
|
23
|
+
|
|
24
|
+
top_right = mesh.geometric_center + mesh.half_dimensions
|
|
25
|
+
bottom_left = mesh.geometric_center - mesh.half_dimensions
|
|
26
|
+
self.root = self.build_bvh(self.primatives, top_right, bottom_left)
|
|
27
|
+
|
|
28
|
+
def build_bvh(self, primatives: list[NarrowPrimative], top_right: glm.vec3, bottom_left: glm.vec3) -> NarrowAABB | NarrowPrimative:
|
|
29
|
+
"""
|
|
30
|
+
Creates a root node for the BVH with the given primatives and bounds
|
|
31
|
+
"""
|
|
32
|
+
best_cost = -1
|
|
33
|
+
best_split = primatives
|
|
34
|
+
best_aabb = []
|
|
35
|
+
count = len(primatives) // 2
|
|
36
|
+
|
|
37
|
+
# return primative if it is a leaf
|
|
38
|
+
if not count: return primatives[0]
|
|
39
|
+
|
|
40
|
+
for axis in range(3):
|
|
41
|
+
# sort primatives along axis and determine if it is lowest cost
|
|
42
|
+
primatives.sort(key=lambda p: p.geometric_center[axis])
|
|
43
|
+
aabb = self.calculate_primative_aabb(primatives[:count]) + self.calculate_primative_aabb(primatives[count:])
|
|
44
|
+
cost = get_aabb_surface_area(aabb[0], aabb[1]) + get_aabb_surface_area(aabb[2], aabb[3])
|
|
45
|
+
|
|
46
|
+
if best_cost < 0 or cost < best_cost:
|
|
47
|
+
best_cost = cost
|
|
48
|
+
best_split = list(primatives) # TODO ensure that this is a shallow copy
|
|
49
|
+
best_aabb = aabb
|
|
50
|
+
|
|
51
|
+
a = self.build_bvh(best_split[:count], best_aabb[0], best_aabb[1])
|
|
52
|
+
b = self.build_bvh(best_split[count:], best_aabb[2], best_aabb[3])
|
|
53
|
+
return NarrowAABB(top_right, bottom_left, a, b)
|
|
54
|
+
|
|
55
|
+
def calculate_primative_aabb(self, primatives: list[NarrowPrimative]) -> float:
|
|
56
|
+
"""
|
|
57
|
+
Computes the aabb surface area of the primatives
|
|
58
|
+
"""
|
|
59
|
+
points = set()
|
|
60
|
+
for primative in primatives:
|
|
61
|
+
points.update([tuple(self.mesh.points[t]) for t in self.mesh.indices[primative.index]])
|
|
62
|
+
return list(get_extreme_points_np(list(points)))
|
|
63
|
+
|
|
64
|
+
def get_possible_triangles(self, point: glm.vec3, vec: glm.vec3) -> list[int]:
|
|
65
|
+
"""
|
|
66
|
+
Determines the closest intersecting on the bvh
|
|
67
|
+
"""
|
|
68
|
+
if isinstance(self.root, NarrowAABB): return self.root.get_possible_triangles(point, vec)
|
|
69
|
+
index = self.root.is_possible_triangle(point, vec)
|
|
70
|
+
return [index] if index != -1 else []
|
|
71
|
+
|
|
72
|
+
def get_best_dot(self, vec: glm.vec3) -> int:
|
|
73
|
+
"""
|
|
74
|
+
Returns the best triangle with the highest dot product with the vector from the geometric center to its AABB
|
|
75
|
+
"""
|
|
76
|
+
if isinstance(self.root, NarrowAABB): return self.root.get_best_dot(vec)
|
|
77
|
+
return self.root.index
|
|
78
|
+
|
|
79
|
+
def get_all_aabbs(self) -> list[tuple[glm.vec3, glm.vec3, int]]:
|
|
80
|
+
"""
|
|
81
|
+
Returns all AABBs, their extreme points, and their layer
|
|
82
|
+
"""
|
|
83
|
+
if isinstance(self.root, NarrowAABB): return self.root.get_all_aabbs(0)
|
|
84
|
+
return [(self.root.top_right, self.root.bottom_left, 0)]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import glm
|
|
2
|
+
from ..generic.meshes import get_aabb_line_collision
|
|
3
|
+
|
|
4
|
+
class NarrowPrimative():
|
|
5
|
+
top_right: glm.vec3
|
|
6
|
+
"""The furthest positive corner of the AABB"""
|
|
7
|
+
bottom_left: glm.vec3
|
|
8
|
+
"""The furthest negative corner of the AABB"""
|
|
9
|
+
geometric_center: glm.vec3
|
|
10
|
+
"""The centroid of the primative"""
|
|
11
|
+
index: int
|
|
12
|
+
"""the index of the triangle in the mesh"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, top_right:glm.vec3, bottom_left:glm.vec3, index: int) -> None:
|
|
15
|
+
self.top_right = top_right
|
|
16
|
+
self.bottom_left = bottom_left
|
|
17
|
+
self.geometric_center = (self.top_right + self.bottom_left) / 2
|
|
18
|
+
self.index = index
|
|
19
|
+
|
|
20
|
+
def is_possible_triangle(self, point: glm.vec3, vec: glm.vec3) -> int:
|
|
21
|
+
"""
|
|
22
|
+
Determines if this triangle's AABB intersects with the line
|
|
23
|
+
"""
|
|
24
|
+
return self.index if get_aabb_line_collision(self.top_right, self.bottom_left, point, vec) else -1
|
|
File without changes
|