basilisk-engine 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of basilisk-engine might be problematic. Click here for more details.

Files changed (82) hide show
  1. basilisk/__init__.py +11 -11
  2. basilisk/bsk_assets/cube.obj +48 -48
  3. basilisk/collisions/broad/broad_aabb.py +102 -102
  4. basilisk/collisions/broad/broad_bvh.py +137 -137
  5. basilisk/collisions/collider.py +95 -83
  6. basilisk/collisions/collider_handler.py +225 -228
  7. basilisk/collisions/narrow/contact_manifold.py +90 -90
  8. basilisk/collisions/narrow/dataclasses.py +33 -27
  9. basilisk/collisions/narrow/deprecated.py +46 -46
  10. basilisk/collisions/narrow/epa.py +91 -91
  11. basilisk/collisions/narrow/gjk.py +66 -66
  12. basilisk/collisions/narrow/graham_scan.py +24 -24
  13. basilisk/collisions/narrow/helper.py +29 -29
  14. basilisk/collisions/narrow/line_intersections.py +106 -106
  15. basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
  16. basilisk/config.py +2 -2
  17. basilisk/draw/draw.py +100 -100
  18. basilisk/draw/draw_handler.py +180 -210
  19. basilisk/draw/font_renderer.py +28 -28
  20. basilisk/engine.py +195 -195
  21. basilisk/generic/abstract_bvh.py +15 -15
  22. basilisk/generic/abstract_custom.py +133 -133
  23. basilisk/generic/collisions.py +70 -70
  24. basilisk/generic/input_validation.py +67 -28
  25. basilisk/generic/math.py +6 -6
  26. basilisk/generic/matrices.py +33 -33
  27. basilisk/generic/meshes.py +72 -72
  28. basilisk/generic/quat.py +137 -137
  29. basilisk/generic/quat_methods.py +7 -7
  30. basilisk/generic/raycast_result.py +24 -0
  31. basilisk/generic/vec3.py +143 -143
  32. basilisk/input/mouse.py +61 -59
  33. basilisk/mesh/cube.py +33 -33
  34. basilisk/mesh/mesh.py +230 -230
  35. basilisk/mesh/mesh_from_data.py +132 -132
  36. basilisk/mesh/model.py +271 -271
  37. basilisk/mesh/narrow_aabb.py +89 -89
  38. basilisk/mesh/narrow_bvh.py +91 -91
  39. basilisk/mesh/narrow_primative.py +23 -23
  40. basilisk/nodes/helper.py +29 -0
  41. basilisk/nodes/node.py +681 -617
  42. basilisk/nodes/node_handler.py +95 -118
  43. basilisk/particles/particle_handler.py +63 -54
  44. basilisk/particles/particle_renderer.py +87 -87
  45. basilisk/physics/impulse.py +112 -112
  46. basilisk/physics/physics_body.py +43 -43
  47. basilisk/physics/physics_engine.py +35 -35
  48. basilisk/render/batch.py +86 -86
  49. basilisk/render/camera.py +204 -199
  50. basilisk/render/chunk.py +99 -99
  51. basilisk/render/chunk_handler.py +154 -154
  52. basilisk/render/frame.py +181 -181
  53. basilisk/render/image.py +75 -75
  54. basilisk/render/image_handler.py +122 -122
  55. basilisk/render/light.py +96 -96
  56. basilisk/render/light_handler.py +58 -58
  57. basilisk/render/material.py +219 -219
  58. basilisk/render/material_handler.py +135 -135
  59. basilisk/render/shader.py +109 -109
  60. basilisk/render/shader_handler.py +79 -79
  61. basilisk/render/sky.py +120 -120
  62. basilisk/scene.py +250 -210
  63. basilisk/shaders/batch.frag +276 -276
  64. basilisk/shaders/batch.vert +115 -115
  65. basilisk/shaders/draw.frag +21 -21
  66. basilisk/shaders/draw.vert +21 -21
  67. basilisk/shaders/filter.frag +22 -22
  68. basilisk/shaders/frame.frag +12 -12
  69. basilisk/shaders/frame.vert +13 -13
  70. basilisk/shaders/geometry.frag +8 -8
  71. basilisk/shaders/geometry.vert +41 -41
  72. basilisk/shaders/normal.frag +59 -59
  73. basilisk/shaders/normal.vert +96 -96
  74. basilisk/shaders/particle.frag +71 -71
  75. basilisk/shaders/particle.vert +84 -84
  76. basilisk/shaders/sky.frag +9 -9
  77. basilisk/shaders/sky.vert +13 -13
  78. {basilisk_engine-0.1.2.dist-info → basilisk_engine-0.1.3.dist-info}/METADATA +45 -38
  79. basilisk_engine-0.1.3.dist-info/RECORD +97 -0
  80. {basilisk_engine-0.1.2.dist-info → basilisk_engine-0.1.3.dist-info}/WHEEL +1 -1
  81. basilisk_engine-0.1.2.dist-info/RECORD +0 -95
  82. {basilisk_engine-0.1.2.dist-info → basilisk_engine-0.1.3.dist-info}/top_level.txt +0 -0
basilisk/scene.py CHANGED
@@ -1,211 +1,251 @@
1
- import moderngl as mgl
2
- import glm
3
- from .render.shader_handler import ShaderHandler
4
- from .mesh.mesh import Mesh
5
- from .render.material import Material
6
- from .render.material_handler import MaterialHandler
7
- from .render.light_handler import LightHandler
8
- from .render.camera import Camera, FreeCamera
9
- from .nodes.node_handler import NodeHandler
10
- from .physics.physics_engine import PhysicsEngine
11
- from .collisions.collider_handler import ColliderHandler
12
- from .draw.draw_handler import DrawHandler
13
- from .render.sky import Sky
14
- from .render.frame import Frame
15
- from .particles.particle_handler import ParticleHandler
16
- from .nodes.node import Node
17
- from .generic.collisions import moller_trumbore
18
-
19
- class Scene():
20
- engine: any
21
- """Parent engine of the scene"""
22
- ctx: mgl.Context
23
- """Reference to the engine context"""
24
-
25
- def __init__(self) -> None:
26
- """
27
- Basilisk scene object. Contains all nodes for the scene
28
- """
29
-
30
- self.engine = None
31
- self.ctx = None
32
-
33
- self.camera = None
34
- self.shader_handler = None
35
- self.node_handler = None
36
- self.material_handler = None
37
- self.light_handler = None
38
- self.draw_handler = None
39
- self.sky = None
40
- self.frame = None
41
-
42
- def update(self) -> None:
43
- """
44
- Updates the physics and in the scene
45
- """
46
-
47
- self.node_handler.update()
48
- self.particle.update()
49
- self.camera.update()
50
- self.collider_handler.resolve_collisions()
51
-
52
- def render(self) -> None:
53
- """
54
- Renders all the nodes with meshes in the scene
55
- """
56
-
57
- self.frame.use()
58
- self.shader_handler.write()
59
- if self.sky: self.sky.render()
60
- self.node_handler.render()
61
- self.particle.render()
62
- self.draw_handler.render()
63
-
64
- if self.engine.headless: return
65
- self.frame.render()
66
-
67
- def add(self, bsk_object: ...) -> ...:
68
- """
69
- Adds an object to the scene. Can pass in any scene objects:
70
- Argument overloads:
71
- object: Node - Adds the given node to the scene.
72
- """
73
-
74
- if isinstance(bsk_object, type(None)):
75
- # Considered well defined behavior
76
- return
77
- elif isinstance(bsk_object, Node):
78
- # Add a node to the scene
79
- return self.node_handler.add(bsk_object)
80
- # Light
81
-
82
- # Mesh
83
-
84
- else:
85
- raise ValueError(f'scene.add: Incompatable object add type {type(bsk_object)}')
86
-
87
- return None
88
-
89
- def remove(self, bsk_object):
90
- """
91
- Removes the given baskilsk object from the scene
92
- """
93
-
94
- if isinstance(bsk_object, type(None)):
95
- # Considered well defined behavior
96
- return
97
- elif isinstance(bsk_object, Node):
98
- self.node_handler.remove(bsk_object)
99
- else:
100
- raise ValueError(f'scene.remove: Incompatable object remove type {type(bsk_object)}')
101
-
102
- return None
103
-
104
- def set_engine(self, engine: any) -> None:
105
- """
106
- Sets the back references to the engine and creates handlers with the context
107
- """
108
-
109
- self.engine = engine
110
- self.ctx = engine.ctx
111
-
112
- self.camera = FreeCamera()
113
- self.shader_handler = ShaderHandler(self)
114
- self.material_handler = MaterialHandler(self)
115
- self.light_handler = LightHandler(self)
116
- self.physics_engine = PhysicsEngine()
117
- self.node_handler = NodeHandler(self)
118
- self.particle = ParticleHandler(self)
119
- self.collider_handler = ColliderHandler(self)
120
- self.draw_handler = DrawHandler(self)
121
- self.frame = Frame(self)
122
- self.sky = Sky(self.engine)
123
-
124
- def raycast(self, position: glm.vec3=None, forward: glm.vec3=None, max_distance: float=1e5, has_collisions: bool=None, has_physics: bool=None, tags: list[str]=[]) -> tuple[Node, glm.vec3]:
125
- """
126
- Ray cast from any posiiton and forward vector and returns the nearest node. If no position or forward is given, uses the scene camera's current position and forward
127
- """
128
- if not position: position = self.camera.position
129
- if not forward: forward = self.camera.forward
130
- forward = glm.normalize(forward)
131
-
132
- # if we are filtering for collisions, use the broad BVH to improve performance
133
- if has_collisions:
134
- colliders = self.collider_handler.bvh.get_line_collided(position, forward)
135
- nodes = [collider.node for collider in colliders]
136
-
137
- def is_valid(node: Node) -> bool:
138
- return all([
139
- has_collisions is None or bool(node.collider) == has_collisions,
140
- has_physics is None or bool(node.physics_body) == has_physics,
141
- all(tag in node.tags for tag in tags)
142
- ])
143
-
144
- nodes: list[Node] = list(filter(lambda node: is_valid(node), nodes))
145
-
146
- # if we are not filtering for collisions, filter nodes and
147
- else: nodes = self.node_handler.get_all(collisions=has_collisions, physics=has_physics, tags=tags)
148
-
149
- # determine closest node
150
- best_distance, best_point, best_node = max_distance, None, None
151
- position_two = position + forward
152
- for node in nodes:
153
-
154
- inv_mat = glm.inverse(node.model_matrix)
155
- relative_position = inv_mat * position
156
- relative_forward = glm.normalize(inv_mat * position_two - relative_position)
157
-
158
- triangles = [node.mesh.indices[i] for i in node.mesh.get_line_collided(relative_position, relative_forward)]
159
-
160
- for triangle in triangles:
161
- intersection = moller_trumbore(relative_position, relative_forward, [node.mesh.points[i] for i in triangle])
162
- if not intersection: continue
163
- intersection = node.model_matrix * intersection
164
- distance = glm.length(intersection - position)
165
- if distance < best_distance:
166
- best_distance = distance
167
- best_point = intersection
168
- best_node = node
169
-
170
- return best_node, best_point
171
-
172
- def raycast_mouse(self, position: tuple[int, int] | glm.vec2, max_distance: float=1e5, has_collisions: bool=None, has_pshyics: bool=None, tags: list[str]=[]) -> tuple[Node, glm.vec3]:
173
- """
174
- Ray casts from the mouse position with respect to the camera. Returns the nearest node that was clicked, if none was clicked, returns None.
175
- """
176
- # derive forward vector from mouse click position
177
- position = glm.vec2(position)
178
- inv_proj, inv_view = glm.inverse(self.camera.m_proj), glm.inverse(self.camera.m_view)
179
- ndc = glm.vec4(2 * position[0] / self.engine.win_size[0] - 1, 1 - 2 * position[1] / self.engine.win_size[1], 1, 1)
180
- point = inv_proj * ndc
181
- point /= point.w
182
- forward = glm.normalize(glm.vec3(inv_view * glm.vec4(point.x, point.y, point.z, 0)))
183
-
184
- return self.raycast(
185
- position=self.camera.position,
186
- forward=forward,
187
- max_distance=max_distance,
188
- has_collisions=has_collisions,
189
- has_physics=has_pshyics,
190
- tags=tags
191
- )
192
-
193
- @property
194
- def camera(self): return self._camera
195
- @property
196
- def sky(self): return self._sky
197
-
198
- @camera.setter
199
- def camera(self, value: Camera):
200
- if not value: return
201
- if not isinstance(value, Camera):
202
- raise TypeError(f'Scene: Invalid camera type: {type(value)}. Expected type bsk.Camera')
203
- self._camera = value
204
- self._camera.scene = self
205
-
206
- @sky.setter
207
- def sky(self, value: Sky):
208
- if not isinstance(value, Sky) and not isinstance(value, type(None)):
209
- raise TypeError(f'Scene: Invalid sky type: {type(value)}. Expected type bsk.Sky or None')
210
- self._sky = value
1
+ import moderngl as mgl
2
+ import glm
3
+
4
+ from .mesh.mesh import Mesh
5
+ from .render.material import Material
6
+ from .render.shader_handler import ShaderHandler
7
+ from .render.material_handler import MaterialHandler
8
+ from .render.light_handler import LightHandler
9
+ from .render.camera import Camera, FreeCamera
10
+ from .nodes.node_handler import NodeHandler
11
+ from .physics.physics_engine import PhysicsEngine
12
+ from .collisions.collider_handler import ColliderHandler
13
+ from .draw.draw_handler import DrawHandler
14
+ from .render.sky import Sky
15
+ from .render.frame import Frame
16
+ from .particles.particle_handler import ParticleHandler
17
+ from .nodes.node import Node
18
+ from .generic.collisions import moller_trumbore
19
+ from .generic.raycast_result import RaycastResult
20
+
21
+ class Scene():
22
+ engine: any
23
+ """Parent engine of the scene"""
24
+ ctx: mgl.Context
25
+ """Reference to the engine context"""
26
+
27
+ def __init__(self) -> None:
28
+ """
29
+ Basilisk scene object. Contains all nodes for the scene
30
+ """
31
+
32
+ self.engine = None
33
+ self.ctx = None
34
+
35
+ self.camera = None
36
+ self.shader_handler = None
37
+ self.node_handler = None
38
+ self.material_handler = None
39
+ self.light_handler = None
40
+ self.draw_handler = None
41
+ self.sky = None
42
+ self.frame = None
43
+
44
+ def update(self) -> None:
45
+ """
46
+ Updates the physics and in the scene
47
+ """
48
+ self.particle.update()
49
+ self.camera.update()
50
+
51
+
52
+ self.node_handler.update()
53
+ if self.engine.delta_time < 0.5: # TODO this will cause physics to slow down when on low frame rate, this is probabl;y acceptable
54
+ self.collider_handler.resolve_collisions()
55
+
56
+ def render(self) -> None:
57
+ """
58
+ Renders all the nodes with meshes in the scene
59
+ """
60
+
61
+ self.frame.use()
62
+ self.shader_handler.write()
63
+ if self.sky: self.sky.render()
64
+ self.node_handler.render()
65
+ self.particle.render()
66
+ self.draw_handler.render()
67
+
68
+ if self.engine.headless: return
69
+ self.frame.render()
70
+
71
+ def add(self, *objects: Node | None) -> None | Node | list:
72
+ """
73
+ Adds the given object(s) to the scene. Can pass in any scene objects:
74
+ Argument overloads:
75
+ object: Node - Adds the given node to the scene.
76
+ """
77
+
78
+ # List of all return values for the added objects
79
+ returns = []
80
+
81
+ # Loop through all objects passed in
82
+ for bsk_object in objects:
83
+
84
+ # Considered well defined behavior to add None
85
+ if isinstance(bsk_object, type(None)):
86
+ continue
87
+
88
+ # Add a node to the scene
89
+ elif isinstance(bsk_object, Node):
90
+ returns.append(self.node_handler.add(bsk_object)); continue
91
+
92
+ # Recived incompatable type
93
+ else:
94
+ raise ValueError(f'scene.add: Incompatable object add type {type(bsk_object)}')
95
+
96
+ # Return based on what the user passed in
97
+ if not returns: return None
98
+ if len(returns) == 1: return returns[0]
99
+ return returns
100
+
101
+ def remove(self, *objects: Node | None) -> None | Node | list:
102
+ """
103
+ Removes the given baskilsk object from the scene
104
+ """
105
+
106
+ # List of all return values for the added objects
107
+ returns = []
108
+
109
+ # Loop through all objects passed in
110
+ for bsk_object in objects:
111
+
112
+ # Considered well defined behavior to remove None
113
+ if isinstance(bsk_object, type(None)):
114
+ continue
115
+
116
+ # Remove a node from the scene
117
+ elif isinstance(bsk_object, Node):
118
+ returns.append(self.node_handler.remove(bsk_object)); continue
119
+
120
+ # Recived incompatable type
121
+ else:
122
+ raise ValueError(f'scene.remove: Incompatable object remove type {type(bsk_object)}')
123
+
124
+ # Return based on what the user passed in
125
+ if not returns: return None
126
+ if len(returns) == 1: return returns[0]
127
+ return returns
128
+
129
+ def set_engine(self, engine: any) -> None:
130
+ """
131
+ Sets the back references to the engine and creates handlers with the context
132
+ """
133
+
134
+ self.engine = engine
135
+ self.ctx = engine.ctx
136
+
137
+ self.camera = FreeCamera()
138
+ self.shader_handler = ShaderHandler(self)
139
+ self.material_handler = MaterialHandler(self)
140
+ self.light_handler = LightHandler(self)
141
+ self.physics_engine = PhysicsEngine()
142
+ self.node_handler = NodeHandler(self)
143
+ self.particle = ParticleHandler(self)
144
+ self.collider_handler = ColliderHandler(self)
145
+ self.draw_handler = DrawHandler(self)
146
+ self.frame = Frame(self)
147
+ self.sky = Sky(self.engine)
148
+
149
+ def raycast(self, position: glm.vec3=None, forward: glm.vec3=None, max_distance: float=1e5, has_collisions: bool=None, has_physics: bool=None, tags: list[str]=[]) -> RaycastResult:
150
+ """
151
+ Ray cast from any posiiton and forward vector and returns a RaycastResult eith the nearest node.
152
+ If no position or forward is given, uses the scene camera's current position and forward
153
+ """
154
+ if not position: position = self.camera.position
155
+ if not forward: forward = self.camera.forward
156
+ forward = glm.normalize(forward)
157
+
158
+ # if we are filtering for collisions, use the broad BVH to improve performance
159
+ if has_collisions:
160
+ colliders = self.collider_handler.bvh.get_line_collided(position, forward)
161
+ nodes = [collider.node for collider in colliders]
162
+
163
+ def is_valid(node: Node) -> bool:
164
+ return all([
165
+ has_collisions is None or bool(node.collider) == has_collisions,
166
+ has_physics is None or bool(node.physics_body) == has_physics,
167
+ all(tag in node.tags for tag in tags)
168
+ ])
169
+
170
+ nodes: list[Node] = list(filter(lambda node: is_valid(node), nodes))
171
+
172
+ # if we are not filtering for collisions, filter nodes and
173
+ else: nodes = self.node_handler.get_all(collisions=has_collisions, physics=has_physics, tags=tags)
174
+
175
+ # determine closest node
176
+ best_distance, best_point, best_node = max_distance, None, None
177
+ position_two = position + forward
178
+ for node in nodes:
179
+
180
+ inv_mat = glm.inverse(node.model_matrix)
181
+ relative_position = inv_mat * position
182
+ relative_forward = glm.normalize(inv_mat * position_two - relative_position)
183
+
184
+ triangles = [node.mesh.indices[i] for i in node.mesh.get_line_collided(relative_position, relative_forward)]
185
+
186
+ for triangle in triangles:
187
+ intersection = moller_trumbore(relative_position, relative_forward, [node.mesh.points[i] for i in triangle])
188
+ if not intersection: continue
189
+ intersection = node.model_matrix * intersection
190
+ distance = glm.length(intersection - position)
191
+ if distance < best_distance:
192
+ best_distance = distance
193
+ best_point = intersection
194
+ best_node = node
195
+
196
+ return RaycastResult(best_node, best_point)
197
+
198
+ def raycast_mouse(self, position: tuple[int, int] | glm.vec2, max_distance: float=1e5, has_collisions: bool=None, has_pshyics: bool=None, tags: list[str]=[]) -> RaycastResult:
199
+ """
200
+ Ray casts from the mouse position with respect to the camera. Returns the nearest node that was clicked, if none was clicked, returns None.
201
+ """
202
+ # derive forward vector from mouse click position
203
+ position = glm.vec2(position)
204
+ inv_proj, inv_view = glm.inverse(self.camera.m_proj), glm.inverse(self.camera.m_view)
205
+ ndc = glm.vec4(2 * position[0] / self.engine.win_size[0] - 1, 1 - 2 * position[1] / self.engine.win_size[1], 1, 1)
206
+ point = inv_proj * ndc
207
+ point /= point.w
208
+ forward = glm.normalize(glm.vec3(inv_view * glm.vec4(point.x, point.y, point.z, 0)))
209
+
210
+ return self.raycast(
211
+ position=self.camera.position,
212
+ forward=forward,
213
+ max_distance=max_distance,
214
+ has_collisions=has_collisions,
215
+ has_physics=has_pshyics,
216
+ tags=tags
217
+ )
218
+
219
+ def get(self, position: glm.vec3=None, scale: glm.vec3=None, rotation: glm.quat=None, forward: glm.vec3=None, mesh: Mesh=None, material: Material=None, velocity: glm.vec3=None, rotational_velocity: glm.quat=None, physics: bool=None, mass: float=None, collisions: bool=None, static_friction: float=None, kinetic_friction: float=None, elasticity: float=None, collision_group: float=None, name: str=None, tags: list[str]=None,static: bool=None) -> Node:
220
+ """
221
+ Returns the first node with the given traits
222
+ """
223
+ self.node_handler.get(position, scale, rotation, forward, mesh, material, velocity, rotational_velocity, physics, mass, collisions, static_friction, kinetic_friction, elasticity, collision_group, name, tags, static)
224
+
225
+ def get_all(self, position: glm.vec3=None, scale: glm.vec3=None, rotation: glm.quat=None, forward: glm.vec3=None, mesh: Mesh=None, material: Material=None, velocity: glm.vec3=None, rotational_velocity: glm.quat=None, physics: bool=None, mass: float=None, collisions: bool=None, static_friction: float=None, kinetic_friction: float=None, elasticity: float=None, collision_group: float=None, name: str=None, tags: list[str]=None,static: bool=None) -> list[Node]:
226
+ """
227
+ Returns all nodes with the given traits
228
+ """
229
+ self.node_handler.get_all(position, scale, rotation, forward, mesh, material, velocity, rotational_velocity, physics, mass, collisions, static_friction, kinetic_friction, elasticity, collision_group, name, tags, static)
230
+
231
+ @property
232
+ def camera(self): return self._camera
233
+ @property
234
+ def sky(self): return self._sky
235
+ @property
236
+ def nodes(self): return self.node_handler.nodes
237
+
238
+ @camera.setter
239
+ def camera(self, value: Camera):
240
+ if not value: return
241
+ if not isinstance(value, Camera):
242
+ raise TypeError(f'Scene: Invalid camera type: {type(value)}. Expected type bsk.Camera')
243
+ self._camera = value
244
+ self._camera.scene = self
245
+
246
+ @sky.setter
247
+ def sky(self, value: Sky):
248
+ if not isinstance(value, Sky) and not isinstance(value, type(None)):
249
+ raise TypeError(f'Scene: Invalid sky type: {type(value)}. Expected type bsk.Sky or None')
250
+ self._sky = value
211
251
  if value: self._sky.write()