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.

Files changed (40) hide show
  1. basilisk/__init__.py +3 -1
  2. basilisk/collisions/broad/broad_bvh.py +31 -2
  3. basilisk/collisions/collider.py +6 -5
  4. basilisk/collisions/collider_handler.py +87 -24
  5. basilisk/collisions/narrow/contact_manifold.py +92 -0
  6. basilisk/collisions/narrow/epa.py +13 -8
  7. basilisk/collisions/narrow/graham_scan.py +25 -0
  8. basilisk/collisions/narrow/helper.py +7 -1
  9. basilisk/collisions/narrow/line_intersections.py +107 -0
  10. basilisk/collisions/narrow/sutherland_hodgman.py +24 -0
  11. basilisk/draw/draw_handler.py +3 -3
  12. basilisk/engine.py +16 -3
  13. basilisk/generic/collisions.py +1 -2
  14. basilisk/generic/quat.py +10 -2
  15. basilisk/generic/vec3.py +9 -1
  16. basilisk/input/mouse.py +3 -3
  17. basilisk/nodes/node.py +48 -77
  18. basilisk/nodes/node_handler.py +8 -4
  19. basilisk/physics/impulse.py +119 -0
  20. basilisk/physics/physics_engine.py +1 -1
  21. basilisk/render/batch.py +2 -0
  22. basilisk/render/camera.py +30 -1
  23. basilisk/render/chunk_handler.py +10 -1
  24. basilisk/render/frame.py +2 -2
  25. basilisk/render/image_handler.py +14 -12
  26. basilisk/render/light_handler.py +2 -2
  27. basilisk/render/material.py +13 -13
  28. basilisk/render/material_handler.py +11 -7
  29. basilisk/render/shader.py +110 -0
  30. basilisk/render/shader_handler.py +18 -34
  31. basilisk/render/sky.py +4 -3
  32. basilisk/scene.py +3 -2
  33. basilisk/shaders/geometry.frag +9 -0
  34. basilisk/shaders/geometry.vert +42 -0
  35. basilisk/shaders/normal.frag +60 -0
  36. basilisk/shaders/normal.vert +92 -0
  37. {basilisk_engine-0.0.8.dist-info → basilisk_engine-0.1.0.dist-info}/METADATA +1 -1
  38. {basilisk_engine-0.0.8.dist-info → basilisk_engine-0.1.0.dist-info}/RECORD +40 -30
  39. {basilisk_engine-0.0.8.dist-info → basilisk_engine-0.1.0.dist-info}/WHEEL +0 -0
  40. {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.quat):
98
+ def data(self, value: glm.vec3):
98
99
  self._data = value
99
- if self.callback and all(abs(self.data[i] - value[i]) > 1e-12 for i in range(4)): self.callback()
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
- if self.callback and all(abs(self.data[i] - value[i]) > 1e-12 for i in range(3)): self.callback()
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.previous_buttons[0] and not self.buttons[0]
37
+ def click(self): return self.buttons[0] and not self.previous_buttons[0]
38
38
  @property
39
- def middle_click(self): return self.previous_buttons[1] and not self.buttons[1]
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.previous_buttons[2] and not self.buttons[2]
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=True
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
- self.static = static and not (self.physics_body or self.velocity or self.rotational_velocity)
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
- print('position')
144
- self.position_updated = True
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
- print('scale')
148
- self.scale_updated = True
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
- print('rotation')
152
- self.rotation_updated = True
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
- # update all hard-to-update variables
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 = rotation_matrix * inertia_tensor * glm.transpose(rotation_matrix)
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 = glm.vec3(value)
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 = glm.vec3(value)
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 = glm.vec3(value)
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.data.x = value
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.data.y = value
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.data.z = value
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)}')
@@ -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: node.update(self.scene.engine.delta_time)
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.physics_body: self.scene.physics_engine.remove(node.physics_body)
93
- if node.collider: self.scene.collider_handler.remove(node.collider)
94
- node.node_handler = None
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, -1, 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
@@ -35,6 +35,8 @@ class Batch():
35
35
  Returns True if batch was successful.
36
36
  """
37
37
 
38
+ self.program = self.chunk.chunk_handler.program
39
+
38
40
  # Empty list to contain all vertex data of models in the chunk
39
41
  batch_data = []
40
42
 
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)
@@ -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.shader_handler.programs['batch']
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'basilisk/shaders/{vert}.vert') as file:
91
+ with open(self.engine.root + f'/shaders/{vert}.vert') as file:
92
92
  vertex_shader = file.read()
93
- with open(f'basilisk/shaders/{frag}.frag') as file:
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
@@ -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(self.scene.shader_handler.programs['batch'])
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, shader_program: mgl.Program) -> None:
79
+ def write(self, regenerate=False) -> None:
81
80
  """
82
- Writes all texture arrays to the given shader program
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
- for i, size in enumerate(texture_sizes):
91
- if not size in self.texture_arrays: continue
92
- shader_program[f'textureArrays[{i}].array'] = i + 3
93
- self.texture_arrays[size].use(location=i+3)
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
  """
@@ -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.scene.shader_handler.programs['batch']
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