basilisk-engine 0.0.9__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/__init__.py +3 -1
- basilisk/collisions/broad/broad_aabb.py +8 -1
- basilisk/collisions/broad/broad_bvh.py +38 -2
- basilisk/collisions/collider.py +20 -10
- basilisk/collisions/collider_handler.py +97 -32
- basilisk/collisions/narrow/contact_manifold.py +91 -0
- basilisk/collisions/narrow/dataclasses.py +27 -0
- basilisk/collisions/narrow/deprecated.py +47 -0
- basilisk/collisions/narrow/epa.py +21 -15
- basilisk/collisions/narrow/gjk.py +15 -14
- basilisk/collisions/narrow/graham_scan.py +25 -0
- basilisk/collisions/narrow/helper.py +14 -7
- basilisk/collisions/narrow/line_intersections.py +107 -0
- basilisk/collisions/narrow/sutherland_hodgman.py +76 -0
- basilisk/draw/draw_handler.py +7 -5
- basilisk/engine.py +28 -6
- basilisk/generic/abstract_custom.py +134 -0
- basilisk/generic/collisions.py +47 -2
- basilisk/generic/quat.py +84 -65
- basilisk/generic/vec3.py +99 -67
- basilisk/input/mouse.py +3 -3
- 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 +211 -101
- basilisk/nodes/node_handler.py +58 -33
- basilisk/particles/__init__.py +0 -0
- basilisk/particles/particle_handler.py +55 -0
- basilisk/particles/particle_renderer.py +87 -0
- basilisk/physics/impulse.py +113 -0
- basilisk/physics/physics_body.py +10 -2
- basilisk/physics/physics_engine.py +2 -3
- basilisk/render/batch.py +3 -1
- basilisk/render/camera.py +35 -1
- basilisk/render/chunk.py +19 -4
- basilisk/render/chunk_handler.py +39 -23
- basilisk/render/image.py +1 -1
- basilisk/render/image_handler.py +17 -14
- basilisk/render/light_handler.py +16 -11
- basilisk/render/material.py +38 -14
- basilisk/render/material_handler.py +31 -18
- basilisk/render/shader.py +110 -0
- basilisk/render/shader_handler.py +20 -35
- basilisk/render/sky.py +8 -5
- basilisk/scene.py +116 -33
- basilisk/shaders/batch.frag +40 -11
- basilisk/shaders/batch.vert +14 -7
- basilisk/shaders/geometry.frag +9 -0
- basilisk/shaders/geometry.vert +42 -0
- basilisk/shaders/normal.frag +60 -0
- basilisk/shaders/normal.vert +97 -0
- basilisk/shaders/particle.frag +72 -0
- basilisk/shaders/particle.vert +85 -0
- {basilisk_engine-0.0.9.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.0.9.dist-info/RECORD +0 -78
- {basilisk_engine-0.0.9.dist-info → basilisk_engine-0.1.1.dist-info}/WHEEL +0 -0
- {basilisk_engine-0.0.9.dist-info → basilisk_engine-0.1.1.dist-info}/top_level.txt +0 -0
basilisk/generic/quat.py
CHANGED
|
@@ -1,81 +1,86 @@
|
|
|
1
1
|
import glm
|
|
2
2
|
import numpy as np
|
|
3
|
+
from .abstract_custom import Custom
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
class Quat():
|
|
6
|
+
class Quat(Custom):
|
|
6
7
|
def __init__(self, *args, callback=None):
|
|
7
8
|
self.callback = callback
|
|
8
|
-
self.
|
|
9
|
-
|
|
10
|
-
def normalize(self):
|
|
11
|
-
"""
|
|
12
|
-
Inplace normalizes the vector
|
|
13
|
-
"""
|
|
14
|
-
self.data = glm.normalize(self.data)
|
|
9
|
+
self.prev_data = glm.quat(1, 0, 0, 0)
|
|
15
10
|
|
|
16
|
-
def set_data(self, *args):
|
|
17
|
-
"""
|
|
18
|
-
Sets the internal vector inplace
|
|
19
|
-
"""
|
|
20
|
-
# overload constructor TODO nvernest this, definitely possible
|
|
21
11
|
if len(args) == 1:
|
|
12
|
+
|
|
22
13
|
if isinstance(args[0], Quat):
|
|
23
14
|
self.data = glm.quat(args[0].data)
|
|
15
|
+
self.prev_data = glm.quat(args[0].prev_data)
|
|
24
16
|
self.callback = args[0].callback
|
|
25
|
-
|
|
17
|
+
|
|
18
|
+
elif isinstance(args[0], glm.quat):
|
|
19
|
+
self.data = args[0]
|
|
20
|
+
|
|
26
21
|
elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
|
|
27
|
-
|
|
22
|
+
assert 2 < len(args[0]) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(args[0])}'
|
|
28
23
|
self.data = glm.quat(args[0])
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
|
|
25
|
+
else:
|
|
26
|
+
try: self.data = glm.quat(args[0])
|
|
27
|
+
except: raise ValueError(f'Quat: Unexpected incoming quaternion type {args[0]}')
|
|
28
|
+
|
|
29
|
+
elif 2 < len(args) < 5: self.data = glm.quat(*args)
|
|
30
|
+
else: raise ValueError(f'Quat: Expected either a vector, quaternion, or 3 or 4 numbers, got {len(args)} values')
|
|
31
|
+
|
|
32
|
+
def apply_function(self, other, func, func_name):
|
|
33
|
+
quat = glm.quat(self.data)
|
|
34
|
+
|
|
35
|
+
if isinstance(other, (glm.vec3, glm.quat)):
|
|
36
|
+
quat = func(quat, other)
|
|
37
|
+
|
|
38
|
+
elif isinstance(other, (tuple, list, np.ndarray)):
|
|
39
|
+
assert 2 < len(other) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(other)}'
|
|
40
|
+
quat = func(quat, other)
|
|
41
|
+
|
|
42
|
+
elif isinstance(other, Custom):
|
|
43
|
+
quat = func(quat, other.data)
|
|
44
|
+
|
|
45
|
+
else:
|
|
46
|
+
try: quat = func(quat, other)
|
|
47
|
+
except: raise ValueError(f'Quat: Not an accepted type for {func_name}, got {type(other)}')
|
|
48
|
+
return Quat(quat)
|
|
32
49
|
|
|
33
|
-
#
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
37
|
-
if len(other) != 4 and len(other) != 3: raise ValueError(f'Quat: Number of added values must be 3 or 4, got {len(other)}')
|
|
38
|
-
self.data += other
|
|
39
|
-
elif isinstance(other, Quat): self.data += other.data
|
|
40
|
-
else: raise ValueError(f'Quat: Not an accepted type for addition, got {type(other)}')
|
|
41
|
-
return self
|
|
42
|
-
|
|
43
|
-
def __isub__(self, other):
|
|
44
|
-
if isinstance(other, glm.quat): self.data -= other
|
|
45
|
-
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
46
|
-
if len(other) != 4 and len(other) != 3: raise ValueError(f'Quat: Number of added values must be 3 or 4, got {len(other)}')
|
|
47
|
-
self.data -= other
|
|
48
|
-
elif isinstance(other, Quat): self.data -= other.data
|
|
49
|
-
else: raise ValueError(f'Quat: Not an accepted type for addition, got {type(other)}')
|
|
50
|
-
return self
|
|
50
|
+
# unary operators
|
|
51
|
+
def __neg__(self):
|
|
52
|
+
return Quat(-self.data, callback=self.callback)
|
|
51
53
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
self.data *= other
|
|
55
|
-
return self
|
|
54
|
+
def __abs__(self):
|
|
55
|
+
return Quat(abs(self.data), callback=self.callback)
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
# TODO add checks for number types
|
|
59
|
-
self.data /= other
|
|
60
|
-
return self
|
|
61
|
-
|
|
62
|
-
def __ifloordiv__(self, other):
|
|
63
|
-
# TODO add checks for number types
|
|
64
|
-
self.data //= other
|
|
65
|
-
return self
|
|
66
|
-
|
|
67
|
-
# override [_] accessor
|
|
57
|
+
# accessor functions
|
|
68
58
|
def __getitem__(self, index):
|
|
69
|
-
|
|
70
|
-
|
|
59
|
+
assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
|
|
60
|
+
assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
|
|
71
61
|
return self.data[index]
|
|
72
62
|
|
|
73
63
|
def __setitem__(self, index, value):
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
|
|
65
|
+
assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
|
|
76
66
|
try: self.data[index] = value
|
|
77
67
|
except: raise ValueError(f'Quat: Invalid element type, got {type(value)}')
|
|
78
68
|
|
|
69
|
+
def __delitem__(self, index): # cannot delete an index from a quaternion, so we set the value to zero instead
|
|
70
|
+
assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
|
|
71
|
+
assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
|
|
72
|
+
self.data[index] = 0
|
|
73
|
+
|
|
74
|
+
def __len__(self):
|
|
75
|
+
return 4
|
|
76
|
+
|
|
77
|
+
def __iter__(self):
|
|
78
|
+
return iter(self.data)
|
|
79
|
+
|
|
80
|
+
def __contains__(self, item):
|
|
81
|
+
return item in self.data
|
|
82
|
+
|
|
83
|
+
# override str operators
|
|
79
84
|
def __repr__(self):
|
|
80
85
|
return str(self.data)
|
|
81
86
|
|
|
@@ -94,26 +99,40 @@ class Quat():
|
|
|
94
99
|
def z(self): return self.data.z
|
|
95
100
|
|
|
96
101
|
@data.setter
|
|
97
|
-
def data(self, value: glm.quat):
|
|
98
|
-
self._data = value
|
|
99
|
-
|
|
102
|
+
def data(self, value: glm.vec3 | glm.quat):
|
|
103
|
+
self._data = glm.quat(value)
|
|
104
|
+
cur = self._data
|
|
105
|
+
prev = self.prev_data
|
|
106
|
+
thresh = 1e-6
|
|
107
|
+
|
|
108
|
+
if self.callback and (abs(cur.w - prev.w) > thresh or abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh):
|
|
109
|
+
self.prev_data = glm.quat(self._data)
|
|
110
|
+
self.callback()
|
|
100
111
|
|
|
101
112
|
@w.setter
|
|
102
113
|
def w(self, value):
|
|
103
114
|
self.data.w = value
|
|
104
|
-
if self.callback
|
|
115
|
+
if self.callback and abs(value - self.prev_data.w) > 1e-6:
|
|
116
|
+
self.prev_data.w = value
|
|
117
|
+
self.callback()
|
|
105
118
|
|
|
106
119
|
@x.setter
|
|
107
120
|
def x(self, value):
|
|
108
|
-
self.
|
|
109
|
-
if self.callback
|
|
121
|
+
self._data.x = value
|
|
122
|
+
if self.callback and abs(value - self.prev_data.x) > 1e-6:
|
|
123
|
+
self.prev_data.x = value
|
|
124
|
+
self.callback()
|
|
110
125
|
|
|
111
126
|
@y.setter
|
|
112
127
|
def y(self, value):
|
|
113
|
-
self.
|
|
114
|
-
if self.callback
|
|
128
|
+
self._data.y = value
|
|
129
|
+
if self.callback and abs(value - self.prev_data.y) > 1e-6:
|
|
130
|
+
self.prev_data.y = value
|
|
131
|
+
self.callback()
|
|
115
132
|
|
|
116
133
|
@z.setter
|
|
117
134
|
def z(self, value):
|
|
118
|
-
self.
|
|
119
|
-
if self.callback
|
|
135
|
+
self._data.z = value
|
|
136
|
+
if self.callback and abs(value - self.prev_data.z) > 1e-6:
|
|
137
|
+
self.prev_data.z = value
|
|
138
|
+
self.callback()
|
basilisk/generic/vec3.py
CHANGED
|
@@ -1,86 +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
|
-
self.
|
|
9
|
-
|
|
10
|
-
def normalize(self):
|
|
11
|
-
"""
|
|
12
|
-
Inplace normalizes the vector
|
|
13
|
-
"""
|
|
14
|
-
self.data = glm.normalize(self.data)
|
|
8
|
+
self.prev_data = glm.vec3(0.0)
|
|
15
9
|
|
|
16
|
-
def set_data(self, *args):
|
|
17
|
-
"""
|
|
18
|
-
Sets the internal vector inplace
|
|
19
|
-
"""
|
|
20
|
-
# overload constructor TODO nvernest this, definitely possible
|
|
21
10
|
if len(args) == 1:
|
|
11
|
+
|
|
22
12
|
if isinstance(args[0], Vec3):
|
|
23
13
|
self.data = glm.vec3(args[0].data)
|
|
14
|
+
self.prev_data = glm.vec3(args[0].prev_data)
|
|
24
15
|
self.callback = args[0].callback
|
|
25
|
-
|
|
16
|
+
|
|
17
|
+
elif isinstance(args[0], glm.vec3):
|
|
18
|
+
self.data = args[0]
|
|
19
|
+
|
|
26
20
|
elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
|
|
27
|
-
|
|
21
|
+
assert len(args[0]) == 3, f'Vec3: Expected 3 values from incoming vector, got {len(args[0])}'
|
|
28
22
|
self.data = glm.vec3(args[0])
|
|
29
|
-
|
|
30
|
-
|
|
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)
|
|
31
29
|
else: raise ValueError(f'Vec3: Expected either 1 vector or 3 numbers, got {len(args)} values')
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
elif isinstance(other, Vec3): self.data += other.data
|
|
40
|
-
else: raise ValueError(f'Vec3: Not an accepted type for addition, got {type(other)}')
|
|
41
|
-
return self
|
|
42
|
-
|
|
43
|
-
def __isub__(self, other):
|
|
44
|
-
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
|
+
|
|
45
37
|
elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
self.data
|
|
60
|
-
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)
|
|
61
52
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
self.data //= other
|
|
65
|
-
return self
|
|
53
|
+
def __abs__(self):
|
|
54
|
+
return Vec3(abs(self.data), callback=self.callback)
|
|
66
55
|
|
|
67
|
-
#
|
|
56
|
+
# accessor functions
|
|
68
57
|
def __getitem__(self, index):
|
|
69
|
-
|
|
70
|
-
|
|
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}'
|
|
71
60
|
return self.data[index]
|
|
72
61
|
|
|
73
62
|
def __setitem__(self, index, value):
|
|
74
|
-
|
|
75
|
-
|
|
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}'
|
|
76
65
|
try: self.data[index] = value
|
|
77
66
|
except: raise ValueError(f'Vec3: Invalid element type, got {type(value)}')
|
|
78
67
|
|
|
79
|
-
def
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
|
|
82
76
|
def __iter__(self):
|
|
83
|
-
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)
|
|
84
88
|
|
|
85
89
|
@property
|
|
86
90
|
def data(self): return self._data
|
|
@@ -93,20 +97,48 @@ class Vec3():
|
|
|
93
97
|
|
|
94
98
|
@data.setter
|
|
95
99
|
def data(self, value: glm.vec3):
|
|
96
|
-
self._data = value
|
|
97
|
-
|
|
100
|
+
self._data = glm.vec3(value)
|
|
101
|
+
cur = self._data
|
|
102
|
+
prev = self.prev_data
|
|
103
|
+
thresh = 1e-6
|
|
104
|
+
|
|
105
|
+
if self.callback and (abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh):
|
|
106
|
+
self.prev_data = glm.vec3(self._data)
|
|
107
|
+
self.callback()
|
|
98
108
|
|
|
99
109
|
@x.setter
|
|
100
110
|
def x(self, value):
|
|
101
|
-
self.
|
|
102
|
-
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()
|
|
103
115
|
|
|
104
116
|
@y.setter
|
|
105
117
|
def y(self, value):
|
|
106
|
-
self.
|
|
107
|
-
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()
|
|
108
122
|
|
|
109
123
|
@z.setter
|
|
110
124
|
def z(self, value):
|
|
111
|
-
self.
|
|
112
|
-
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/input/mouse.py
CHANGED
|
@@ -34,11 +34,11 @@ class Mouse():
|
|
|
34
34
|
pg.mouse.set_pos(x, y)
|
|
35
35
|
|
|
36
36
|
@property
|
|
37
|
-
def click(self): return self.
|
|
37
|
+
def click(self): return self.buttons[0] and not self.previous_buttons[0]
|
|
38
38
|
@property
|
|
39
|
-
def middle_click(self): return self.
|
|
39
|
+
def middle_click(self): return self.buttons[1] and not self.previous_buttons[1]
|
|
40
40
|
@property
|
|
41
|
-
def right_click(self): return self.
|
|
41
|
+
def right_click(self): return self.buttons[2] and not self.previous_buttons[2]
|
|
42
42
|
@property
|
|
43
43
|
def left_down(self): return self.buttons[0]
|
|
44
44
|
@property
|
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
|