basilisk-engine 0.1.12__py3-none-any.whl → 0.1.13__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 (87) hide show
  1. basilisk/__init__.py +14 -14
  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 +224 -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 +2 -2
  18. basilisk/draw/draw.py +100 -100
  19. basilisk/draw/draw_handler.py +179 -179
  20. basilisk/draw/font_renderer.py +28 -28
  21. basilisk/engine.py +206 -206
  22. basilisk/generic/abstract_bvh.py +15 -15
  23. basilisk/generic/abstract_custom.py +133 -133
  24. basilisk/generic/collisions.py +72 -72
  25. basilisk/generic/input_validation.py +66 -66
  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 +23 -23
  32. basilisk/generic/vec3.py +143 -143
  33. basilisk/input/mouse.py +61 -61
  34. basilisk/input/path.py +14 -14
  35. basilisk/mesh/cube.py +33 -33
  36. basilisk/mesh/mesh.py +230 -230
  37. basilisk/mesh/mesh_from_data.py +130 -130
  38. basilisk/mesh/model.py +271 -271
  39. basilisk/mesh/narrow_aabb.py +89 -89
  40. basilisk/mesh/narrow_bvh.py +91 -91
  41. basilisk/mesh/narrow_primative.py +23 -23
  42. basilisk/nodes/helper.py +28 -28
  43. basilisk/nodes/node.py +684 -684
  44. basilisk/nodes/node_handler.py +95 -95
  45. basilisk/particles/particle_handler.py +63 -63
  46. basilisk/particles/particle_renderer.py +87 -87
  47. basilisk/physics/impulse.py +112 -112
  48. basilisk/physics/physics_body.py +43 -43
  49. basilisk/physics/physics_engine.py +35 -35
  50. basilisk/render/batch.py +105 -105
  51. basilisk/render/camera.py +211 -211
  52. basilisk/render/chunk.py +106 -106
  53. basilisk/render/chunk_handler.py +165 -165
  54. basilisk/render/frame.py +101 -101
  55. basilisk/render/framebuffer.py +130 -130
  56. basilisk/render/image.py +87 -87
  57. basilisk/render/image_handler.py +122 -122
  58. basilisk/render/light.py +96 -96
  59. basilisk/render/light_handler.py +58 -58
  60. basilisk/render/material.py +219 -219
  61. basilisk/render/material_handler.py +135 -135
  62. basilisk/render/post_process.py +132 -132
  63. basilisk/render/shader.py +110 -110
  64. basilisk/render/shader_handler.py +79 -79
  65. basilisk/render/sky.py +120 -120
  66. basilisk/scene.py +270 -264
  67. basilisk/shaders/batch.frag +276 -276
  68. basilisk/shaders/batch.vert +115 -115
  69. basilisk/shaders/crt.frag +31 -31
  70. basilisk/shaders/draw.frag +21 -21
  71. basilisk/shaders/draw.vert +21 -21
  72. basilisk/shaders/filter.frag +22 -22
  73. basilisk/shaders/frame.frag +12 -12
  74. basilisk/shaders/frame.vert +13 -13
  75. basilisk/shaders/geometry.frag +8 -8
  76. basilisk/shaders/geometry.vert +41 -41
  77. basilisk/shaders/normal.frag +59 -59
  78. basilisk/shaders/normal.vert +96 -96
  79. basilisk/shaders/particle.frag +71 -71
  80. basilisk/shaders/particle.vert +84 -84
  81. basilisk/shaders/sky.frag +9 -9
  82. basilisk/shaders/sky.vert +13 -13
  83. {basilisk_engine-0.1.12.dist-info → basilisk_engine-0.1.13.dist-info}/METADATA +38 -45
  84. basilisk_engine-0.1.13.dist-info/RECORD +103 -0
  85. {basilisk_engine-0.1.12.dist-info → basilisk_engine-0.1.13.dist-info}/WHEEL +1 -1
  86. basilisk_engine-0.1.12.dist-info/RECORD +0 -103
  87. {basilisk_engine-0.1.12.dist-info → basilisk_engine-0.1.13.dist-info}/top_level.txt +0 -0
@@ -1,73 +1,73 @@
1
- import glm
2
- import numpy as np
3
-
4
-
5
- # transformations
6
- def transform_points(points: np.ndarray, model_matrix: glm.mat4x4) -> list[glm.vec3]:
7
- """
8
- Transforms the mesh points to world space
9
- """
10
- return [model_matrix * pt for pt in points]
11
-
12
- # bvh
13
- def get_aabb_surface_area(top_right: glm.vec3, bottom_left: glm.vec3) -> float:
14
- """
15
- Returns the surface area of the AABB
16
- """
17
- diagonal = top_right - bottom_left
18
- return 2 * (diagonal.x * diagonal.y + diagonal.y * diagonal.z + diagonal.x * diagonal.z)
19
-
20
- def get_extreme_points_np(points: np.ndarray) -> tuple[glm.vec3, glm.vec3]:
21
- """
22
- Returns the top right and bottom left points of the aabb encapsulating the points
23
- """
24
- top_right = glm.vec3(-1e10)
25
- bottom_left = glm.vec3(1e10)
26
- for pt in points:
27
- for i in range(3):
28
- if top_right[i] < pt[i]: top_right[i] = pt[i]
29
- if bottom_left[i] > pt[i]: bottom_left[i] = pt[i]
30
- return top_right, bottom_left
31
-
32
- def get_aabb_line_collision(top_right:glm.vec3, bottom_left:glm.vec3, point:glm.vec3, vec:glm.vec3) -> bool:
33
- """
34
- Determines if a line has collided with an aabb
35
- """
36
- tmin, tmax = -1e10, 1e10
37
- for i in range(3):
38
- if vec[i] != 0:
39
- inv_dir = 1.0 / vec[i]
40
- t1 = (bottom_left[i] - point[i]) * inv_dir
41
- t2 = (top_right[i] - point[i]) * inv_dir
42
- t1, t2 = min(t1, t2), max(t1, t2)
43
- tmin = max(tmin, t1)
44
- tmax = min(tmax, t2)
45
- if tmin > tmax: return False
46
- elif point[i] < bottom_left[i] or point[i] > top_right[i]: return False
47
- return tmax >= 0 and tmin <= 1
48
-
49
- def moller_trumbore(point:glm.vec3, vec:glm.vec3, triangle:list[glm.vec3], epsilon:float=1e-7) -> glm.vec3 | None:
50
- """
51
- Determines where a line intersects with a triangle and where that intersection occurred
52
- """
53
- edge1, edge2 = triangle[1] - triangle[0], triangle[2] - triangle[0]
54
- ray_cross = glm.cross(vec, edge2)
55
- det = glm.dot(edge1, ray_cross)
56
-
57
- # if the ray is parallel to the triangle
58
- if abs(det) < epsilon: return None
59
-
60
- inv_det = 1 / det
61
- s = point - triangle[0]
62
- u = glm.dot(s, ray_cross) * inv_det
63
-
64
- if (u < 0 and abs(u) > epsilon) or (u > 1 and abs(u - 1) > epsilon): return None
65
-
66
- s_cross = glm.cross(s, edge1)
67
- v = glm.dot(vec, s_cross) * inv_det
68
-
69
- if (v < 0 and abs(v) > epsilon) or (u + v > 1 and abs(u + v - 1) > epsilon): return None
70
-
71
- t = glm.dot(edge2, s_cross) * inv_det
72
- if t > epsilon: return point + vec * t
1
+ import glm
2
+ import numpy as np
3
+
4
+
5
+ # transformations
6
+ def transform_points(points: np.ndarray, model_matrix: glm.mat4x4) -> list[glm.vec3]:
7
+ """
8
+ Transforms the mesh points to world space
9
+ """
10
+ return [model_matrix * pt for pt in points]
11
+
12
+ # bvh
13
+ def get_aabb_surface_area(top_right: glm.vec3, bottom_left: glm.vec3) -> float:
14
+ """
15
+ Returns the surface area of the AABB
16
+ """
17
+ diagonal = top_right - bottom_left
18
+ return 2 * (diagonal.x * diagonal.y + diagonal.y * diagonal.z + diagonal.x * diagonal.z)
19
+
20
+ def get_extreme_points_np(points: np.ndarray) -> tuple[glm.vec3, glm.vec3]:
21
+ """
22
+ Returns the top right and bottom left points of the aabb encapsulating the points
23
+ """
24
+ top_right = glm.vec3(-1e10)
25
+ bottom_left = glm.vec3(1e10)
26
+ for pt in points:
27
+ for i in range(3):
28
+ if top_right[i] < pt[i]: top_right[i] = pt[i]
29
+ if bottom_left[i] > pt[i]: bottom_left[i] = pt[i]
30
+ return top_right, bottom_left
31
+
32
+ def get_aabb_line_collision(top_right:glm.vec3, bottom_left:glm.vec3, point:glm.vec3, vec:glm.vec3) -> bool:
33
+ """
34
+ Determines if a line has collided with an aabb
35
+ """
36
+ tmin, tmax = -1e10, 1e10
37
+ for i in range(3):
38
+ if vec[i] != 0:
39
+ inv_dir = 1.0 / vec[i]
40
+ t1 = (bottom_left[i] - point[i]) * inv_dir
41
+ t2 = (top_right[i] - point[i]) * inv_dir
42
+ t1, t2 = min(t1, t2), max(t1, t2)
43
+ tmin = max(tmin, t1)
44
+ tmax = min(tmax, t2)
45
+ if tmin > tmax: return False
46
+ elif point[i] < bottom_left[i] or point[i] > top_right[i]: return False
47
+ return tmax >= 0 and tmin <= 1
48
+
49
+ def moller_trumbore(point:glm.vec3, vec:glm.vec3, triangle:list[glm.vec3], epsilon:float=1e-7) -> glm.vec3 | None:
50
+ """
51
+ Determines where a line intersects with a triangle and where that intersection occurred
52
+ """
53
+ edge1, edge2 = triangle[1] - triangle[0], triangle[2] - triangle[0]
54
+ ray_cross = glm.cross(vec, edge2)
55
+ det = glm.dot(edge1, ray_cross)
56
+
57
+ # if the ray is parallel to the triangle
58
+ if abs(det) < epsilon: return None
59
+
60
+ inv_det = 1 / det
61
+ s = point - triangle[0]
62
+ u = glm.dot(s, ray_cross) * inv_det
63
+
64
+ if (u < 0 and abs(u) > epsilon) or (u > 1 and abs(u - 1) > epsilon): return None
65
+
66
+ s_cross = glm.cross(s, edge1)
67
+ v = glm.dot(vec, s_cross) * inv_det
68
+
69
+ if (v < 0 and abs(v) > epsilon) or (u + v > 1 and abs(u + v - 1) > epsilon): return None
70
+
71
+ t = glm.dot(edge2, s_cross) * inv_det
72
+ if t > epsilon: return point + vec * t
73
73
  return None
basilisk/generic/quat.py CHANGED
@@ -1,143 +1,143 @@
1
- import glm
2
- import numpy as np
3
- from .abstract_custom import Custom
4
-
5
-
6
- class Quat(Custom):
7
- def __init__(self, *args, callback=None):
8
- self.callback = callback
9
- self.prev_data = glm.quat(1, 0, 0, 0)
10
-
11
- if len(args) == 1:
12
-
13
- if isinstance(args[0], Quat):
14
- self.data = glm.quat(args[0].data)
15
- self.prev_data = glm.quat(args[0].prev_data)
16
- self.callback = args[0].callback
17
-
18
- elif isinstance(args[0], glm.quat):
19
- self.data = args[0]
20
-
21
- elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
22
- assert 2 < len(args[0]) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(args[0])}'
23
- self.data = glm.quat(args[0])
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)
49
-
50
- # unary operators
51
- def __neg__(self):
52
- return Quat(-self.data, callback=self.callback)
53
-
54
- def __abs__(self):
55
- return Quat(abs(self.data), callback=self.callback)
56
-
57
- # accessor functions
58
- def __getitem__(self, index):
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}'
61
- return self.data[index]
62
-
63
- def __setitem__(self, index, value):
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}'
66
- try: self.data[index] = value
67
- except: raise ValueError(f'Quat: Invalid element type, got {type(value)}')
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
84
- def __repr__(self):
85
- return 'bsk' + str(self.data)
86
-
87
- def __str__(self):
88
- return 'bsk ' + str(self.data)
89
-
90
- @property
91
- def data(self): return self._data
92
- @property
93
- def w(self): return self.data.w
94
- @property
95
- def x(self): return self.data.x
96
- @property
97
- def y(self): return self.data.y
98
- @property
99
- def z(self): return self.data.z
100
-
101
- @data.setter
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()
111
- self.normalize()
112
-
113
- @w.setter
114
- def w(self, value):
115
- self.data.w = value
116
- if self.callback and abs(value - self.prev_data.w) > 1e-6:
117
- self.prev_data.w = value
118
- self.callback()
119
- self.normalize()
120
-
121
- @x.setter
122
- def x(self, value):
123
- self._data.x = value
124
- if self.callback and abs(value - self.prev_data.x) > 1e-6:
125
- self.prev_data.x = value
126
- self.callback()
127
- self.normalize()
128
-
129
- @y.setter
130
- def y(self, value):
131
- self._data.y = value
132
- if self.callback and abs(value - self.prev_data.y) > 1e-6:
133
- self.prev_data.y = value
134
- self.callback()
135
- self.normalize()
136
-
137
- @z.setter
138
- def z(self, value):
139
- self._data.z = value
140
- if self.callback and abs(value - self.prev_data.z) > 1e-6:
141
- self.prev_data.z = value
142
- self.callback()
1
+ import glm
2
+ import numpy as np
3
+ from .abstract_custom import Custom
4
+
5
+
6
+ class Quat(Custom):
7
+ def __init__(self, *args, callback=None):
8
+ self.callback = callback
9
+ self.prev_data = glm.quat(1, 0, 0, 0)
10
+
11
+ if len(args) == 1:
12
+
13
+ if isinstance(args[0], Quat):
14
+ self.data = glm.quat(args[0].data)
15
+ self.prev_data = glm.quat(args[0].prev_data)
16
+ self.callback = args[0].callback
17
+
18
+ elif isinstance(args[0], glm.quat):
19
+ self.data = args[0]
20
+
21
+ elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
22
+ assert 2 < len(args[0]) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(args[0])}'
23
+ self.data = glm.quat(args[0])
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)
49
+
50
+ # unary operators
51
+ def __neg__(self):
52
+ return Quat(-self.data, callback=self.callback)
53
+
54
+ def __abs__(self):
55
+ return Quat(abs(self.data), callback=self.callback)
56
+
57
+ # accessor functions
58
+ def __getitem__(self, index):
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}'
61
+ return self.data[index]
62
+
63
+ def __setitem__(self, index, value):
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}'
66
+ try: self.data[index] = value
67
+ except: raise ValueError(f'Quat: Invalid element type, got {type(value)}')
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
84
+ def __repr__(self):
85
+ return 'bsk' + str(self.data)
86
+
87
+ def __str__(self):
88
+ return 'bsk ' + str(self.data)
89
+
90
+ @property
91
+ def data(self): return self._data
92
+ @property
93
+ def w(self): return self.data.w
94
+ @property
95
+ def x(self): return self.data.x
96
+ @property
97
+ def y(self): return self.data.y
98
+ @property
99
+ def z(self): return self.data.z
100
+
101
+ @data.setter
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()
111
+ self.normalize()
112
+
113
+ @w.setter
114
+ def w(self, value):
115
+ self.data.w = value
116
+ if self.callback and abs(value - self.prev_data.w) > 1e-6:
117
+ self.prev_data.w = value
118
+ self.callback()
119
+ self.normalize()
120
+
121
+ @x.setter
122
+ def x(self, value):
123
+ self._data.x = value
124
+ if self.callback and abs(value - self.prev_data.x) > 1e-6:
125
+ self.prev_data.x = value
126
+ self.callback()
127
+ self.normalize()
128
+
129
+ @y.setter
130
+ def y(self, value):
131
+ self._data.y = value
132
+ if self.callback and abs(value - self.prev_data.y) > 1e-6:
133
+ self.prev_data.y = value
134
+ self.callback()
135
+ self.normalize()
136
+
137
+ @z.setter
138
+ def z(self, value):
139
+ self._data.z = value
140
+ if self.callback and abs(value - self.prev_data.z) > 1e-6:
141
+ self.prev_data.z = value
142
+ self.callback()
143
143
  self.normalize()
@@ -1,8 +1,8 @@
1
- import glm
2
-
3
-
4
- def rotate_vec_by_quat(vec: glm.vec3, quat: glm.quat) -> glm.vec3:
5
- """
6
- Rotates a vector by a quaternion. Probably just dont use this, just a reminder of how glm works with quaternions
7
- """
1
+ import glm
2
+
3
+
4
+ def rotate_vec_by_quat(vec: glm.vec3, quat: glm.quat) -> glm.vec3:
5
+ """
6
+ Rotates a vector by a quaternion. Probably just dont use this, just a reminder of how glm works with quaternions
7
+ """
8
8
  return vec * quat
@@ -1,24 +1,24 @@
1
- import glm
2
- from ..nodes.node import Node
3
-
4
-
5
- class RaycastResult:
6
- node: Node | None
7
- """The node that the raycast hit. Is None if no object was hit"""
8
- position: glm.vec3
9
- """The node that the raycast hit"""
10
-
11
- def __init__(self, node: Node | None, position: glm.vec3):
12
- """
13
- Container for returning raycast results.
14
- Contains the node hit and the global position the raycast hit at.
15
- """
16
-
17
- self.node = node
18
- self.position = position
19
-
20
- def __bool__(self):
21
- return bool(self.node)
22
-
23
- def __repr__(self):
1
+ import glm
2
+ from ..nodes.node import Node
3
+
4
+
5
+ class RaycastResult:
6
+ node: Node | None
7
+ """The node that the raycast hit. Is None if no object was hit"""
8
+ position: glm.vec3
9
+ """The node that the raycast hit"""
10
+
11
+ def __init__(self, node: Node | None, position: glm.vec3):
12
+ """
13
+ Container for returning raycast results.
14
+ Contains the node hit and the global position the raycast hit at.
15
+ """
16
+
17
+ self.node = node
18
+ self.position = position
19
+
20
+ def __bool__(self):
21
+ return bool(self.node)
22
+
23
+ def __repr__(self):
24
24
  return f'<Raycast | Node: {self.node}, Position: {self.position}>'