basilisk-engine 0.1.51__py3-none-any.whl → 0.1.53__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/__init__.py +27 -27
- basilisk/audio/sound.py +40 -40
- basilisk/bsk_assets/cube.obj +48 -48
- basilisk/collisions/broad/broad_aabb.py +102 -102
- basilisk/collisions/broad/broad_bvh.py +137 -137
- basilisk/collisions/collider.py +95 -95
- basilisk/collisions/collider_handler.py +225 -225
- basilisk/collisions/narrow/contact_manifold.py +95 -95
- basilisk/collisions/narrow/dataclasses.py +34 -34
- basilisk/collisions/narrow/deprecated.py +46 -46
- basilisk/collisions/narrow/epa.py +91 -91
- basilisk/collisions/narrow/gjk.py +66 -66
- basilisk/collisions/narrow/graham_scan.py +24 -24
- basilisk/collisions/narrow/helper.py +29 -29
- basilisk/collisions/narrow/line_intersections.py +106 -106
- basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
- basilisk/config.py +53 -53
- basilisk/draw/draw.py +100 -100
- basilisk/draw/draw_handler.py +181 -181
- basilisk/draw/font_renderer.py +28 -28
- basilisk/engine.py +168 -168
- basilisk/generic/abstract_bvh.py +15 -15
- basilisk/generic/abstract_custom.py +133 -133
- basilisk/generic/collisions.py +70 -70
- basilisk/generic/input_validation.py +82 -82
- basilisk/generic/math.py +17 -17
- basilisk/generic/matrices.py +35 -35
- basilisk/generic/meshes.py +72 -72
- basilisk/generic/quat.py +142 -142
- basilisk/generic/quat_methods.py +7 -7
- basilisk/generic/raycast_result.py +26 -26
- basilisk/generic/vec3.py +143 -143
- basilisk/input_output/IO_handler.py +91 -91
- basilisk/input_output/clock.py +49 -49
- basilisk/input_output/keys.py +43 -43
- basilisk/input_output/mouse.py +90 -90
- basilisk/input_output/path.py +14 -14
- basilisk/mesh/cube.py +33 -33
- basilisk/mesh/mesh.py +233 -233
- basilisk/mesh/mesh_from_data.py +150 -150
- basilisk/mesh/model.py +271 -271
- basilisk/mesh/narrow_aabb.py +89 -89
- basilisk/mesh/narrow_bvh.py +91 -91
- basilisk/mesh/narrow_primative.py +23 -23
- basilisk/nodes/helper.py +28 -28
- basilisk/nodes/node.py +709 -709
- basilisk/nodes/node_handler.py +107 -98
- basilisk/particles/particle_handler.py +69 -65
- basilisk/particles/particle_renderer.py +92 -93
- basilisk/physics/impulse.py +112 -112
- basilisk/physics/physics_body.py +43 -43
- basilisk/physics/physics_engine.py +35 -35
- basilisk/render/batch.py +103 -103
- basilisk/render/bloom.py +117 -117
- basilisk/render/camera.py +260 -260
- basilisk/render/chunk.py +113 -113
- basilisk/render/chunk_handler.py +167 -167
- basilisk/render/frame.py +130 -130
- basilisk/render/framebuffer.py +192 -192
- basilisk/render/image.py +128 -128
- basilisk/render/image_handler.py +120 -120
- basilisk/render/light.py +96 -96
- basilisk/render/light_handler.py +58 -58
- basilisk/render/material.py +232 -232
- basilisk/render/material_handler.py +133 -133
- basilisk/render/post_process.py +180 -180
- basilisk/render/shader.py +135 -135
- basilisk/render/shader_handler.py +109 -109
- basilisk/render/sky.py +119 -119
- basilisk/scene.py +295 -291
- basilisk/shaders/batch.frag +291 -291
- basilisk/shaders/batch.vert +117 -117
- basilisk/shaders/bloom_downsample.frag +23 -23
- basilisk/shaders/bloom_upsample.frag +33 -33
- basilisk/shaders/crt.frag +34 -34
- basilisk/shaders/draw.frag +27 -27
- basilisk/shaders/draw.vert +25 -25
- basilisk/shaders/filter.frag +22 -22
- basilisk/shaders/frame.frag +13 -13
- basilisk/shaders/frame.vert +13 -13
- basilisk/shaders/frame_hdr.frag +27 -27
- basilisk/shaders/geometry.frag +10 -10
- basilisk/shaders/geometry.vert +41 -41
- basilisk/shaders/normal.frag +62 -62
- basilisk/shaders/normal.vert +96 -96
- basilisk/shaders/particle.frag +81 -81
- basilisk/shaders/particle.vert +86 -86
- basilisk/shaders/sky.frag +23 -23
- basilisk/shaders/sky.vert +13 -13
- {basilisk_engine-0.1.51.dist-info → basilisk_engine-0.1.53.dist-info}/METADATA +82 -89
- basilisk_engine-0.1.53.dist-info/RECORD +110 -0
- {basilisk_engine-0.1.51.dist-info → basilisk_engine-0.1.53.dist-info}/WHEEL +1 -1
- basilisk_engine-0.1.51.dist-info/RECORD +0 -110
- {basilisk_engine-0.1.51.dist-info → basilisk_engine-0.1.53.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, generate_bvh: bool=True) -> 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) if generate_bvh else None
|
|
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, generate_bvh: bool=True) -> 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) if generate_bvh else None
|
|
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
|