basilisk-engine 0.1.34__py3-none-any.whl → 0.1.36__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.

Files changed (98) hide show
  1. basilisk/__init__.py +26 -26
  2. basilisk/audio/sound.py +27 -27
  3. basilisk/bsk_assets/cube.obj +48 -48
  4. basilisk/collisions/broad/broad_aabb.py +102 -102
  5. basilisk/collisions/broad/broad_bvh.py +137 -137
  6. basilisk/collisions/collider.py +95 -95
  7. basilisk/collisions/collider_handler.py +225 -224
  8. basilisk/collisions/narrow/contact_manifold.py +95 -95
  9. basilisk/collisions/narrow/dataclasses.py +34 -34
  10. basilisk/collisions/narrow/deprecated.py +46 -46
  11. basilisk/collisions/narrow/epa.py +91 -91
  12. basilisk/collisions/narrow/gjk.py +66 -66
  13. basilisk/collisions/narrow/graham_scan.py +24 -24
  14. basilisk/collisions/narrow/helper.py +29 -29
  15. basilisk/collisions/narrow/line_intersections.py +106 -106
  16. basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
  17. basilisk/config.py +54 -4
  18. basilisk/draw/draw.py +100 -100
  19. basilisk/draw/draw_handler.py +175 -175
  20. basilisk/draw/font_renderer.py +28 -28
  21. basilisk/engine.py +165 -165
  22. basilisk/generic/abstract_bvh.py +15 -15
  23. basilisk/generic/abstract_custom.py +133 -133
  24. basilisk/generic/collisions.py +70 -70
  25. basilisk/generic/input_validation.py +82 -74
  26. basilisk/generic/math.py +6 -6
  27. basilisk/generic/matrices.py +35 -35
  28. basilisk/generic/meshes.py +72 -72
  29. basilisk/generic/quat.py +142 -142
  30. basilisk/generic/quat_methods.py +7 -7
  31. basilisk/generic/raycast_result.py +26 -26
  32. basilisk/generic/vec3.py +143 -143
  33. basilisk/input_output/IO_handler.py +91 -91
  34. basilisk/input_output/clock.py +49 -49
  35. basilisk/input_output/keys.py +43 -43
  36. basilisk/input_output/mouse.py +90 -89
  37. basilisk/input_output/path.py +14 -14
  38. basilisk/mesh/cube.py +33 -33
  39. basilisk/mesh/mesh.py +233 -233
  40. basilisk/mesh/mesh_from_data.py +150 -150
  41. basilisk/mesh/model.py +271 -271
  42. basilisk/mesh/narrow_aabb.py +89 -89
  43. basilisk/mesh/narrow_bvh.py +91 -91
  44. basilisk/mesh/narrow_primative.py +23 -23
  45. basilisk/nodes/helper.py +28 -28
  46. basilisk/nodes/node.py +704 -695
  47. basilisk/nodes/node_handler.py +97 -97
  48. basilisk/particles/particle_handler.py +64 -64
  49. basilisk/particles/particle_renderer.py +92 -92
  50. basilisk/physics/impulse.py +112 -112
  51. basilisk/physics/physics_body.py +43 -43
  52. basilisk/physics/physics_engine.py +35 -35
  53. basilisk/render/batch.py +103 -103
  54. basilisk/render/bloom.py +108 -0
  55. basilisk/render/camera.py +260 -260
  56. basilisk/render/chunk.py +108 -106
  57. basilisk/render/chunk_handler.py +167 -165
  58. basilisk/render/frame.py +107 -95
  59. basilisk/render/framebuffer.py +203 -192
  60. basilisk/render/image.py +120 -120
  61. basilisk/render/image_handler.py +120 -120
  62. basilisk/render/light.py +96 -96
  63. basilisk/render/light_handler.py +58 -58
  64. basilisk/render/material.py +232 -221
  65. basilisk/render/material_handler.py +133 -133
  66. basilisk/render/post_process.py +139 -139
  67. basilisk/render/shader.py +134 -134
  68. basilisk/render/shader_handler.py +85 -83
  69. basilisk/render/sky.py +120 -120
  70. basilisk/scene.py +289 -289
  71. basilisk/shaders/batch.frag +289 -276
  72. basilisk/shaders/batch.vert +117 -115
  73. basilisk/shaders/bloom_downsample.frag +43 -0
  74. basilisk/shaders/bloom_frame.frag +25 -0
  75. basilisk/shaders/bloom_upsample.frag +34 -0
  76. basilisk/shaders/crt.frag +31 -31
  77. basilisk/shaders/draw.frag +25 -22
  78. basilisk/shaders/draw.vert +25 -25
  79. basilisk/shaders/filter.frag +22 -22
  80. basilisk/shaders/frame.frag +12 -12
  81. basilisk/shaders/frame.vert +13 -13
  82. basilisk/shaders/geometry.frag +8 -8
  83. basilisk/shaders/geometry.vert +41 -41
  84. basilisk/shaders/normal.frag +59 -59
  85. basilisk/shaders/normal.vert +96 -96
  86. basilisk/shaders/particle.frag +71 -71
  87. basilisk/shaders/particle.vert +84 -84
  88. basilisk/shaders/sky.frag +23 -9
  89. basilisk/shaders/sky.vert +13 -13
  90. basilisk_engine-0.1.36.dist-info/METADATA +89 -0
  91. basilisk_engine-0.1.36.dist-info/RECORD +110 -0
  92. {basilisk_engine-0.1.34.dist-info → basilisk_engine-0.1.36.dist-info}/WHEEL +1 -1
  93. basilisk/input/__init__.py +0 -0
  94. basilisk/input/mouse.py +0 -62
  95. basilisk/input/path.py +0 -14
  96. basilisk_engine-0.1.34.dist-info/METADATA +0 -45
  97. basilisk_engine-0.1.34.dist-info/RECORD +0 -109
  98. {basilisk_engine-0.1.34.dist-info → basilisk_engine-0.1.36.dist-info}/top_level.txt +0 -0
basilisk/mesh/mesh.py CHANGED
@@ -1,234 +1,234 @@
1
- import numpy as np
2
- import glm
3
- import os
4
- # from pyobjloader import load_model
5
- from .model import load_model
6
- from .narrow_bvh import NarrowBVH
7
- from ..generic.matrices import compute_inertia_moment, compute_inertia_product
8
- from ..generic.meshes import get_extreme_points_np, moller_trumbore
9
- from .mesh_from_data import from_data
10
- import time
11
-
12
-
13
- class Mesh():
14
- data: np.ndarray
15
- """The mesh vertex data stored as a 4-byte float numpy array. Format will be [position.xyz, uv.xy, normal.xyz, tangent.xyz, bitangent.xyz]"""
16
- points: np.ndarray
17
- """All the unique points of the mesh given by the model file"""
18
- indices: np.ndarray
19
- """Indices of the triangles corresponding to the points array"""
20
- bvh: any
21
- """Data structure allowing the access of closest points more efficiently"""
22
- volume: float
23
- """The volume of the unscaled mesh"""
24
- geometric_center: glm.vec3
25
- """The geometric center of the mesh"""
26
- center_of_mass: glm.vec3
27
- """The center of mass of the mesh calculated from the inertia tensor algorithm"""
28
- half_dimensions: glm.vec3
29
- """The aligned half dimensions to the untransformed mesh"""
30
- bvh: NarrowBVH
31
- """BVH for accessing triangle intersections with a line"""
32
-
33
- def __init__(self, data: str | os.PathLike | np.ndarray, custom_format:bool=False) -> None:
34
- """
35
- Mesh object containing all the data needed to render an object and perform physics/collisions on it
36
- Args:
37
- data: str
38
- path to the .obj file of the model or an array of the mesh data
39
- custom_format: bool
40
- makes expected changes to the given data if false. Leaves data as given if true
41
- """
42
-
43
- # Verify the path type
44
- if isinstance(data, str) or isinstance(data, os.PathLike): # Load the model from file
45
- model = load_model(data, calculate_tangents=True)
46
-
47
- # Get the vertex data
48
- if len(model.vertex_data[0]) == 8:
49
- self.data = model.vertex_data.copy()
50
- else:
51
- self.data = np.zeros(shape=(len(model.vertex_data), 8))
52
- self.data[:,:3] = model.vertex_data[:,:3]
53
- self.data[:,5:] = model.vertex_data[:,3:]
54
-
55
- # Get tangent data
56
- if len(model.tangent_data[0]) == 6:
57
- self.data = np.hstack([self.data, model.tangent_data])
58
- else:
59
- tangents = np.zeros(shape=(len(self.data), 6))
60
- tangents[:,:] += [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
61
- self.data = np.hstack([self.data, tangents])
62
-
63
- elif isinstance(data, np.ndarray):
64
- model = from_data(data, custom_format)
65
- self.data = model.vertex_data
66
-
67
- else: # Invalid data type
68
- raise TypeError(f'Invalid path type: {type(data)}. Expected a string or os.path')
69
-
70
- # Mesh points and triangles used for physics/collisions
71
- self.points = model.vertex_points.copy()
72
- self.indices = model.point_indices.copy()
73
-
74
- self.hash = hash(str(self.data))
75
- self.custom = custom_format
76
-
77
- # generate edges from faces
78
- edges = [set() for _ in range(len(self.points))]
79
- for face in self.indices:
80
-
81
- # add points to the dictionary since each point on a face is adjacent to the others
82
- for i in range(3): edges[face[i]].update([int(face[(i + 1) % 3]), int(face[(i + 2) % 3])])
83
- self.edges = [tuple(adjacent) for adjacent in edges]
84
-
85
- # Model will no longer be used
86
- del model
87
-
88
- # generate geometric data
89
- maximum, minimum = get_extreme_points_np(self.points)
90
- self.geometric_center = (glm.vec3(maximum) + glm.vec3(minimum)) / 2
91
- self.half_dimensions = maximum - self.geometric_center
92
-
93
- # volume and center of mass
94
- self.volume = 0
95
- self.center_of_mass = glm.vec3(0.0)
96
- for triangle in self.indices:
97
- pts = [glm.vec3(self.points[t]) for t in triangle]
98
- det_j = glm.dot(pts[0], glm.cross(pts[1], pts[2]))
99
- tet_volume = det_j / 6
100
- self.volume += tet_volume
101
- self.center_of_mass += tet_volume * (pts[0] + pts[1] + pts[2]) / 4
102
- self.center_of_mass /= self.volume
103
-
104
- # data structrues
105
- self.bvh = NarrowBVH(self)
106
-
107
- def get_inertia_tensor(self, scale: glm.vec3) -> glm.mat3x3:
108
- """
109
- Gets the inertia tensor of the mesh with the given scale and mass 1
110
- """
111
- # scale variables
112
- center_of_mass = self.center_of_mass * scale
113
- volume = self.volume * scale.x * scale.y * scale.z
114
-
115
- # uses density = 1 to calculate variables, should be the same for mass = 1 since they are only spatial variables
116
- points = self.points.copy()
117
- points[:, 0] *= scale.x
118
- points[:, 1] *= scale.y
119
- points[:, 2] *= scale.z
120
-
121
- ia = ib = ic = iap = ibp = icp = 0
122
- for triangle in self.indices:
123
- pts = [points[t] for t in triangle]
124
- det_j = glm.dot(pts[0], glm.cross(pts[1], pts[2]))
125
-
126
- ia += det_j * (compute_inertia_moment(pts, 1) + compute_inertia_moment(pts, 2))
127
- ib += det_j * (compute_inertia_moment(pts, 0) + compute_inertia_moment(pts, 2))
128
- ic += det_j * (compute_inertia_moment(pts, 0) + compute_inertia_moment(pts, 1))
129
- iap += det_j * compute_inertia_product(pts, 1, 2)
130
- ibp += det_j * compute_inertia_product(pts, 0, 1)
131
- icp += det_j * compute_inertia_product(pts, 0, 2)
132
-
133
- # since tensor was calc with density = 1. we say mass = density / volume = 1 / volume
134
- ia = ia / volume / 60 - volume * (center_of_mass[1] ** 2 + center_of_mass[2] ** 2)
135
- ib = ib / volume / 60 - volume * (center_of_mass[0] ** 2 + center_of_mass[2] ** 2)
136
- ic = ic / volume / 60 - volume * (center_of_mass[0] ** 2 + center_of_mass[1] ** 2)
137
- iap = iap / volume / 120 - volume * center_of_mass[1] * center_of_mass[2]
138
- ibp = ibp / volume / 120 - volume * center_of_mass[0] * center_of_mass[1]
139
- icp = icp / volume / 120 - volume * center_of_mass[0] * center_of_mass[2]
140
-
141
- return glm.mat3x3(
142
- ia, -ibp, -icp,
143
- -ibp, ib, -iap,
144
- -icp, -iap, ic
145
- )
146
-
147
- # return glm.mat3x3(
148
- # ic, -iap, -icp,
149
- # -iap, ib, -ibp,
150
- # -ibp, -iap, ia
151
- # )
152
-
153
- def get_best_triangle(self, point: glm.vec3, vec: glm.vec3) -> int:
154
- """
155
- Gets the triangle with the closest intersection, -1 if no intersection is found
156
- """
157
- indices = self.bvh.get_possible_triangles(point, vec)
158
- best_distance = -1
159
- best_index = -1
160
-
161
- point = glm.vec3(point)
162
- vec = glm.vec3(vec)
163
-
164
- for triangle in indices:
165
-
166
- # check if triangle intersects
167
- intersection = moller_trumbore(point, vec, [self.points[t] for t in self.indices[triangle]])
168
- if not intersection: continue
169
-
170
- # check if triangle is on correct side of line
171
- difference = intersection - self.geometric_center
172
- if glm.dot(difference, vec) < 0: continue
173
-
174
- # determine best distance
175
- distance = glm.length(difference)
176
- if best_distance < 0 or distance < best_distance:
177
- best_distance = distance
178
- best_index = triangle
179
-
180
- return best_index
181
-
182
- def get_best_dot(self, vec: glm.vec3) -> int:
183
- """
184
- Gets the point with the highest normalized dot product to the given vector
185
- """
186
- triangle = self.bvh.get_best_dot(vec)
187
- if triangle == -1: return None
188
- index = max(self.indices[triangle], key=lambda t: glm.dot(glm.normalize(self.points[t]), vec))
189
- return index
190
-
191
- def get_best_dot_hill_climbing(self, vec: glm.vec3) -> int:
192
- """
193
- 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.
194
- """
195
- best_index = 0
196
- best_dot = glm.dot(self.points[best_index], vec)
197
-
198
- while True:
199
-
200
- best_changed = False
201
- for index in self.edges[best_index]:
202
-
203
- dot = glm.dot(self.points[index], vec)
204
- if dot > best_dot:
205
- best_dot = dot
206
- best_changed = True
207
- best_index = index
208
-
209
- if not best_changed: break
210
-
211
- return best_index
212
-
213
- def get_line_collided(self, position: glm.vec3, forward: glm.vec3) -> list[tuple[int, int, int]]:
214
- """
215
- Determines which triangles are intersecting with the given line segment. Returns the indices of the triangle contained in the mesh points list
216
- """
217
- return self.bvh.get_line_collided(position, forward)
218
-
219
- def __repr__(self) -> str:
220
- size = (self.data.nbytes + self.points.nbytes + self.indices.nbytes) / 1024 / 1024
221
- return f'<Basilisk Mesh | {len(self.data)} vertices, {size:.2} mb>'
222
-
223
- @property
224
- def top_right(self): return self.bvh.root.top_right
225
- @property
226
- def bottom_left(self): return self.bvh.root.bottom_left
227
- @property
228
- def aabb_points(self):
229
- x1, y1, z1 = self.top_right
230
- x2, y2, z2 = self.bottom_left
231
- return [glm.vec3(x, y, z) for z in (z1, z2) for y in (y1, y2) for x in (x1, x2)]
232
-
233
- def __hash__(self):
1
+ import numpy as np
2
+ import glm
3
+ import os
4
+ # from pyobjloader import load_model
5
+ from .model import load_model
6
+ from .narrow_bvh import NarrowBVH
7
+ from ..generic.matrices import compute_inertia_moment, compute_inertia_product
8
+ from ..generic.meshes import get_extreme_points_np, moller_trumbore
9
+ from .mesh_from_data import from_data
10
+ import time
11
+
12
+
13
+ class Mesh():
14
+ data: np.ndarray
15
+ """The mesh vertex data stored as a 4-byte float numpy array. Format will be [position.xyz, uv.xy, normal.xyz, tangent.xyz, bitangent.xyz]"""
16
+ points: np.ndarray
17
+ """All the unique points of the mesh given by the model file"""
18
+ indices: np.ndarray
19
+ """Indices of the triangles corresponding to the points array"""
20
+ bvh: any
21
+ """Data structure allowing the access of closest points more efficiently"""
22
+ volume: float
23
+ """The volume of the unscaled mesh"""
24
+ geometric_center: glm.vec3
25
+ """The geometric center of the mesh"""
26
+ center_of_mass: glm.vec3
27
+ """The center of mass of the mesh calculated from the inertia tensor algorithm"""
28
+ half_dimensions: glm.vec3
29
+ """The aligned half dimensions to the untransformed mesh"""
30
+ bvh: NarrowBVH
31
+ """BVH for accessing triangle intersections with a line"""
32
+
33
+ def __init__(self, data: str | os.PathLike | np.ndarray, custom_format:bool=False) -> None:
34
+ """
35
+ Mesh object containing all the data needed to render an object and perform physics/collisions on it
36
+ Args:
37
+ data: str
38
+ path to the .obj file of the model or an array of the mesh data
39
+ custom_format: bool
40
+ makes expected changes to the given data if false. Leaves data as given if true
41
+ """
42
+
43
+ # Verify the path type
44
+ if isinstance(data, str) or isinstance(data, os.PathLike): # Load the model from file
45
+ model = load_model(data, calculate_tangents=True)
46
+
47
+ # Get the vertex data
48
+ if len(model.vertex_data[0]) == 8:
49
+ self.data = model.vertex_data.copy()
50
+ else:
51
+ self.data = np.zeros(shape=(len(model.vertex_data), 8))
52
+ self.data[:,:3] = model.vertex_data[:,:3]
53
+ self.data[:,5:] = model.vertex_data[:,3:]
54
+
55
+ # Get tangent data
56
+ if len(model.tangent_data[0]) == 6:
57
+ self.data = np.hstack([self.data, model.tangent_data])
58
+ else:
59
+ tangents = np.zeros(shape=(len(self.data), 6))
60
+ tangents[:,:] += [1.0, 0.0, 0.0, 0.0, 1.0, 0.0]
61
+ self.data = np.hstack([self.data, tangents])
62
+
63
+ elif isinstance(data, np.ndarray):
64
+ model = from_data(data, custom_format)
65
+ self.data = model.vertex_data
66
+
67
+ else: # Invalid data type
68
+ raise TypeError(f'Invalid path type: {type(data)}. Expected a string or os.path')
69
+
70
+ # Mesh points and triangles used for physics/collisions
71
+ self.points = model.vertex_points.copy()
72
+ self.indices = model.point_indices.copy()
73
+
74
+ self.hash = hash(str(self.data))
75
+ self.custom = custom_format
76
+
77
+ # generate edges from faces
78
+ edges = [set() for _ in range(len(self.points))]
79
+ for face in self.indices:
80
+
81
+ # add points to the dictionary since each point on a face is adjacent to the others
82
+ for i in range(3): edges[face[i]].update([int(face[(i + 1) % 3]), int(face[(i + 2) % 3])])
83
+ self.edges = [tuple(adjacent) for adjacent in edges]
84
+
85
+ # Model will no longer be used
86
+ del model
87
+
88
+ # generate geometric data
89
+ maximum, minimum = get_extreme_points_np(self.points)
90
+ self.geometric_center = (glm.vec3(maximum) + glm.vec3(minimum)) / 2
91
+ self.half_dimensions = maximum - self.geometric_center
92
+
93
+ # volume and center of mass
94
+ self.volume = 0
95
+ self.center_of_mass = glm.vec3(0.0)
96
+ for triangle in self.indices:
97
+ pts = [glm.vec3(self.points[t]) for t in triangle]
98
+ det_j = glm.dot(pts[0], glm.cross(pts[1], pts[2]))
99
+ tet_volume = det_j / 6
100
+ self.volume += tet_volume
101
+ self.center_of_mass += tet_volume * (pts[0] + pts[1] + pts[2]) / 4
102
+ self.center_of_mass /= self.volume
103
+
104
+ # data structrues
105
+ self.bvh = NarrowBVH(self)
106
+
107
+ def get_inertia_tensor(self, scale: glm.vec3) -> glm.mat3x3:
108
+ """
109
+ Gets the inertia tensor of the mesh with the given scale and mass 1
110
+ """
111
+ # scale variables
112
+ center_of_mass = self.center_of_mass * scale
113
+ volume = self.volume * scale.x * scale.y * scale.z
114
+
115
+ # uses density = 1 to calculate variables, should be the same for mass = 1 since they are only spatial variables
116
+ points = self.points.copy()
117
+ points[:, 0] *= scale.x
118
+ points[:, 1] *= scale.y
119
+ points[:, 2] *= scale.z
120
+
121
+ ia = ib = ic = iap = ibp = icp = 0
122
+ for triangle in self.indices:
123
+ pts = [points[t] for t in triangle]
124
+ det_j = glm.dot(pts[0], glm.cross(pts[1], pts[2]))
125
+
126
+ ia += det_j * (compute_inertia_moment(pts, 1) + compute_inertia_moment(pts, 2))
127
+ ib += det_j * (compute_inertia_moment(pts, 0) + compute_inertia_moment(pts, 2))
128
+ ic += det_j * (compute_inertia_moment(pts, 0) + compute_inertia_moment(pts, 1))
129
+ iap += det_j * compute_inertia_product(pts, 1, 2)
130
+ ibp += det_j * compute_inertia_product(pts, 0, 1)
131
+ icp += det_j * compute_inertia_product(pts, 0, 2)
132
+
133
+ # since tensor was calc with density = 1. we say mass = density / volume = 1 / volume
134
+ ia = ia / volume / 60 - volume * (center_of_mass[1] ** 2 + center_of_mass[2] ** 2)
135
+ ib = ib / volume / 60 - volume * (center_of_mass[0] ** 2 + center_of_mass[2] ** 2)
136
+ ic = ic / volume / 60 - volume * (center_of_mass[0] ** 2 + center_of_mass[1] ** 2)
137
+ iap = iap / volume / 120 - volume * center_of_mass[1] * center_of_mass[2]
138
+ ibp = ibp / volume / 120 - volume * center_of_mass[0] * center_of_mass[1]
139
+ icp = icp / volume / 120 - volume * center_of_mass[0] * center_of_mass[2]
140
+
141
+ return glm.mat3x3(
142
+ ia, -ibp, -icp,
143
+ -ibp, ib, -iap,
144
+ -icp, -iap, ic
145
+ )
146
+
147
+ # return glm.mat3x3(
148
+ # ic, -iap, -icp,
149
+ # -iap, ib, -ibp,
150
+ # -ibp, -iap, ia
151
+ # )
152
+
153
+ def get_best_triangle(self, point: glm.vec3, vec: glm.vec3) -> int:
154
+ """
155
+ Gets the triangle with the closest intersection, -1 if no intersection is found
156
+ """
157
+ indices = self.bvh.get_possible_triangles(point, vec)
158
+ best_distance = -1
159
+ best_index = -1
160
+
161
+ point = glm.vec3(point)
162
+ vec = glm.vec3(vec)
163
+
164
+ for triangle in indices:
165
+
166
+ # check if triangle intersects
167
+ intersection = moller_trumbore(point, vec, [self.points[t] for t in self.indices[triangle]])
168
+ if not intersection: continue
169
+
170
+ # check if triangle is on correct side of line
171
+ difference = intersection - self.geometric_center
172
+ if glm.dot(difference, vec) < 0: continue
173
+
174
+ # determine best distance
175
+ distance = glm.length(difference)
176
+ if best_distance < 0 or distance < best_distance:
177
+ best_distance = distance
178
+ best_index = triangle
179
+
180
+ return best_index
181
+
182
+ def get_best_dot(self, vec: glm.vec3) -> int:
183
+ """
184
+ Gets the point with the highest normalized dot product to the given vector
185
+ """
186
+ triangle = self.bvh.get_best_dot(vec)
187
+ if triangle == -1: return None
188
+ index = max(self.indices[triangle], key=lambda t: glm.dot(glm.normalize(self.points[t]), vec))
189
+ return index
190
+
191
+ def get_best_dot_hill_climbing(self, vec: glm.vec3) -> int:
192
+ """
193
+ 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.
194
+ """
195
+ best_index = 0
196
+ best_dot = glm.dot(self.points[best_index], vec)
197
+
198
+ while True:
199
+
200
+ best_changed = False
201
+ for index in self.edges[best_index]:
202
+
203
+ dot = glm.dot(self.points[index], vec)
204
+ if dot > best_dot:
205
+ best_dot = dot
206
+ best_changed = True
207
+ best_index = index
208
+
209
+ if not best_changed: break
210
+
211
+ return best_index
212
+
213
+ def get_line_collided(self, position: glm.vec3, forward: glm.vec3) -> list[tuple[int, int, int]]:
214
+ """
215
+ Determines which triangles are intersecting with the given line segment. Returns the indices of the triangle contained in the mesh points list
216
+ """
217
+ return self.bvh.get_line_collided(position, forward)
218
+
219
+ def __repr__(self) -> str:
220
+ size = (self.data.nbytes + self.points.nbytes + self.indices.nbytes) / 1024 / 1024
221
+ return f'<Basilisk Mesh | {len(self.data)} vertices, {size:.2} mb>'
222
+
223
+ @property
224
+ def top_right(self): return self.bvh.root.top_right
225
+ @property
226
+ def bottom_left(self): return self.bvh.root.bottom_left
227
+ @property
228
+ def aabb_points(self):
229
+ x1, y1, z1 = self.top_right
230
+ x2, y2, z2 = self.bottom_left
231
+ return [glm.vec3(x, y, z) for z in (z1, z2) for y in (y1, y2) for x in (x1, x2)]
232
+
233
+ def __hash__(self):
234
234
  return self.hash