basilisk-engine 0.1.43__py3-none-any.whl → 0.1.44__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 +26 -26
- basilisk/audio/sound.py +27 -27
- 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 +178 -178
- basilisk/draw/font_renderer.py +28 -28
- basilisk/engine.py +169 -169
- 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/__init__.py +0 -0
- basilisk/input/mouse.py +62 -0
- basilisk/input/path.py +14 -0
- 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 +97 -97
- basilisk/particles/particle_handler.py +64 -64
- basilisk/particles/particle_renderer.py +93 -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 +120 -120
- 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 +287 -287
- basilisk/shaders/batch.frag +288 -293
- basilisk/shaders/batch.vert +117 -117
- basilisk/shaders/bloom_downsample.frag +23 -23
- basilisk/shaders/bloom_frame.frag +25 -0
- 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.43.dist-info → basilisk_engine-0.1.44.dist-info}/METADATA +89 -89
- basilisk_engine-0.1.44.dist-info/RECORD +115 -0
- {basilisk_engine-0.1.43.dist-info → basilisk_engine-0.1.44.dist-info}/WHEEL +1 -1
- basilisk_engine-0.1.43.dist-info/RECORD +0 -111
- {basilisk_engine-0.1.43.dist-info → basilisk_engine-0.1.44.dist-info}/top_level.txt +0 -0
basilisk/scene.py
CHANGED
|
@@ -1,288 +1,288 @@
|
|
|
1
|
-
import moderngl as mgl
|
|
2
|
-
import glm
|
|
3
|
-
import pygame as pg
|
|
4
|
-
|
|
5
|
-
from .mesh.mesh import Mesh
|
|
6
|
-
from .render.material import Material
|
|
7
|
-
from .render.shader import Shader
|
|
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 .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
|
-
from .generic.raycast_result import RaycastResult
|
|
19
|
-
from .render.post_process import PostProcess
|
|
20
|
-
from .render.framebuffer import Framebuffer
|
|
21
|
-
|
|
22
|
-
class Scene():
|
|
23
|
-
engine: ...=None
|
|
24
|
-
"""Parent engine of the scene"""
|
|
25
|
-
ctx: mgl.Context
|
|
26
|
-
"""Reference to the engine context"""
|
|
27
|
-
camera: Camera=None
|
|
28
|
-
""""""
|
|
29
|
-
light_handler: LightHandler=None
|
|
30
|
-
""""""
|
|
31
|
-
physics_engine: PhysicsEngine=None
|
|
32
|
-
""""""
|
|
33
|
-
node_handler: NodeHandler=None
|
|
34
|
-
""""""
|
|
35
|
-
|
|
36
|
-
def __init__(self, engine: ..., shader: Shader=None) -> None:
|
|
37
|
-
"""
|
|
38
|
-
Basilisk scene object. Contains all nodes for the scene
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
self.engine = engine
|
|
42
|
-
self.ctx = engine.ctx
|
|
43
|
-
self.shader = shader if shader else engine.shader
|
|
44
|
-
self.camera = FreeCamera()
|
|
45
|
-
self.light_handler = LightHandler(self)
|
|
46
|
-
self.physics_engine = PhysicsEngine()
|
|
47
|
-
self.node_handler = NodeHandler(self)
|
|
48
|
-
self.particle = ParticleHandler(self)
|
|
49
|
-
self.collider_handler = ColliderHandler(self)
|
|
50
|
-
self.sky = Sky(self)
|
|
51
|
-
self.frame = Frame(self.engine)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def update(self, render: bool=True, nodes: bool=True, particles: bool=True, collisions: bool=True) -> None:
|
|
55
|
-
"""
|
|
56
|
-
Updates the physics and in the scene
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
# Call the internal engine update (for IO and time)
|
|
60
|
-
self.engine._update()
|
|
61
|
-
|
|
62
|
-
# Check that the engine is still running
|
|
63
|
-
if not self.engine.running: return
|
|
64
|
-
|
|
65
|
-
# Update based on the given parameters
|
|
66
|
-
if nodes: self.node_handler.update()
|
|
67
|
-
if particles: self.particle.update()
|
|
68
|
-
|
|
69
|
-
# Update the camera
|
|
70
|
-
self.camera.update()
|
|
71
|
-
if self.engine.event_resize: self.camera.use()
|
|
72
|
-
|
|
73
|
-
if collisions and self.engine.delta_time < 0.5: # TODO this will cause physics to slow down when on low frame rate, this is probabl;y acceptable
|
|
74
|
-
self.collider_handler.resolve_collisions()
|
|
75
|
-
|
|
76
|
-
# Render by default to the scene frame
|
|
77
|
-
if render: self.render()
|
|
78
|
-
|
|
79
|
-
def render(self, target=None) -> None:
|
|
80
|
-
"""
|
|
81
|
-
Renders all the nodes with meshes in the scene
|
|
82
|
-
"""
|
|
83
|
-
|
|
84
|
-
target.use() if target else self.frame.use(); self.frame.clear()
|
|
85
|
-
self.engine.shader_handler.write(self)
|
|
86
|
-
self.particle.render()
|
|
87
|
-
self.node_handler.render()
|
|
88
|
-
if self.sky: self.sky.render()
|
|
89
|
-
|
|
90
|
-
if target: return
|
|
91
|
-
# This will show the frame to screen on engine.update()
|
|
92
|
-
self.frame.scene_render(self.ctx.screen)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def add(self, *objects: Node | None) -> None | Node | list:
|
|
97
|
-
"""
|
|
98
|
-
Adds the given object(s) to the scene. Can pass in any scene objects:
|
|
99
|
-
Argument overloads:
|
|
100
|
-
object: Node - Adds the given node to the scene.
|
|
101
|
-
"""
|
|
102
|
-
|
|
103
|
-
# List of all return values for the added objects
|
|
104
|
-
returns = []
|
|
105
|
-
|
|
106
|
-
# Loop through all objects passed in
|
|
107
|
-
for bsk_object in objects:
|
|
108
|
-
|
|
109
|
-
# Considered well defined behavior to add None
|
|
110
|
-
if isinstance(bsk_object, type(None)):
|
|
111
|
-
continue
|
|
112
|
-
|
|
113
|
-
# Add a node to the scene
|
|
114
|
-
elif isinstance(bsk_object, Node):
|
|
115
|
-
returns.append(self.node_handler.add(bsk_object)); continue
|
|
116
|
-
|
|
117
|
-
# Add a node to the scene
|
|
118
|
-
elif isinstance(bsk_object, PostProcess):
|
|
119
|
-
returns.append(self.engine.frame.add_post_process(bsk_object)); continue
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
# Recived incompatable type
|
|
123
|
-
else:
|
|
124
|
-
raise ValueError(f'scene.add: Incompatable object add type {type(bsk_object)}')
|
|
125
|
-
|
|
126
|
-
# Return based on what the user passed in
|
|
127
|
-
if not returns: return None
|
|
128
|
-
if len(returns) == 1: return returns[0]
|
|
129
|
-
return returns
|
|
130
|
-
|
|
131
|
-
def remove(self, *objects: Node | None) -> None | Node | list:
|
|
132
|
-
"""
|
|
133
|
-
Removes the given baskilsk object from the scene
|
|
134
|
-
"""
|
|
135
|
-
|
|
136
|
-
# List of all return values for the added objects
|
|
137
|
-
returns = []
|
|
138
|
-
|
|
139
|
-
# Loop through all objects passed in
|
|
140
|
-
for bsk_object in objects:
|
|
141
|
-
|
|
142
|
-
# Considered well defined behavior to remove None
|
|
143
|
-
if isinstance(bsk_object, type(None)):
|
|
144
|
-
continue
|
|
145
|
-
|
|
146
|
-
# Remove a node from the scene
|
|
147
|
-
elif isinstance(bsk_object, Node):
|
|
148
|
-
returns.append(self.node_handler.remove(bsk_object)); continue
|
|
149
|
-
|
|
150
|
-
# Recived incompatable type
|
|
151
|
-
else:
|
|
152
|
-
raise ValueError(f'scene.remove: Incompatable object remove type {type(bsk_object)}')
|
|
153
|
-
|
|
154
|
-
# Return based on what the user passed in
|
|
155
|
-
if not returns: return None
|
|
156
|
-
if len(returns) == 1: return returns[0]
|
|
157
|
-
return returns
|
|
158
|
-
|
|
159
|
-
def set_engine(self, engine: any) -> None:
|
|
160
|
-
"""
|
|
161
|
-
Sets the back references to the engine and creates handlers with the context
|
|
162
|
-
"""
|
|
163
|
-
|
|
164
|
-
if not self.engine:
|
|
165
|
-
self.engine = engine
|
|
166
|
-
self.ctx = engine.ctx
|
|
167
|
-
self.init_handlers()
|
|
168
|
-
else:
|
|
169
|
-
self.engine = engine
|
|
170
|
-
self.ctx = engine.ctx
|
|
171
|
-
|
|
172
|
-
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:
|
|
173
|
-
"""
|
|
174
|
-
Ray cast from any posiiton and forward vector and returns a RaycastResult eith the nearest node.
|
|
175
|
-
If no position or forward is given, uses the scene camera's current position and forward
|
|
176
|
-
"""
|
|
177
|
-
if not position: position = self.camera.position
|
|
178
|
-
if not forward: forward = self.camera.forward
|
|
179
|
-
forward = glm.normalize(forward)
|
|
180
|
-
|
|
181
|
-
# if we are filtering for collisions, use the broad BVH to improve performance
|
|
182
|
-
if has_collisions:
|
|
183
|
-
colliders = self.collider_handler.bvh.get_line_collided(position, forward)
|
|
184
|
-
nodes = [collider.node for collider in colliders]
|
|
185
|
-
|
|
186
|
-
def is_valid(node: Node) -> bool:
|
|
187
|
-
return all([
|
|
188
|
-
has_collisions is None or bool(node.collider) == has_collisions,
|
|
189
|
-
has_physics is None or bool(node.physics_body) == has_physics,
|
|
190
|
-
all(tag in node.tags for tag in tags)
|
|
191
|
-
])
|
|
192
|
-
|
|
193
|
-
nodes: list[Node] = list(filter(lambda node: is_valid(node), nodes))
|
|
194
|
-
|
|
195
|
-
# if we are not filtering for collisions, filter nodes and
|
|
196
|
-
else: nodes = self.node_handler.get_all(collisions=has_collisions, physics=has_physics, tags=tags)
|
|
197
|
-
|
|
198
|
-
# determine closest node
|
|
199
|
-
best_distance, best_point, best_node, best_triangle = max_distance, None, None, None
|
|
200
|
-
position_two = position + forward
|
|
201
|
-
for node in nodes:
|
|
202
|
-
|
|
203
|
-
inv_mat = glm.inverse(node.model_matrix)
|
|
204
|
-
relative_position = inv_mat * position
|
|
205
|
-
relative_forward = glm.normalize(inv_mat * position_two - relative_position)
|
|
206
|
-
|
|
207
|
-
triangles = [node.mesh.indices[i] for i in node.mesh.get_line_collided(relative_position, relative_forward)]
|
|
208
|
-
|
|
209
|
-
for triangle in triangles:
|
|
210
|
-
intersection = moller_trumbore(relative_position, relative_forward, [node.mesh.points[i] for i in triangle])
|
|
211
|
-
if not intersection: continue
|
|
212
|
-
intersection = node.model_matrix * intersection
|
|
213
|
-
distance = glm.length(intersection - position)
|
|
214
|
-
if distance < best_distance:
|
|
215
|
-
best_distance = distance
|
|
216
|
-
best_point = intersection
|
|
217
|
-
best_node = node
|
|
218
|
-
best_triangle = triangle
|
|
219
|
-
|
|
220
|
-
if not best_node: return RaycastResult(best_node, best_point, None)
|
|
221
|
-
|
|
222
|
-
points = [best_node.model_matrix * best_node.mesh.points[t] for t in best_triangle]
|
|
223
|
-
normal = glm.normalize(glm.cross(points[1] - points[0], points[2] - points[0]))
|
|
224
|
-
|
|
225
|
-
return RaycastResult(best_node, best_point, normal)
|
|
226
|
-
|
|
227
|
-
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:
|
|
228
|
-
"""
|
|
229
|
-
Ray casts from the mouse position with respect to the camera. Returns the nearest node that was clicked, if none was clicked, returns None.
|
|
230
|
-
"""
|
|
231
|
-
# derive forward vector from mouse click position
|
|
232
|
-
position = glm.vec2(position)
|
|
233
|
-
inv_proj, inv_view = glm.inverse(self.camera.m_proj), glm.inverse(self.camera.m_view)
|
|
234
|
-
ndc = glm.vec4(2 * position[0] / self.engine.win_size[0] - 1, 1 - 2 * position[1] / self.engine.win_size[1], 1, 1)
|
|
235
|
-
point = inv_proj * ndc
|
|
236
|
-
point /= point.w
|
|
237
|
-
forward = glm.normalize(glm.vec3(inv_view * glm.vec4(point.x, point.y, point.z, 0)))
|
|
238
|
-
|
|
239
|
-
return self.raycast(
|
|
240
|
-
position=self.camera.position,
|
|
241
|
-
forward=forward,
|
|
242
|
-
max_distance=max_distance,
|
|
243
|
-
has_collisions=has_collisions,
|
|
244
|
-
has_physics=has_pshyics,
|
|
245
|
-
tags=tags
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
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:
|
|
249
|
-
"""
|
|
250
|
-
Returns the first node with the given traits
|
|
251
|
-
"""
|
|
252
|
-
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)
|
|
253
|
-
|
|
254
|
-
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]:
|
|
255
|
-
"""
|
|
256
|
-
Returns all nodes with the given traits
|
|
257
|
-
"""
|
|
258
|
-
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)
|
|
259
|
-
|
|
260
|
-
@property
|
|
261
|
-
def camera(self): return self._camera
|
|
262
|
-
@property
|
|
263
|
-
def sky(self): return self._sky
|
|
264
|
-
@property
|
|
265
|
-
def nodes(self): return self.node_handler.nodes
|
|
266
|
-
@property
|
|
267
|
-
def shader(self): return self._shader
|
|
268
|
-
|
|
269
|
-
@camera.setter
|
|
270
|
-
def camera(self, value: Camera):
|
|
271
|
-
if not value: return
|
|
272
|
-
if not isinstance(value, Camera):
|
|
273
|
-
raise TypeError(f'Scene: Invalid camera type: {type(value)}. Expected type bsk.Camera')
|
|
274
|
-
self._camera = value
|
|
275
|
-
self._camera.scene = self
|
|
276
|
-
|
|
277
|
-
@sky.setter
|
|
278
|
-
def sky(self, value: Sky):
|
|
279
|
-
if not isinstance(value, Sky) and not isinstance(value, type(None)):
|
|
280
|
-
raise TypeError(f'Scene: Invalid sky type: {type(value)}. Expected type bsk.Sky or None')
|
|
281
|
-
self._sky = value
|
|
282
|
-
if value: self._sky.write()
|
|
283
|
-
|
|
284
|
-
@shader.setter
|
|
285
|
-
def shader(self, value):
|
|
286
|
-
self._shader = value
|
|
287
|
-
value.set_main(self)
|
|
1
|
+
import moderngl as mgl
|
|
2
|
+
import glm
|
|
3
|
+
import pygame as pg
|
|
4
|
+
|
|
5
|
+
from .mesh.mesh import Mesh
|
|
6
|
+
from .render.material import Material
|
|
7
|
+
from .render.shader import Shader
|
|
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 .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
|
+
from .generic.raycast_result import RaycastResult
|
|
19
|
+
from .render.post_process import PostProcess
|
|
20
|
+
from .render.framebuffer import Framebuffer
|
|
21
|
+
|
|
22
|
+
class Scene():
|
|
23
|
+
engine: ...=None
|
|
24
|
+
"""Parent engine of the scene"""
|
|
25
|
+
ctx: mgl.Context
|
|
26
|
+
"""Reference to the engine context"""
|
|
27
|
+
camera: Camera=None
|
|
28
|
+
""""""
|
|
29
|
+
light_handler: LightHandler=None
|
|
30
|
+
""""""
|
|
31
|
+
physics_engine: PhysicsEngine=None
|
|
32
|
+
""""""
|
|
33
|
+
node_handler: NodeHandler=None
|
|
34
|
+
""""""
|
|
35
|
+
|
|
36
|
+
def __init__(self, engine: ..., shader: Shader=None) -> None:
|
|
37
|
+
"""
|
|
38
|
+
Basilisk scene object. Contains all nodes for the scene
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
self.engine = engine
|
|
42
|
+
self.ctx = engine.ctx
|
|
43
|
+
self.shader = shader if shader else engine.shader
|
|
44
|
+
self.camera = FreeCamera()
|
|
45
|
+
self.light_handler = LightHandler(self)
|
|
46
|
+
self.physics_engine = PhysicsEngine()
|
|
47
|
+
self.node_handler = NodeHandler(self)
|
|
48
|
+
self.particle = ParticleHandler(self)
|
|
49
|
+
self.collider_handler = ColliderHandler(self)
|
|
50
|
+
self.sky = Sky(self)
|
|
51
|
+
self.frame = Frame(self.engine)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def update(self, render: bool=True, nodes: bool=True, particles: bool=True, collisions: bool=True) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Updates the physics and in the scene
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
# Call the internal engine update (for IO and time)
|
|
60
|
+
self.engine._update()
|
|
61
|
+
|
|
62
|
+
# Check that the engine is still running
|
|
63
|
+
if not self.engine.running: return
|
|
64
|
+
|
|
65
|
+
# Update based on the given parameters
|
|
66
|
+
if nodes: self.node_handler.update()
|
|
67
|
+
if particles: self.particle.update()
|
|
68
|
+
|
|
69
|
+
# Update the camera
|
|
70
|
+
self.camera.update()
|
|
71
|
+
if self.engine.event_resize: self.camera.use()
|
|
72
|
+
|
|
73
|
+
if collisions and self.engine.delta_time < 0.5: # TODO this will cause physics to slow down when on low frame rate, this is probabl;y acceptable
|
|
74
|
+
self.collider_handler.resolve_collisions()
|
|
75
|
+
|
|
76
|
+
# Render by default to the scene frame
|
|
77
|
+
if render: self.render()
|
|
78
|
+
|
|
79
|
+
def render(self, target=None) -> None:
|
|
80
|
+
"""
|
|
81
|
+
Renders all the nodes with meshes in the scene
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
target.use() if target else self.frame.use(); self.frame.clear()
|
|
85
|
+
self.engine.shader_handler.write(self)
|
|
86
|
+
self.particle.render()
|
|
87
|
+
self.node_handler.render()
|
|
88
|
+
if self.sky: self.sky.render()
|
|
89
|
+
|
|
90
|
+
if target: return
|
|
91
|
+
# This will show the frame to screen on engine.update()
|
|
92
|
+
self.frame.scene_render(self.ctx.screen)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def add(self, *objects: Node | None) -> None | Node | list:
|
|
97
|
+
"""
|
|
98
|
+
Adds the given object(s) to the scene. Can pass in any scene objects:
|
|
99
|
+
Argument overloads:
|
|
100
|
+
object: Node - Adds the given node to the scene.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
# List of all return values for the added objects
|
|
104
|
+
returns = []
|
|
105
|
+
|
|
106
|
+
# Loop through all objects passed in
|
|
107
|
+
for bsk_object in objects:
|
|
108
|
+
|
|
109
|
+
# Considered well defined behavior to add None
|
|
110
|
+
if isinstance(bsk_object, type(None)):
|
|
111
|
+
continue
|
|
112
|
+
|
|
113
|
+
# Add a node to the scene
|
|
114
|
+
elif isinstance(bsk_object, Node):
|
|
115
|
+
returns.append(self.node_handler.add(bsk_object)); continue
|
|
116
|
+
|
|
117
|
+
# Add a node to the scene
|
|
118
|
+
elif isinstance(bsk_object, PostProcess):
|
|
119
|
+
returns.append(self.engine.frame.add_post_process(bsk_object)); continue
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Recived incompatable type
|
|
123
|
+
else:
|
|
124
|
+
raise ValueError(f'scene.add: Incompatable object add type {type(bsk_object)}')
|
|
125
|
+
|
|
126
|
+
# Return based on what the user passed in
|
|
127
|
+
if not returns: return None
|
|
128
|
+
if len(returns) == 1: return returns[0]
|
|
129
|
+
return returns
|
|
130
|
+
|
|
131
|
+
def remove(self, *objects: Node | None) -> None | Node | list:
|
|
132
|
+
"""
|
|
133
|
+
Removes the given baskilsk object from the scene
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
# List of all return values for the added objects
|
|
137
|
+
returns = []
|
|
138
|
+
|
|
139
|
+
# Loop through all objects passed in
|
|
140
|
+
for bsk_object in objects:
|
|
141
|
+
|
|
142
|
+
# Considered well defined behavior to remove None
|
|
143
|
+
if isinstance(bsk_object, type(None)):
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
# Remove a node from the scene
|
|
147
|
+
elif isinstance(bsk_object, Node):
|
|
148
|
+
returns.append(self.node_handler.remove(bsk_object)); continue
|
|
149
|
+
|
|
150
|
+
# Recived incompatable type
|
|
151
|
+
else:
|
|
152
|
+
raise ValueError(f'scene.remove: Incompatable object remove type {type(bsk_object)}')
|
|
153
|
+
|
|
154
|
+
# Return based on what the user passed in
|
|
155
|
+
if not returns: return None
|
|
156
|
+
if len(returns) == 1: return returns[0]
|
|
157
|
+
return returns
|
|
158
|
+
|
|
159
|
+
def set_engine(self, engine: any) -> None:
|
|
160
|
+
"""
|
|
161
|
+
Sets the back references to the engine and creates handlers with the context
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
if not self.engine:
|
|
165
|
+
self.engine = engine
|
|
166
|
+
self.ctx = engine.ctx
|
|
167
|
+
self.init_handlers()
|
|
168
|
+
else:
|
|
169
|
+
self.engine = engine
|
|
170
|
+
self.ctx = engine.ctx
|
|
171
|
+
|
|
172
|
+
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:
|
|
173
|
+
"""
|
|
174
|
+
Ray cast from any posiiton and forward vector and returns a RaycastResult eith the nearest node.
|
|
175
|
+
If no position or forward is given, uses the scene camera's current position and forward
|
|
176
|
+
"""
|
|
177
|
+
if not position: position = self.camera.position
|
|
178
|
+
if not forward: forward = self.camera.forward
|
|
179
|
+
forward = glm.normalize(forward)
|
|
180
|
+
|
|
181
|
+
# if we are filtering for collisions, use the broad BVH to improve performance
|
|
182
|
+
if has_collisions:
|
|
183
|
+
colliders = self.collider_handler.bvh.get_line_collided(position, forward)
|
|
184
|
+
nodes = [collider.node for collider in colliders]
|
|
185
|
+
|
|
186
|
+
def is_valid(node: Node) -> bool:
|
|
187
|
+
return all([
|
|
188
|
+
has_collisions is None or bool(node.collider) == has_collisions,
|
|
189
|
+
has_physics is None or bool(node.physics_body) == has_physics,
|
|
190
|
+
all(tag in node.tags for tag in tags)
|
|
191
|
+
])
|
|
192
|
+
|
|
193
|
+
nodes: list[Node] = list(filter(lambda node: is_valid(node), nodes))
|
|
194
|
+
|
|
195
|
+
# if we are not filtering for collisions, filter nodes and
|
|
196
|
+
else: nodes = self.node_handler.get_all(collisions=has_collisions, physics=has_physics, tags=tags)
|
|
197
|
+
|
|
198
|
+
# determine closest node
|
|
199
|
+
best_distance, best_point, best_node, best_triangle = max_distance, None, None, None
|
|
200
|
+
position_two = position + forward
|
|
201
|
+
for node in nodes:
|
|
202
|
+
|
|
203
|
+
inv_mat = glm.inverse(node.model_matrix)
|
|
204
|
+
relative_position = inv_mat * position
|
|
205
|
+
relative_forward = glm.normalize(inv_mat * position_two - relative_position)
|
|
206
|
+
|
|
207
|
+
triangles = [node.mesh.indices[i] for i in node.mesh.get_line_collided(relative_position, relative_forward)]
|
|
208
|
+
|
|
209
|
+
for triangle in triangles:
|
|
210
|
+
intersection = moller_trumbore(relative_position, relative_forward, [node.mesh.points[i] for i in triangle])
|
|
211
|
+
if not intersection: continue
|
|
212
|
+
intersection = node.model_matrix * intersection
|
|
213
|
+
distance = glm.length(intersection - position)
|
|
214
|
+
if distance < best_distance:
|
|
215
|
+
best_distance = distance
|
|
216
|
+
best_point = intersection
|
|
217
|
+
best_node = node
|
|
218
|
+
best_triangle = triangle
|
|
219
|
+
|
|
220
|
+
if not best_node: return RaycastResult(best_node, best_point, None)
|
|
221
|
+
|
|
222
|
+
points = [best_node.model_matrix * best_node.mesh.points[t] for t in best_triangle]
|
|
223
|
+
normal = glm.normalize(glm.cross(points[1] - points[0], points[2] - points[0]))
|
|
224
|
+
|
|
225
|
+
return RaycastResult(best_node, best_point, normal)
|
|
226
|
+
|
|
227
|
+
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:
|
|
228
|
+
"""
|
|
229
|
+
Ray casts from the mouse position with respect to the camera. Returns the nearest node that was clicked, if none was clicked, returns None.
|
|
230
|
+
"""
|
|
231
|
+
# derive forward vector from mouse click position
|
|
232
|
+
position = glm.vec2(position)
|
|
233
|
+
inv_proj, inv_view = glm.inverse(self.camera.m_proj), glm.inverse(self.camera.m_view)
|
|
234
|
+
ndc = glm.vec4(2 * position[0] / self.engine.win_size[0] - 1, 1 - 2 * position[1] / self.engine.win_size[1], 1, 1)
|
|
235
|
+
point = inv_proj * ndc
|
|
236
|
+
point /= point.w
|
|
237
|
+
forward = glm.normalize(glm.vec3(inv_view * glm.vec4(point.x, point.y, point.z, 0)))
|
|
238
|
+
|
|
239
|
+
return self.raycast(
|
|
240
|
+
position=self.camera.position,
|
|
241
|
+
forward=forward,
|
|
242
|
+
max_distance=max_distance,
|
|
243
|
+
has_collisions=has_collisions,
|
|
244
|
+
has_physics=has_pshyics,
|
|
245
|
+
tags=tags
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
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:
|
|
249
|
+
"""
|
|
250
|
+
Returns the first node with the given traits
|
|
251
|
+
"""
|
|
252
|
+
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)
|
|
253
|
+
|
|
254
|
+
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]:
|
|
255
|
+
"""
|
|
256
|
+
Returns all nodes with the given traits
|
|
257
|
+
"""
|
|
258
|
+
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)
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def camera(self): return self._camera
|
|
262
|
+
@property
|
|
263
|
+
def sky(self): return self._sky
|
|
264
|
+
@property
|
|
265
|
+
def nodes(self): return self.node_handler.nodes
|
|
266
|
+
@property
|
|
267
|
+
def shader(self): return self._shader
|
|
268
|
+
|
|
269
|
+
@camera.setter
|
|
270
|
+
def camera(self, value: Camera):
|
|
271
|
+
if not value: return
|
|
272
|
+
if not isinstance(value, Camera):
|
|
273
|
+
raise TypeError(f'Scene: Invalid camera type: {type(value)}. Expected type bsk.Camera')
|
|
274
|
+
self._camera = value
|
|
275
|
+
self._camera.scene = self
|
|
276
|
+
|
|
277
|
+
@sky.setter
|
|
278
|
+
def sky(self, value: Sky):
|
|
279
|
+
if not isinstance(value, Sky) and not isinstance(value, type(None)):
|
|
280
|
+
raise TypeError(f'Scene: Invalid sky type: {type(value)}. Expected type bsk.Sky or None')
|
|
281
|
+
self._sky = value
|
|
282
|
+
if value: self._sky.write()
|
|
283
|
+
|
|
284
|
+
@shader.setter
|
|
285
|
+
def shader(self, value):
|
|
286
|
+
self._shader = value
|
|
287
|
+
value.set_main(self)
|
|
288
288
|
if self.light_handler: self.light_handler.write(value)
|