basilisk-engine 0.1.0__py3-none-any.whl → 0.1.1__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 (55) hide show
  1. basilisk/collisions/broad/broad_aabb.py +8 -1
  2. basilisk/collisions/broad/broad_bvh.py +8 -1
  3. basilisk/collisions/collider.py +15 -6
  4. basilisk/collisions/collider_handler.py +70 -68
  5. basilisk/collisions/narrow/contact_manifold.py +9 -10
  6. basilisk/collisions/narrow/dataclasses.py +27 -0
  7. basilisk/collisions/narrow/deprecated.py +47 -0
  8. basilisk/collisions/narrow/epa.py +11 -10
  9. basilisk/collisions/narrow/gjk.py +15 -14
  10. basilisk/collisions/narrow/helper.py +8 -7
  11. basilisk/collisions/narrow/sutherland_hodgman.py +52 -0
  12. basilisk/draw/draw_handler.py +5 -3
  13. basilisk/engine.py +14 -5
  14. basilisk/generic/abstract_custom.py +134 -0
  15. basilisk/generic/collisions.py +46 -0
  16. basilisk/generic/quat.py +77 -66
  17. basilisk/generic/vec3.py +91 -67
  18. basilisk/mesh/cube.py +20 -2
  19. basilisk/mesh/mesh.py +69 -54
  20. basilisk/mesh/mesh_from_data.py +106 -21
  21. basilisk/mesh/narrow_aabb.py +10 -1
  22. basilisk/mesh/narrow_bvh.py +9 -1
  23. basilisk/nodes/node.py +169 -30
  24. basilisk/nodes/node_handler.py +51 -30
  25. basilisk/particles/__init__.py +0 -0
  26. basilisk/particles/particle_handler.py +55 -0
  27. basilisk/particles/particle_renderer.py +87 -0
  28. basilisk/physics/impulse.py +7 -13
  29. basilisk/physics/physics_body.py +10 -2
  30. basilisk/physics/physics_engine.py +1 -2
  31. basilisk/render/batch.py +2 -2
  32. basilisk/render/camera.py +5 -0
  33. basilisk/render/chunk.py +19 -4
  34. basilisk/render/chunk_handler.py +33 -26
  35. basilisk/render/image.py +1 -1
  36. basilisk/render/image_handler.py +4 -3
  37. basilisk/render/light_handler.py +16 -11
  38. basilisk/render/material.py +25 -1
  39. basilisk/render/material_handler.py +22 -13
  40. basilisk/render/shader.py +7 -7
  41. basilisk/render/shader_handler.py +13 -12
  42. basilisk/render/sky.py +5 -3
  43. basilisk/scene.py +114 -32
  44. basilisk/shaders/batch.frag +40 -11
  45. basilisk/shaders/batch.vert +14 -7
  46. basilisk/shaders/normal.frag +5 -5
  47. basilisk/shaders/normal.vert +8 -3
  48. basilisk/shaders/particle.frag +72 -0
  49. basilisk/shaders/particle.vert +85 -0
  50. {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.1.dist-info}/METADATA +5 -5
  51. basilisk_engine-0.1.1.dist-info/RECORD +95 -0
  52. basilisk/shaders/image.png +0 -0
  53. basilisk_engine-0.1.0.dist-info/RECORD +0 -88
  54. {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.1.dist-info}/WHEEL +0 -0
  55. {basilisk_engine-0.1.0.dist-info → basilisk_engine-0.1.1.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ import glm
4
4
  from math import cos, sin, atan2
5
5
  from ..render.image import Image
6
6
  from .font_renderer import FontRenderer
7
+ from ..render.shader import Shader
7
8
 
8
9
  class DrawHandler():
9
10
  engine: ...
@@ -27,8 +28,9 @@ class DrawHandler():
27
28
  self.engine = scene.engine
28
29
  self.ctx = scene.engine.ctx
29
30
 
30
- # Get the program
31
- self.program = self.scene.shader_handler.shaders['draw'].program
31
+ # Get the shader
32
+ root = self.engine.root
33
+ self.shader = self.scene.shader_handler.add(Shader(self.engine, root + '/shaders/draw.vert', root + '/shaders/draw.frag'))
32
34
 
33
35
  # Initialize draw data as blank
34
36
  self.draw_data = []
@@ -52,7 +54,7 @@ class DrawHandler():
52
54
 
53
55
  # Create buffer and VAO
54
56
  self.vbo = self.ctx.buffer(data)
55
- self.vao = self.ctx.vertex_array(self.program, [(self.vbo, '2f 4f 1i', *['in_position', 'in_color', 'in_uses_image'])], skip_errors=True)
57
+ self.vao = self.ctx.vertex_array(self.shader.program, [(self.vbo, '2f 4f 1i', *['in_position', 'in_color', 'in_uses_image'])], skip_errors=True)
56
58
 
57
59
  # Render the VAO
58
60
  self.ctx.enable(mgl.BLEND)
basilisk/engine.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import os
2
+ from sys import platform
2
3
  os.environ['PYGAME_HIDE_SUPPORT_PROMPT'] = "hide"
3
4
  import pygame as pg
4
5
  import moderngl as mgl
@@ -36,7 +37,7 @@ class Engine():
36
37
  root: str
37
38
  """Path to the root directory containing internal data"""
38
39
 
39
- def __init__(self, win_size=(800, 800), title="Basilisk Engine", vsync=False, grab_mouse=True, headless=False) -> None:
40
+ def __init__(self, win_size=(800, 800), title="Basilisk Engine", vsync=None, grab_mouse=True, headless=False) -> None:
40
41
  """
41
42
  Basilisk Engine Class. Sets up the engine enviornment and allows the user to interact with Basilisk
42
43
  Args:
@@ -49,6 +50,11 @@ class Engine():
49
50
  headless: bool
50
51
  Flag for headless rendering
51
52
  """
53
+
54
+ if platform == 'win32' : self.platform = 'windows'
55
+ elif platform == 'darwin': self.platform = 'mac'
56
+ else: self.platform = 'linux'
57
+
52
58
  # Save the window size
53
59
  self.win_size = win_size
54
60
 
@@ -57,6 +63,8 @@ class Engine():
57
63
  pg.display.gl_set_attribute(pg.GL_CONTEXT_MAJOR_VERSION, 3)
58
64
  pg.display.gl_set_attribute(pg.GL_CONTEXT_MINOR_VERSION, 3)
59
65
  pg.display.gl_set_attribute(pg.GL_CONTEXT_PROFILE_MASK, pg.GL_CONTEXT_PROFILE_CORE)
66
+ # Check vsync against platform defaults
67
+ if vsync == None: vsync = True if platform == 'linux' else False
60
68
  # Pygame display init
61
69
  if headless:
62
70
  pg.display.set_mode((300, 50), vsync=vsync, flags=pg.OPENGL | pg.DOUBLEBUF)
@@ -93,6 +101,7 @@ class Engine():
93
101
 
94
102
  # Load a default shader
95
103
  self.shader = Shader(self, self.root + '/shaders/batch.vert', self.root + '/shaders/batch.frag')
104
+ self.shader.hash = self.shader.hash + hash('engine_shader')
96
105
 
97
106
  # Set the scene to running
98
107
  self.running = True
@@ -107,6 +116,9 @@ class Engine():
107
116
  self.time += self.delta_time
108
117
  pg.display.set_caption(f"FPS: {round(self.clock.get_fps())}")
109
118
 
119
+ # Update the previous input lists for the next frame
120
+ self.previous_keys = self.keys
121
+
110
122
  # Get inputs and events
111
123
  self.events = pg.event.get()
112
124
  self.keys = pg.key.get_pressed()
@@ -130,8 +142,6 @@ class Engine():
130
142
  # Render after the scene and engine has been updated
131
143
  self.render()
132
144
 
133
- # Update the previous input lists for the next frame
134
- self.previous_keys = self.keys
135
145
 
136
146
  def render(self) -> None:
137
147
  """
@@ -179,9 +189,8 @@ class Engine():
179
189
  self._scene = value
180
190
  if self._scene:
181
191
  self._scene.set_engine(self)
182
- self.shader.use()
183
192
 
184
193
  @shader.setter
185
194
  def shader(self, value):
186
195
  self._shader = value
187
- if self.scene: value.use()
196
+ if self.scene: value.set_main()
@@ -0,0 +1,134 @@
1
+ import glm
2
+ import numpy as np
3
+
4
+
5
+ class Custom():
6
+
7
+ def normalize(self):
8
+ """
9
+ Inplace normalizes the vector
10
+ """
11
+ self.data = glm.normalize(self.data)
12
+ return self
13
+
14
+ def apply_function(): ... # will be overridden by child custom classes
15
+
16
+ # math functions
17
+ def add(self, other):
18
+ def func(a, b): return a + b
19
+ return self.apply_function(other, func, 'addition')
20
+
21
+ def subtract(self, other):
22
+ def func(a, b): return a - b
23
+ return self.apply_function(other, func, 'subtraction')
24
+
25
+ def rhs_subtract(self, other):
26
+ def func(a, b): return b - a
27
+ return self.apply_function(other, func, 'subtraction')
28
+
29
+ def multiply(self, other):
30
+ def func(a, b): return a * b
31
+ return self.apply_function(other, func, 'multiplication')
32
+
33
+ def divide(self, other):
34
+ def func(a, b): return a / b
35
+ return self.apply_function(other, func, 'division')
36
+
37
+ def rhs_divide(self, other):
38
+ def func(a, b): return b / a
39
+ return self.apply_function(other, func, 'division')
40
+
41
+ def floor_divide(self, other):
42
+ def func(a, b): return a // b
43
+ return self.apply_function(other, func, 'division')
44
+
45
+ def rhs_floor_divide(self, other):
46
+ def func(a, b): return b // a
47
+ return self.apply_function(other, func, 'division')
48
+
49
+ def mod(self, other):
50
+ def func(a, b): return a % b
51
+ return self.apply_function(other, func, 'modulus')
52
+
53
+ def rhs_mod(self, other):
54
+ def func(a, b): return b % a
55
+ return self.apply_function(other, func, 'modulus')
56
+
57
+ def pow(self, other):
58
+ def func(a, b): return a ** b
59
+ return self.apply_function(other, func, 'power')
60
+
61
+ def rhs_pow(self, other):
62
+ def func(a, b): return b ** a
63
+ return self.apply_function(other, func, 'power')
64
+
65
+ def __add__(self, other): return self.add(other) # this + that
66
+ def __radd__(self, other): return self.add(other) # that + this
67
+ def __iadd__(self, other): # this += that
68
+ self = self.add(other)
69
+ return self
70
+
71
+ def __sub__(self, other): return self.subtract(other)
72
+ def __rsub__(self, other): return self.rhs_subtract(other) # non-commutative
73
+ def __isub__(self, other):
74
+ self = self.subtract(other)
75
+ return self
76
+
77
+ def __mul__(self, other): return self.multiply(other)
78
+ def __rmul__(self, other): return self.multiply(other)
79
+ def __imul__(self, other):
80
+ self = self.multiply(other)
81
+ return self
82
+
83
+ def __truediv__(self, other): return self.divide(other)
84
+ def __rtruediv__(self, other): return self.rhs_divide(other) # non-commutative
85
+ def __itruediv__(self, other):
86
+ self = self.divide(other)
87
+ return self
88
+
89
+ def __floordiv__(self, other): return self.floor_divide(other)
90
+ def __rfloordiv__(self, other): return self.rhs_floor_divide(other) # non-commutative
91
+ def __ifloordiv__(self, other):
92
+ self = self.floor_divide(other)
93
+ return self
94
+
95
+ def __mod__(self, other): return self.mod(other)
96
+ def __rmod__(self, other): return self.rhs_mod(other)
97
+ def __imod__(self, other):
98
+ self = self.mod(other)
99
+ return self
100
+
101
+ def __pow__(self, other): return self.pow(other)
102
+ def __rpow__(self, other): return self.rhs_pow(other)
103
+ def __ipow__(self, other):
104
+ self = self.pow(other)
105
+ return self
106
+
107
+ # comparison functions
108
+ def __eq__(self, other):
109
+ if isinstance(other, Custom): return self.data == other.data
110
+ return self.data == other
111
+
112
+ def __ne__(self, other):
113
+ if isinstance(other, Custom): return self.data != other.data
114
+ return self.data != other
115
+
116
+ def __lt__(self, other):
117
+ if isinstance(other, Custom): return self.data < other.data
118
+ return self.data < other
119
+
120
+ def __gt__(self, other):
121
+ if isinstance(other, Custom): return self.data > other.data
122
+ return self.data > other
123
+
124
+ def __le__(self, other):
125
+ if isinstance(other, Custom): return self.data <= other.data
126
+ return self.data <= other
127
+
128
+ def __ge__(self, other):
129
+ if isinstance(other, Custom): return self.data >= other.data
130
+ return self.data >= other
131
+
132
+ # unary operators
133
+ def __pos__(self):
134
+ return self
@@ -7,6 +7,52 @@ def collide_aabb_aabb(top_right1: glm.vec3, bottom_left1: glm.vec3, top_right2:
7
7
  """
8
8
  return all(bottom_left1[i] <= top_right2[i] + epsilon and epsilon + top_right1[i] >= bottom_left2[i] for i in range(3))
9
9
 
10
+ def collide_aabb_line(top_right: glm.vec3, bottom_left: glm.vec3, position: glm.vec3, forward: glm.vec3) -> bool: # TODO check algorithm
11
+ """
12
+ Determines if an infinite line intersects with an AABB
13
+ """
14
+ tmin, tmax = -1e10, 1e10
15
+ for i in range(3):
16
+ if forward[i]: # if forward[i] is not 0 to avoid division errors
17
+
18
+ deno = 1 / forward[i]
19
+ tlow = (bottom_left[i] - position[i]) * deno
20
+ thigh = (top_right[i] - position[i]) * deno
21
+ if deno < 0: tlow, thigh = thigh, tlow
22
+ tmin = max(tmin, tlow)
23
+ tmax = min(tmax, thigh)
24
+ if tmax <= tmin: return False
25
+
26
+ elif position[i] + 1e-7 < bottom_left[i] or position[i] > top_right[i] + 1e-7: return False
27
+
28
+ return True
29
+
30
+ def moller_trumbore(point:glm.vec3, vec:glm.vec3, triangle:list[glm.vec3], epsilon:float=1e-7) -> glm.vec3:
31
+ """
32
+ Determines where a line intersects with a triangle and where that intersection occurred
33
+ """
34
+ edge1, edge2 = triangle[1] - triangle[0], triangle[2] - triangle[0]
35
+ ray_cross = glm.cross(vec, edge2)
36
+ det = glm.dot(edge1, ray_cross)
37
+
38
+ # if the ray is parallel to the triangle
39
+ if abs(det) < epsilon: return None
40
+
41
+ inv_det = 1 / det
42
+ s = point - triangle[0]
43
+ u = glm.dot(s, ray_cross) * inv_det
44
+
45
+ if (u < 0 and abs(u) > epsilon) or (u > 1 and abs(u - 1) > epsilon): return None
46
+
47
+ s_cross = glm.cross(s, edge1)
48
+ v = glm.dot(vec, s_cross) * inv_det
49
+
50
+ if (v < 0 and abs(v) > epsilon) or (u + v > 1 and abs(u + v - 1) > epsilon): return None
51
+
52
+ t = glm.dot(edge2, s_cross) * inv_det
53
+ if t > epsilon: return point + vec * t
54
+ return None
55
+
10
56
  def get_sat_axes(rotation1: glm.quat, rotation2: glm.quat) -> list[glm.vec3]:
11
57
  """
12
58
  Gets the axes for SAT from obb rotation matrices
basilisk/generic/quat.py CHANGED
@@ -1,82 +1,86 @@
1
1
  import glm
2
2
  import numpy as np
3
+ from .abstract_custom import Custom
3
4
 
4
5
 
5
- class Quat():
6
+ class Quat(Custom):
6
7
  def __init__(self, *args, callback=None):
7
8
  self.callback = callback
8
9
  self.prev_data = glm.quat(1, 0, 0, 0)
9
- self.set_data(*args)
10
-
11
- def normalize(self):
12
- """
13
- Inplace normalizes the vector
14
- """
15
- self.data = glm.normalize(self.data)
16
10
 
17
- def set_data(self, *args):
18
- """
19
- Sets the internal vector inplace
20
- """
21
- # overload constructor TODO nvernest this, definitely possible
22
11
  if len(args) == 1:
12
+
23
13
  if isinstance(args[0], Quat):
24
14
  self.data = glm.quat(args[0].data)
15
+ self.prev_data = glm.quat(args[0].prev_data)
25
16
  self.callback = args[0].callback
26
- elif isinstance(args[0], glm.quat): self.data = glm.quat(args[0])
17
+
18
+ elif isinstance(args[0], glm.quat):
19
+ self.data = args[0]
20
+
27
21
  elif isinstance(args[0], tuple) or isinstance(args[0], list) or isinstance(args[0], np.ndarray):
28
- if len(args[0]) != 4 and len(args[0]) != 3: raise ValueError(f'Quat: Expected 3 or 4 values from incoming vector, got {len(args[0])}')
22
+ assert 2 < len(args[0]) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(args[0])}'
29
23
  self.data = glm.quat(args[0])
30
- else: raise ValueError(f'Quat: Unexpected incoming vector type {args[0]}')
31
- elif len(args) == 4: self.data = glm.quat(*args)
32
- else: raise ValueError(f'Quat: Expected either 1 vector or 4 numbers, got {len(args)} values')
24
+
25
+ else:
26
+ try: self.data = glm.quat(args[0])
27
+ except: raise ValueError(f'Quat: Unexpected incoming quaternion type {args[0]}')
28
+
29
+ elif 2 < len(args) < 5: self.data = glm.quat(*args)
30
+ else: raise ValueError(f'Quat: Expected either a vector, quaternion, or 3 or 4 numbers, got {len(args)} values')
31
+
32
+ def apply_function(self, other, func, func_name):
33
+ quat = glm.quat(self.data)
34
+
35
+ if isinstance(other, (glm.vec3, glm.quat)):
36
+ quat = func(quat, other)
37
+
38
+ elif isinstance(other, (tuple, list, np.ndarray)):
39
+ assert 2 < len(other) < 5, f'Quat: Expected 3 or 4 values from incoming vector, got {len(other)}'
40
+ quat = func(quat, other)
41
+
42
+ elif isinstance(other, Custom):
43
+ quat = func(quat, other.data)
44
+
45
+ else:
46
+ try: quat = func(quat, other)
47
+ except: raise ValueError(f'Quat: Not an accepted type for {func_name}, got {type(other)}')
48
+ return Quat(quat)
33
49
 
34
- # override _= operators
35
- def __iadd__(self, other):
36
- if isinstance(other, glm.quat): self.data += other
37
- elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
38
- if len(other) != 4 and len(other) != 3: raise ValueError(f'Quat: Number of added values must be 3 or 4, got {len(other)}')
39
- self.data += other
40
- elif isinstance(other, Quat): self.data += other.data
41
- else: raise ValueError(f'Quat: Not an accepted type for addition, got {type(other)}')
42
- return self
43
-
44
- def __isub__(self, other):
45
- if isinstance(other, glm.quat): self.data -= other
46
- elif isinstance(other, tuple) or isinstance(other, list) or isinstance(other, np.ndarray):
47
- if len(other) != 4 and len(other) != 3: raise ValueError(f'Quat: Number of added values must be 3 or 4, got {len(other)}')
48
- self.data -= other
49
- elif isinstance(other, Quat): self.data -= other.data
50
- else: raise ValueError(f'Quat: Not an accepted type for addition, got {type(other)}')
51
- return self
52
-
53
- def __imul__(self, other):
54
- # TODO add checks for number types
55
- self.data *= other
56
- return self
50
+ # unary operators
51
+ def __neg__(self):
52
+ return Quat(-self.data, callback=self.callback)
57
53
 
58
- def __idiv__(self, other):
59
- # TODO add checks for number types
60
- self.data /= other
61
- return self
54
+ def __abs__(self):
55
+ return Quat(abs(self.data), callback=self.callback)
62
56
 
63
- def __ifloordiv__(self, other):
64
- # TODO add checks for number types
65
- self.data //= other
66
- return self
67
-
68
- # override [_] accessor
57
+ # accessor functions
69
58
  def __getitem__(self, index):
70
- if int(index) != index: raise IndexError(f'Quat: index must be an int, got {type(index)}') # check if index is a float
71
- if index < 0 or index > 3: raise IndexError(f'Quat: index out of bounds, got {index}')
59
+ assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
60
+ assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
72
61
  return self.data[index]
73
62
 
74
63
  def __setitem__(self, index, value):
75
- if int(index) != index: raise IndexError(f'Quat: index must be an int, got {type(index)}') # check if index is a float
76
- if index < 0 or index > 3: raise IndexError(f'Quat: index out of bounds, got {index}')
64
+ assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
65
+ assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
77
66
  try: self.data[index] = value
78
67
  except: raise ValueError(f'Quat: Invalid element type, got {type(value)}')
79
68
 
69
+ def __delitem__(self, index): # cannot delete an index from a quaternion, so we set the value to zero instead
70
+ assert int(index) == index, f'Quat: index must be an int, got {type(index)}' # check if index is a float
71
+ assert 0 <= index <= 3, f'Quat: index out of bounds, got {index}'
72
+ self.data[index] = 0
73
+
74
+ def __len__(self):
75
+ return 4
76
+
77
+ def __iter__(self):
78
+ return iter(self.data)
79
+
80
+ def __contains__(self, item):
81
+ return item in self.data
82
+
83
+ # override str operators
80
84
  def __repr__(self):
81
85
  return str(self.data)
82
86
 
@@ -95,33 +99,40 @@ class Quat():
95
99
  def z(self): return self.data.z
96
100
 
97
101
  @data.setter
98
- def data(self, value: glm.vec3):
99
- self._data = value
100
-
102
+ def data(self, value: glm.vec3 | glm.quat):
103
+ self._data = glm.quat(value)
101
104
  cur = self._data
102
105
  prev = self.prev_data
103
106
  thresh = 1e-6
104
107
 
105
108
  if self.callback and (abs(cur.w - prev.w) > thresh or abs(cur.x - prev.x) > thresh or abs(cur.y - prev.y) > thresh or abs(cur.z - prev.z) > thresh):
106
- self.prev_data = self._data
109
+ self.prev_data = glm.quat(self._data)
107
110
  self.callback()
108
111
 
109
112
  @w.setter
110
113
  def w(self, value):
111
114
  self.data.w = value
112
- if self.callback: self.callback()
115
+ if self.callback and abs(value - self.prev_data.w) > 1e-6:
116
+ self.prev_data.w = value
117
+ self.callback()
113
118
 
114
119
  @x.setter
115
120
  def x(self, value):
116
- self.data.x = value
117
- if self.callback: self.callback()
121
+ self._data.x = value
122
+ if self.callback and abs(value - self.prev_data.x) > 1e-6:
123
+ self.prev_data.x = value
124
+ self.callback()
118
125
 
119
126
  @y.setter
120
127
  def y(self, value):
121
- self.data.y = value
122
- if self.callback: self.callback()
128
+ self._data.y = value
129
+ if self.callback and abs(value - self.prev_data.y) > 1e-6:
130
+ self.prev_data.y = value
131
+ self.callback()
123
132
 
124
133
  @z.setter
125
134
  def z(self, value):
126
- self.data.z = value
127
- if self.callback: self.callback()
135
+ self._data.z = value
136
+ if self.callback and abs(value - self.prev_data.z) > 1e-6:
137
+ self.prev_data.z = value
138
+ self.callback()