basilisk-engine 0.0.8__py3-none-any.whl → 0.1.0__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_bvh.py +31 -2
- basilisk/collisions/collider.py +6 -5
- basilisk/collisions/collider_handler.py +87 -24
- basilisk/collisions/narrow/contact_manifold.py +92 -0
- basilisk/collisions/narrow/epa.py +13 -8
- basilisk/collisions/narrow/graham_scan.py +25 -0
- basilisk/collisions/narrow/helper.py +7 -1
- basilisk/collisions/narrow/line_intersections.py +107 -0
- basilisk/collisions/narrow/sutherland_hodgman.py +24 -0
- basilisk/draw/draw_handler.py +3 -3
- basilisk/engine.py +16 -3
- basilisk/generic/collisions.py +1 -2
- basilisk/generic/quat.py +10 -2
- basilisk/generic/vec3.py +9 -1
- basilisk/input/mouse.py +3 -3
- basilisk/nodes/node.py +48 -77
- basilisk/nodes/node_handler.py +8 -4
- basilisk/physics/impulse.py +119 -0
- basilisk/physics/physics_engine.py +1 -1
- basilisk/render/batch.py +2 -0
- basilisk/render/camera.py +30 -1
- basilisk/render/chunk_handler.py +10 -1
- basilisk/render/frame.py +2 -2
- basilisk/render/image_handler.py +14 -12
- basilisk/render/light_handler.py +2 -2
- basilisk/render/material.py +13 -13
- basilisk/render/material_handler.py +11 -7
- basilisk/render/shader.py +110 -0
- basilisk/render/shader_handler.py +18 -34
- basilisk/render/sky.py +4 -3
- basilisk/scene.py +3 -2
- basilisk/shaders/geometry.frag +9 -0
- basilisk/shaders/geometry.vert +42 -0
- basilisk/shaders/normal.frag +60 -0
- basilisk/shaders/normal.vert +92 -0
- {basilisk_engine-0.0.8.dist-info → basilisk_engine-0.1.0.dist-info}/METADATA +1 -1
- {basilisk_engine-0.0.8.dist-info → basilisk_engine-0.1.0.dist-info}/RECORD +40 -30
- {basilisk_engine-0.0.8.dist-info → basilisk_engine-0.1.0.dist-info}/WHEEL +0 -0
- {basilisk_engine-0.0.8.dist-info → basilisk_engine-0.1.0.dist-info}/top_level.txt +0 -0
basilisk/generic/quat.py
CHANGED
|
@@ -5,6 +5,7 @@ import numpy as np
|
|
|
5
5
|
class Quat():
|
|
6
6
|
def __init__(self, *args, callback=None):
|
|
7
7
|
self.callback = callback
|
|
8
|
+
self.prev_data = glm.quat(1, 0, 0, 0)
|
|
8
9
|
self.set_data(*args)
|
|
9
10
|
|
|
10
11
|
def normalize(self):
|
|
@@ -94,9 +95,16 @@ class Quat():
|
|
|
94
95
|
def z(self): return self.data.z
|
|
95
96
|
|
|
96
97
|
@data.setter
|
|
97
|
-
def data(self, value: glm.
|
|
98
|
+
def data(self, value: glm.vec3):
|
|
98
99
|
self._data = value
|
|
99
|
-
|
|
100
|
+
|
|
101
|
+
cur = self._data
|
|
102
|
+
prev = self.prev_data
|
|
103
|
+
thresh = 1e-6
|
|
104
|
+
|
|
105
|
+
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):
|
|
106
|
+
self.prev_data = self._data
|
|
107
|
+
self.callback()
|
|
100
108
|
|
|
101
109
|
@w.setter
|
|
102
110
|
def w(self, value):
|
basilisk/generic/vec3.py
CHANGED
|
@@ -5,6 +5,7 @@ import numpy as np
|
|
|
5
5
|
class Vec3():
|
|
6
6
|
def __init__(self, *args, callback=None):
|
|
7
7
|
self.callback = callback
|
|
8
|
+
self.prev_data = glm.vec3(0.0)
|
|
8
9
|
self.set_data(*args)
|
|
9
10
|
|
|
10
11
|
def normalize(self):
|
|
@@ -94,7 +95,14 @@ class Vec3():
|
|
|
94
95
|
@data.setter
|
|
95
96
|
def data(self, value: glm.vec3):
|
|
96
97
|
self._data = value
|
|
97
|
-
|
|
98
|
+
|
|
99
|
+
cur = self._data
|
|
100
|
+
prev = self.prev_data
|
|
101
|
+
thresh = 1e-6
|
|
102
|
+
|
|
103
|
+
if self.callback and (abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh):
|
|
104
|
+
self.prev_data = glm.vec3(self._data)
|
|
105
|
+
self.callback()
|
|
98
106
|
|
|
99
107
|
@x.setter
|
|
100
108
|
def x(self, 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/nodes/node.py
CHANGED
|
@@ -8,6 +8,7 @@ from ..render.material import Material
|
|
|
8
8
|
from ..physics.physics_body import PhysicsBody
|
|
9
9
|
from ..collisions.collider import Collider
|
|
10
10
|
from ..render.chunk import Chunk
|
|
11
|
+
from ..render.shader import Shader
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class Node():
|
|
@@ -53,6 +54,8 @@ class Node():
|
|
|
53
54
|
"""""" # TODO Jonah description
|
|
54
55
|
children: list
|
|
55
56
|
"""List of nodes that this node is a parent of"""
|
|
57
|
+
shader: Shader
|
|
58
|
+
"""Shader that is used to render the node. If none is given, engine default will be used"""
|
|
56
59
|
|
|
57
60
|
def __init__(self, node_handler,
|
|
58
61
|
position: glm.vec3=None,
|
|
@@ -73,7 +76,8 @@ class Node():
|
|
|
73
76
|
collision_group : float=None,
|
|
74
77
|
name: str='',
|
|
75
78
|
tags: list[str]=None,
|
|
76
|
-
static: bool=
|
|
79
|
+
static: bool=None,
|
|
80
|
+
shader: Shader=None
|
|
77
81
|
) -> None:
|
|
78
82
|
"""
|
|
79
83
|
Basilisk node object.
|
|
@@ -86,13 +90,14 @@ class Node():
|
|
|
86
90
|
self.chunk = None
|
|
87
91
|
|
|
88
92
|
# lazy update variables
|
|
89
|
-
self.needs_geometric_center = True
|
|
90
|
-
self.needs_model_matrix = True
|
|
93
|
+
self.needs_geometric_center = True # pos
|
|
94
|
+
self.needs_model_matrix = True # pos, scale, rot
|
|
91
95
|
|
|
92
96
|
# node data
|
|
93
97
|
self.internal_position: Vec3 = Vec3(position) if position else Vec3(0, 0, 0)
|
|
94
98
|
self.internal_scale : Vec3 = Vec3(scale) if scale else Vec3(1, 1, 1)
|
|
95
99
|
self.internal_rotation: Quat = Quat(rotation) if rotation else Quat(1, 0, 0, 0)
|
|
100
|
+
|
|
96
101
|
self.forward = forward if forward else glm.vec3(1, 0, 0)
|
|
97
102
|
self.mesh = mesh if mesh else self.node_handler.scene.engine.cube
|
|
98
103
|
self.material = material if material else None # TODO add default base material
|
|
@@ -106,7 +111,7 @@ class Node():
|
|
|
106
111
|
|
|
107
112
|
# collider
|
|
108
113
|
if collisions:
|
|
109
|
-
if not mesh: raise ValueError('Node: cannot collide if it doezsnt have a mesh')
|
|
114
|
+
if not self.mesh: raise ValueError('Node: cannot collide if it doezsnt have a mesh')
|
|
110
115
|
self.collider: Collider = self.node_handler.scene.collider_handler.add(
|
|
111
116
|
node = self,
|
|
112
117
|
box_mesh = True if collider == 'box' else False,
|
|
@@ -125,31 +130,50 @@ class Node():
|
|
|
125
130
|
# information and recursion
|
|
126
131
|
self.name = name
|
|
127
132
|
self.tags = tags if tags else []
|
|
128
|
-
|
|
133
|
+
if static == None:
|
|
134
|
+
self.static = not(physics or any(self.velocity) or any(self.rotational_velocity))
|
|
135
|
+
else: self.static = static
|
|
129
136
|
self.data_index = 0
|
|
130
137
|
self.children = []
|
|
138
|
+
|
|
139
|
+
# Shader given by user or none for default
|
|
140
|
+
self.shader = shader
|
|
131
141
|
|
|
132
142
|
# default callback functions for node transform
|
|
133
143
|
self.previous_position: Vec3 = Vec3(position) if position else Vec3(0, 0, 0)
|
|
134
144
|
self.previous_scale : Vec3 = Vec3(scale) if scale else Vec3(1, 1, 1)
|
|
135
145
|
self.previous_rotation: Quat = Quat(rotation) if rotation else Quat(1, 0, 0, 0) # TODO Do these need to be the callback class or can they just be glm?
|
|
136
146
|
|
|
137
|
-
self.position_updated = False
|
|
138
|
-
self.scale_updated = False
|
|
139
|
-
self.rotation_updated = False
|
|
140
|
-
|
|
141
147
|
# callback function to be added to the custom Vec3 and Quat classes
|
|
142
148
|
def position_callback():
|
|
143
|
-
|
|
144
|
-
|
|
149
|
+
self.chunk.node_update_callback(self)
|
|
150
|
+
|
|
151
|
+
# update variables
|
|
152
|
+
self.needs_geometric_center = True
|
|
153
|
+
self.needs_model_matrix = True
|
|
154
|
+
if self.collider:
|
|
155
|
+
self.collider.needs_bvh = True
|
|
156
|
+
self.collider.needs_obb = True
|
|
145
157
|
|
|
146
158
|
def scale_callback():
|
|
147
|
-
|
|
148
|
-
|
|
159
|
+
self.chunk.node_update_callback(self)
|
|
160
|
+
|
|
161
|
+
# update variables
|
|
162
|
+
self.needs_model_matrix = True
|
|
163
|
+
if self.collider:
|
|
164
|
+
self.collider.needs_bvh = True
|
|
165
|
+
self.collider.needs_obb = True
|
|
166
|
+
self.collider.needs_half_dimensions = True
|
|
149
167
|
|
|
150
168
|
def rotation_callback():
|
|
151
|
-
|
|
152
|
-
|
|
169
|
+
self.chunk.node_update_callback(self)
|
|
170
|
+
|
|
171
|
+
# update variables
|
|
172
|
+
self.needs_model_matrix = True
|
|
173
|
+
if self.collider:
|
|
174
|
+
self.collider.needs_bvh = True
|
|
175
|
+
self.collider.needs_obb = True
|
|
176
|
+
self.collider.needs_half_dimensions = True
|
|
153
177
|
|
|
154
178
|
self.internal_position.callback = position_callback
|
|
155
179
|
self.internal_scale.callback = scale_callback
|
|
@@ -159,18 +183,8 @@ class Node():
|
|
|
159
183
|
"""
|
|
160
184
|
Updates the node's movement variables based on the delta time
|
|
161
185
|
"""
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
# reset updates
|
|
166
|
-
self.position_updated = False
|
|
167
|
-
self.scale_updated = False
|
|
168
|
-
self.rotation_updated = False
|
|
169
|
-
|
|
170
|
-
self.position += dt * self.velocity
|
|
171
|
-
self.rotation += 0.5 * dt * self.rotation * glm.quat(0, *self.rotational_velocity)
|
|
172
|
-
self.rotation = glm.normalize(self.rotation)
|
|
173
|
-
self.scale = self.scale
|
|
186
|
+
if any(self.velocity): self.position += dt * self.velocity
|
|
187
|
+
if any(self.rotational_velocity): self.rotation = glm.normalize(self.rotation - 0.5 * dt * self.rotation * glm.quat(0, *self.rotational_velocity))
|
|
174
188
|
|
|
175
189
|
if self.physics_body:
|
|
176
190
|
self.velocity += self.physics_body.get_delta_velocity(dt)
|
|
@@ -249,7 +263,7 @@ class Node():
|
|
|
249
263
|
|
|
250
264
|
# rotation
|
|
251
265
|
rotation_matrix = glm.mat3_cast(self.rotation)
|
|
252
|
-
inertia_tensor
|
|
266
|
+
inertia_tensor = rotation_matrix * inertia_tensor * glm.transpose(rotation_matrix)
|
|
253
267
|
|
|
254
268
|
return glm.inverse(inertia_tensor)
|
|
255
269
|
|
|
@@ -347,21 +361,19 @@ class Node():
|
|
|
347
361
|
|
|
348
362
|
@position.setter
|
|
349
363
|
def position(self, value: tuple | list | glm.vec3 | np.ndarray):
|
|
350
|
-
if isinstance(value, glm.vec3): self.internal_position.data =
|
|
364
|
+
if isinstance(value, glm.vec3): self.internal_position.data = value
|
|
351
365
|
elif isinstance(value, tuple) or isinstance(value, list) or isinstance(value, np.ndarray):
|
|
352
366
|
if len(value) != 3: raise ValueError(f'Node: Invalid number of values for position. Expected 3, got {len(value)}')
|
|
353
367
|
self.internal_position.data = glm.vec3(value)
|
|
354
368
|
else: raise TypeError(f'Node: Invalid position value type {type(value)}')
|
|
355
|
-
self.update_position()
|
|
356
369
|
|
|
357
370
|
@scale.setter
|
|
358
371
|
def scale(self, value: tuple | list | glm.vec3 | np.ndarray):
|
|
359
|
-
if isinstance(value, glm.vec3): self.internal_scale.data =
|
|
372
|
+
if isinstance(value, glm.vec3): self.internal_scale.data = value
|
|
360
373
|
elif isinstance(value, tuple) or isinstance(value, list) or isinstance(value, np.ndarray):
|
|
361
374
|
if len(value) != 3: raise ValueError(f'Node: Invalid number of values for scale. Expected 3, got {len(value)}')
|
|
362
375
|
self.internal_scale.data = glm.vec3(value)
|
|
363
376
|
else: raise TypeError(f'Node: Invalid scale value type {type(value)}')
|
|
364
|
-
self.update_scale()
|
|
365
377
|
|
|
366
378
|
@rotation.setter
|
|
367
379
|
def rotation(self, value: tuple | list | glm.vec3 | glm.quat | glm.vec4 | np.ndarray):
|
|
@@ -371,51 +383,10 @@ class Node():
|
|
|
371
383
|
elif len(value) == 4: self.internal_rotation.data = glm.quat(*value)
|
|
372
384
|
else: raise ValueError(f'Node: Invalid number of values for rotation. Expected 3 or 4, got {len(value)}')
|
|
373
385
|
else: raise TypeError(f'Node: Invalid rotation value type {type(value)}')
|
|
374
|
-
self.update_rotation()
|
|
375
|
-
|
|
376
|
-
def update_position(self): # TODO Could these be repeated in a Vec3/Quat Callback function?
|
|
377
|
-
"""
|
|
378
|
-
Updates the rotation
|
|
379
|
-
"""
|
|
380
|
-
|
|
381
|
-
thresh = 0.01
|
|
382
|
-
cur = self.position
|
|
383
|
-
prev = self.previous_position
|
|
384
|
-
|
|
385
|
-
if self.chunk and (abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh):
|
|
386
|
-
self.chunk.node_update_callback(self)
|
|
387
|
-
self.previous_position = self.position
|
|
388
|
-
|
|
389
|
-
def update_scale(self):
|
|
390
|
-
"""
|
|
391
|
-
Updates the rotation
|
|
392
|
-
"""
|
|
393
|
-
|
|
394
|
-
thresh = 0.01
|
|
395
|
-
cur = self.scale
|
|
396
|
-
prev = self.previous_scale
|
|
397
|
-
|
|
398
|
-
if self.chunk and (abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh):
|
|
399
|
-
self.chunk.node_update_callback(self)
|
|
400
|
-
self.previous_scale = self.scale
|
|
401
|
-
|
|
402
|
-
def update_rotation(self):
|
|
403
|
-
"""
|
|
404
|
-
Updates the rotation
|
|
405
|
-
"""
|
|
406
|
-
|
|
407
|
-
thresh = 0.01
|
|
408
|
-
cur = self.rotation
|
|
409
|
-
prev = self.previous_rotation
|
|
410
|
-
|
|
411
|
-
if self.chunk and (abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh or abs(cur.w - prev.w) > thresh):
|
|
412
|
-
self.chunk.node_update_callback(self)
|
|
413
|
-
self.previous_rotation = self.rotation
|
|
414
|
-
|
|
415
386
|
|
|
416
387
|
@forward.setter
|
|
417
388
|
def forward(self, value: tuple | list | glm.vec3 | np.ndarray):
|
|
418
|
-
if isinstance(value, glm.vec3): self._forward =
|
|
389
|
+
if isinstance(value, glm.vec3): self._forward = value
|
|
419
390
|
elif isinstance(value, tuple) or isinstance(value, list) or isinstance(value, np.ndarray):
|
|
420
391
|
if len(value) != 3: raise ValueError(f'Node: Invalid number of values for forward. Expected 3, got {len(value)}')
|
|
421
392
|
self._forward = glm.vec3(value)
|
|
@@ -493,15 +464,15 @@ class Node():
|
|
|
493
464
|
|
|
494
465
|
@x.setter
|
|
495
466
|
def x(self, value: int | float):
|
|
496
|
-
if isinstance(value, int) or isinstance(value, float): self.internal_position.
|
|
467
|
+
if isinstance(value, int) or isinstance(value, float): self.internal_position.x = value
|
|
497
468
|
else: raise TypeError(f'Node: Invalid positional x value type {type(value)}')
|
|
498
469
|
|
|
499
470
|
@y.setter
|
|
500
471
|
def y(self, value: int | float):
|
|
501
|
-
if isinstance(value, int) or isinstance(value, float): self.internal_position.
|
|
472
|
+
if isinstance(value, int) or isinstance(value, float): self.internal_position.y = value
|
|
502
473
|
else: raise TypeError(f'Node: Invalid positional y value type {type(value)}')
|
|
503
474
|
|
|
504
475
|
@z.setter
|
|
505
476
|
def z(self, value: int | float):
|
|
506
|
-
if isinstance(value, int) or isinstance(value, float): self.internal_position.
|
|
477
|
+
if isinstance(value, int) or isinstance(value, float): self.internal_position.z = value
|
|
507
478
|
else: raise TypeError(f'Node: Invalid positional z value type {type(value)}')
|
basilisk/nodes/node_handler.py
CHANGED
|
@@ -25,7 +25,9 @@ class NodeHandler():
|
|
|
25
25
|
"""
|
|
26
26
|
Updates the nodes and chunks in the scene
|
|
27
27
|
"""
|
|
28
|
-
for node in self.nodes:
|
|
28
|
+
for node in self.nodes:
|
|
29
|
+
if node.static: continue
|
|
30
|
+
node.update(self.scene.engine.delta_time)
|
|
29
31
|
self.chunk_handler.update()
|
|
30
32
|
|
|
31
33
|
def render(self):
|
|
@@ -89,6 +91,8 @@ class NodeHandler():
|
|
|
89
91
|
Removes a node and all of its children from their handlers
|
|
90
92
|
"""
|
|
91
93
|
# TODO add support for recursive nodes
|
|
92
|
-
if node
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
if node in self.nodes:
|
|
95
|
+
if node.physics_body: self.scene.physics_engine.remove(node.physics_body)
|
|
96
|
+
if node.collider: self.scene.collider_handler.remove(node.collider)
|
|
97
|
+
node.node_handler = None
|
|
98
|
+
self.nodes.remove(node)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import glm
|
|
2
|
+
from ..nodes.node import Node
|
|
3
|
+
|
|
4
|
+
def calculate_collisions(normal:glm.vec3, node1: Node, node2: Node, contact_points:list[glm.vec3], inv_inertia1:glm.mat3x3, inv_inertia2:glm.mat3x3, center1:glm.vec3, center2:glm.vec3) -> None:
|
|
5
|
+
"""
|
|
6
|
+
Resolve the collisions between two objects with multiple contact points
|
|
7
|
+
"""
|
|
8
|
+
physics_body1 = node1.physics_body
|
|
9
|
+
physics_body2 = node2.physics_body
|
|
10
|
+
collider1 = node1.collider
|
|
11
|
+
collider2 = node2.collider
|
|
12
|
+
|
|
13
|
+
# determine whether or not the colliders have physics
|
|
14
|
+
has_physics1, has_physics2 = physics_body1 is not None, physics_body2 is not None
|
|
15
|
+
|
|
16
|
+
# get physics data from valid bodies
|
|
17
|
+
if has_physics1: inv_mass1 = 1 / physics_body1.mass
|
|
18
|
+
if has_physics2: inv_mass2 = 1 / physics_body2.mass
|
|
19
|
+
|
|
20
|
+
# gets coefficients
|
|
21
|
+
elasticity = max(collider1.elasticity, collider2.elasticity)
|
|
22
|
+
kinetic = min(collider1.kinetic_friction, collider2.kinetic_friction)
|
|
23
|
+
static = min(collider1.static_friction, collider2.static_friction)
|
|
24
|
+
num_points = len(contact_points)
|
|
25
|
+
|
|
26
|
+
total_impulse = glm.vec3(0, 0, 0)
|
|
27
|
+
|
|
28
|
+
# calculate impulses from contact points
|
|
29
|
+
if has_physics1 and has_physics2:
|
|
30
|
+
for contact_point in contact_points:
|
|
31
|
+
|
|
32
|
+
# apply impulse based reduced by total points
|
|
33
|
+
radius1, radius2 = contact_point - center1, contact_point - center2
|
|
34
|
+
impulse = calculate_impulse2(node1, node2, inv_mass1, inv_mass2, node1.rotational_velocity, node2.rotational_velocity, radius1, radius2, inv_inertia1, inv_inertia2, elasticity, kinetic, static, normal)
|
|
35
|
+
total_impulse += impulse / num_points
|
|
36
|
+
|
|
37
|
+
# apply impulses
|
|
38
|
+
apply_impulse(radius1, total_impulse, inv_inertia1, inv_mass1, node1)
|
|
39
|
+
apply_impulse(radius2, -total_impulse, inv_inertia2, inv_mass2, node2)
|
|
40
|
+
|
|
41
|
+
elif has_physics1:
|
|
42
|
+
for contact_point in contact_points:
|
|
43
|
+
radius = contact_point - center1
|
|
44
|
+
impulse = calculate_impluse1(node1, inv_mass1, node1.rotational_velocity, radius, inv_inertia1, elasticity, kinetic, static, normal)
|
|
45
|
+
total_impulse += impulse / num_points
|
|
46
|
+
|
|
47
|
+
# apply impulses
|
|
48
|
+
apply_impulse(radius, total_impulse, inv_inertia1, inv_mass1, node1)
|
|
49
|
+
|
|
50
|
+
else: # only physics body 2
|
|
51
|
+
for contact_point in contact_points:
|
|
52
|
+
radius = contact_point - center2
|
|
53
|
+
impulse = calculate_impluse1(node2, inv_mass2, node2.rotational_velocity, radius, inv_inertia2, elasticity, kinetic, static, normal)
|
|
54
|
+
total_impulse += impulse / num_points
|
|
55
|
+
|
|
56
|
+
# apply impulse
|
|
57
|
+
apply_impulse(radius, total_impulse, inv_inertia2, inv_mass2, node2)
|
|
58
|
+
|
|
59
|
+
def calculate_impluse1(node: Node, inv_mass, omega, radius, inv_inertia, elasticity, kinetic, static, normal) -> glm.vec3:
|
|
60
|
+
"""
|
|
61
|
+
Calculates the impulse from a collision including friction from the impulse
|
|
62
|
+
"""
|
|
63
|
+
# determine if mass needs to be calculated TODO determine if this is a good check
|
|
64
|
+
if glm.dot(radius, node.velocity) < 0: return glm.vec3(0, 0, 0)
|
|
65
|
+
|
|
66
|
+
# normal impulse
|
|
67
|
+
relative_velocity = node.velocity + glm.cross(omega, radius)
|
|
68
|
+
relative_normal_velocity = glm.dot(relative_velocity, normal)
|
|
69
|
+
|
|
70
|
+
# calculate denominator
|
|
71
|
+
denominator = inv_mass + glm.dot(normal, glm.cross(inv_inertia * glm.cross(radius, normal), radius))
|
|
72
|
+
|
|
73
|
+
# calculate normal impulse
|
|
74
|
+
normal_impulse_magnitude = -(1 + elasticity) * relative_normal_velocity / denominator
|
|
75
|
+
normal_impulse = normal_impulse_magnitude * normal
|
|
76
|
+
|
|
77
|
+
# friction impulse
|
|
78
|
+
rel_tan_vel = relative_velocity - glm.dot(relative_velocity, normal) * normal
|
|
79
|
+
rel_tan_vel_len = glm.length(rel_tan_vel)
|
|
80
|
+
|
|
81
|
+
if rel_tan_vel_len < 1e-7: friction_impulse = glm.vec3(0, 0, 0) # no friction
|
|
82
|
+
elif rel_tan_vel_len < 1e-2: friction_impulse = -static * glm.length(normal_impulse) * glm.normalize(rel_tan_vel) # static friction
|
|
83
|
+
else: friction_impulse = -kinetic * glm.length(normal_impulse) * glm.normalize(rel_tan_vel) # kinetic friction
|
|
84
|
+
|
|
85
|
+
# return total impulse
|
|
86
|
+
return normal_impulse + friction_impulse
|
|
87
|
+
|
|
88
|
+
def calculate_impulse2(node1: Node, node2: Node, inv_mass1, inv_mass2, omega1, omega2, radius1, radius2, inv_inertia1, inv_inertia2, elasticity, kinetic, static, normal) -> glm.vec3:
|
|
89
|
+
"""
|
|
90
|
+
Calculates the impulse from a collision including friction from the impulse
|
|
91
|
+
"""
|
|
92
|
+
# normal impulse
|
|
93
|
+
relative_velocity = node1.velocity + glm.cross(omega1, radius1) - (node2.velocity + glm.cross(omega2, radius2))
|
|
94
|
+
relative_normal_velocity = glm.dot(relative_velocity, normal)
|
|
95
|
+
# calculate denominator
|
|
96
|
+
term1 = inv_mass1 + inv_mass2
|
|
97
|
+
term2 = glm.dot(normal, glm.cross(inv_inertia1 * glm.cross(radius1, normal), radius1) + glm.cross(inv_inertia2 * glm.cross(radius2, normal), radius2))
|
|
98
|
+
# calculate normal impulse
|
|
99
|
+
normal_impulse = -(1 + elasticity) * relative_normal_velocity / (term1 + term2) * normal
|
|
100
|
+
|
|
101
|
+
# friction impulse
|
|
102
|
+
rel_tan_vel = relative_velocity - glm.dot(relative_velocity, normal) * normal
|
|
103
|
+
rel_tan_vel_len = glm.length(rel_tan_vel)
|
|
104
|
+
if rel_tan_vel_len < 1e-7: friction_impulse = glm.vec3(0, 0, 0)
|
|
105
|
+
elif rel_tan_vel_len < 1e-2: friction_impulse = -static * glm.length(normal_impulse) * glm.normalize(rel_tan_vel)
|
|
106
|
+
else: friction_impulse = -kinetic * glm.length(normal_impulse) * glm.normalize(rel_tan_vel)
|
|
107
|
+
# return total impulse
|
|
108
|
+
return normal_impulse + friction_impulse
|
|
109
|
+
|
|
110
|
+
def apply_impulse(radius, impulse_signed, inv_inertia, inv_mass, node: Node) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Applies the given impulse to the physics body, changing translational and rotational velcoity.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
# Update linear velocity
|
|
116
|
+
node.velocity += impulse_signed * inv_mass
|
|
117
|
+
|
|
118
|
+
# update rotational velcoity
|
|
119
|
+
node.rotational_velocity += inv_inertia * glm.cross(radius, impulse_signed)
|
|
@@ -15,7 +15,7 @@ class PhysicsEngine():
|
|
|
15
15
|
|
|
16
16
|
def __init__(self, accelerations: list[glm.vec3] = None, rotational_accelerations: list[glm.vec3] = None, forces: list[glm.vec3] = None, torques: list[glm.vec3] = None) -> None:
|
|
17
17
|
self.physics_bodies = []
|
|
18
|
-
self.accelerations = accelerations if accelerations else [glm.vec3(0, -
|
|
18
|
+
self.accelerations = accelerations if accelerations else [glm.vec3(0, -9.8, 0)]
|
|
19
19
|
self.rotational_accelerations = rotational_accelerations if rotational_accelerations else []
|
|
20
20
|
self.forces = forces if forces else []
|
|
21
21
|
self.torques = torques if torques else []
|
basilisk/render/batch.py
CHANGED
basilisk/render/camera.py
CHANGED
|
@@ -145,7 +145,7 @@ class FreeCamera(Camera):
|
|
|
145
145
|
"""
|
|
146
146
|
Checks for button presses and updates vectors accordingly.
|
|
147
147
|
"""
|
|
148
|
-
velocity = SPEED * self.engine.delta_time
|
|
148
|
+
velocity = (SPEED + self.engine.keys[pg.K_CAPSLOCK] * 10) * self.engine.delta_time
|
|
149
149
|
keys = self.engine.keys
|
|
150
150
|
if keys[pg.K_w]:
|
|
151
151
|
self.position += glm.normalize(glm.vec3(self.forward.x, 0, self.forward.z)) * velocity
|
|
@@ -161,6 +161,35 @@ class FreeCamera(Camera):
|
|
|
161
161
|
self.position -= self.UP * velocity
|
|
162
162
|
|
|
163
163
|
|
|
164
|
+
class FollowCamera(FreeCamera):
|
|
165
|
+
def __init__(self, parent, position=(0, 0, 20), yaw=-90, pitch=0, offset=(0, 0, 0)):
|
|
166
|
+
super().__init__(position, yaw, pitch)
|
|
167
|
+
self.parent = parent
|
|
168
|
+
self.offest = glm.vec3(offset)
|
|
169
|
+
|
|
170
|
+
def move(self) -> None:
|
|
171
|
+
"""
|
|
172
|
+
Moves the camera to the parent node
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
self.position = self.parent.position + self.offest
|
|
176
|
+
|
|
177
|
+
class OrbitCamera(FreeCamera):
|
|
178
|
+
def __init__(self, parent, position=(0, 0, 20), yaw=-90, pitch=0, distance=5):
|
|
179
|
+
self.parent = parent
|
|
180
|
+
self.distance = distance
|
|
181
|
+
super().__init__(position, yaw, pitch)
|
|
182
|
+
|
|
183
|
+
def get_view_matrix(self) -> glm.mat4x4:
|
|
184
|
+
return glm.lookAt(self.position, self.parent.position, self.up)
|
|
185
|
+
|
|
186
|
+
def move(self) -> None:
|
|
187
|
+
"""
|
|
188
|
+
Moves the camera to the parent node
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
self.position = self.parent.position - glm.normalize(self.forward) * self.distance
|
|
192
|
+
|
|
164
193
|
class StaticCamera(Camera):
|
|
165
194
|
def __init__(self, position=(0, 0, 20), yaw=-90, pitch=0):
|
|
166
195
|
super().__init__(position, yaw, pitch)
|
basilisk/render/chunk_handler.py
CHANGED
|
@@ -28,7 +28,7 @@ class ChunkHandler():
|
|
|
28
28
|
self.scene = scene
|
|
29
29
|
self.engine = scene.engine
|
|
30
30
|
self.ctx = scene.engine.ctx
|
|
31
|
-
self.program = scene.
|
|
31
|
+
self.program = scene.engine.shader.program
|
|
32
32
|
|
|
33
33
|
# List for the dynamic and static chunk dictionaries | [dyanmic: dict, static: dict]
|
|
34
34
|
self.chunks = [{} , {} ]
|
|
@@ -58,6 +58,8 @@ class ChunkHandler():
|
|
|
58
58
|
Updates all the chunks that have been updated since the last frame.
|
|
59
59
|
"""
|
|
60
60
|
|
|
61
|
+
self.program = self.scene.engine.shader.program
|
|
62
|
+
|
|
61
63
|
# Loop through the set of updated chunk keys and update the chunk
|
|
62
64
|
removes = []
|
|
63
65
|
|
|
@@ -76,6 +78,13 @@ class ChunkHandler():
|
|
|
76
78
|
# Clears the set of updated chunks so that they are not updated unless they are updated again
|
|
77
79
|
self.updated_chunks = [set(), set()]
|
|
78
80
|
|
|
81
|
+
def update_all(self):
|
|
82
|
+
self.program = self.scene.engine.shader.program
|
|
83
|
+
for chunk in self.chunks[0].values():
|
|
84
|
+
self.updated_chunks[0].add(chunk)
|
|
85
|
+
for chunk in self.chunks[1].values():
|
|
86
|
+
self.updated_chunks[1].add(chunk)
|
|
87
|
+
|
|
79
88
|
def add(self, node: Node) -> Node:
|
|
80
89
|
"""
|
|
81
90
|
Adds an existing node to its chunk. Updates the node's chunk reference
|
basilisk/render/frame.py
CHANGED
|
@@ -88,9 +88,9 @@ class Frame:
|
|
|
88
88
|
"""
|
|
89
89
|
|
|
90
90
|
# Read the shaders
|
|
91
|
-
with open(f'
|
|
91
|
+
with open(self.engine.root + f'/shaders/{vert}.vert') as file:
|
|
92
92
|
vertex_shader = file.read()
|
|
93
|
-
with open(f'
|
|
93
|
+
with open(self.engine.root + f'/shaders/{frag}.frag') as file:
|
|
94
94
|
fragment_shader = file.read()
|
|
95
95
|
|
|
96
96
|
# Create the program
|
basilisk/render/image_handler.py
CHANGED
|
@@ -43,8 +43,7 @@ class ImageHandler():
|
|
|
43
43
|
if image in self.images: return
|
|
44
44
|
|
|
45
45
|
self.images.append(image)
|
|
46
|
-
self.write(
|
|
47
|
-
self.write(self.scene.shader_handler.programs['draw'])
|
|
46
|
+
self.write(regenerate=True)
|
|
48
47
|
|
|
49
48
|
def generate_texture_array(self) -> None:
|
|
50
49
|
"""
|
|
@@ -77,20 +76,23 @@ class ImageHandler():
|
|
|
77
76
|
self.texture_arrays[size].filter = (mgl.LINEAR_MIPMAP_LINEAR, mgl.LINEAR)
|
|
78
77
|
self.texture_arrays[size].anisotropy = 32.0
|
|
79
78
|
|
|
80
|
-
def write(self,
|
|
79
|
+
def write(self, regenerate=False) -> None:
|
|
81
80
|
"""
|
|
82
|
-
Writes all texture arrays to
|
|
83
|
-
Args:
|
|
84
|
-
shader_program: mgl.Program:
|
|
85
|
-
Destination of the texture array write
|
|
81
|
+
Writes all texture arrays to shaders that use images
|
|
86
82
|
"""
|
|
87
83
|
|
|
88
|
-
self.generate_texture_array()
|
|
84
|
+
if regenerate: self.generate_texture_array()
|
|
85
|
+
|
|
86
|
+
if not self.texture_arrays: return
|
|
87
|
+
|
|
88
|
+
for shader in self.engine.scene.shader_handler.shaders.values():
|
|
89
|
+
if 'textureArrays[5]' not in shader.uniforms: continue
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
for i, size in enumerate(texture_sizes):
|
|
92
|
+
if not size in self.texture_arrays: continue
|
|
93
|
+
if not self.texture_arrays[size]: continue
|
|
94
|
+
shader.program[f'textureArrays[{i}].array'] = i + 3
|
|
95
|
+
self.texture_arrays[size].use(location=i+3)
|
|
94
96
|
|
|
95
97
|
def get(self, identifier: str | int) -> any:
|
|
96
98
|
"""
|
basilisk/render/light_handler.py
CHANGED
|
@@ -38,9 +38,9 @@ class LightHandler():
|
|
|
38
38
|
Writes all the lights in a scene to the given shader program
|
|
39
39
|
"""
|
|
40
40
|
|
|
41
|
-
if not program: program = self.
|
|
41
|
+
if not program: program = self.engine.shader.program
|
|
42
42
|
|
|
43
|
-
if directional and self.directional_lights:
|
|
43
|
+
if directional and self.directional_lights and 'numDirLights' in self.engine.shader.uniforms:
|
|
44
44
|
|
|
45
45
|
program['numDirLights'].write(glm.int32(len(self.directional_lights)))
|
|
46
46
|
|