basilisk-engine 0.1.10__py3-none-any.whl → 0.1.11__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 (87) hide show
  1. basilisk/__init__.py +14 -14
  2. basilisk/audio/sound.py +27 -27
  3. basilisk/bsk_assets/cube.obj +48 -48
  4. basilisk/collisions/broad/broad_aabb.py +102 -102
  5. basilisk/collisions/broad/broad_bvh.py +137 -137
  6. basilisk/collisions/collider.py +95 -95
  7. basilisk/collisions/collider_handler.py +224 -224
  8. basilisk/collisions/narrow/contact_manifold.py +95 -95
  9. basilisk/collisions/narrow/dataclasses.py +34 -34
  10. basilisk/collisions/narrow/deprecated.py +46 -46
  11. basilisk/collisions/narrow/epa.py +91 -91
  12. basilisk/collisions/narrow/gjk.py +66 -66
  13. basilisk/collisions/narrow/graham_scan.py +24 -24
  14. basilisk/collisions/narrow/helper.py +29 -29
  15. basilisk/collisions/narrow/line_intersections.py +106 -106
  16. basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
  17. basilisk/config.py +2 -2
  18. basilisk/draw/draw.py +100 -100
  19. basilisk/draw/draw_handler.py +179 -179
  20. basilisk/draw/font_renderer.py +28 -28
  21. basilisk/engine.py +206 -206
  22. basilisk/generic/abstract_bvh.py +15 -15
  23. basilisk/generic/abstract_custom.py +133 -133
  24. basilisk/generic/collisions.py +72 -72
  25. basilisk/generic/input_validation.py +66 -66
  26. basilisk/generic/math.py +6 -6
  27. basilisk/generic/matrices.py +35 -35
  28. basilisk/generic/meshes.py +72 -72
  29. basilisk/generic/quat.py +142 -142
  30. basilisk/generic/quat_methods.py +7 -7
  31. basilisk/generic/raycast_result.py +23 -23
  32. basilisk/generic/vec3.py +143 -143
  33. basilisk/input/mouse.py +61 -61
  34. basilisk/input/path.py +14 -14
  35. basilisk/mesh/cube.py +33 -33
  36. basilisk/mesh/mesh.py +230 -230
  37. basilisk/mesh/mesh_from_data.py +130 -130
  38. basilisk/mesh/model.py +271 -271
  39. basilisk/mesh/narrow_aabb.py +89 -89
  40. basilisk/mesh/narrow_bvh.py +91 -91
  41. basilisk/mesh/narrow_primative.py +23 -23
  42. basilisk/nodes/helper.py +28 -28
  43. basilisk/nodes/node.py +682 -682
  44. basilisk/nodes/node_handler.py +95 -95
  45. basilisk/particles/particle_handler.py +63 -63
  46. basilisk/particles/particle_renderer.py +87 -87
  47. basilisk/physics/impulse.py +112 -112
  48. basilisk/physics/physics_body.py +43 -43
  49. basilisk/physics/physics_engine.py +35 -35
  50. basilisk/render/batch.py +105 -86
  51. basilisk/render/camera.py +206 -206
  52. basilisk/render/chunk.py +106 -99
  53. basilisk/render/chunk_handler.py +166 -155
  54. basilisk/render/frame.py +101 -101
  55. basilisk/render/framebuffer.py +130 -130
  56. basilisk/render/image.py +87 -87
  57. basilisk/render/image_handler.py +122 -122
  58. basilisk/render/light.py +96 -96
  59. basilisk/render/light_handler.py +58 -58
  60. basilisk/render/material.py +219 -219
  61. basilisk/render/material_handler.py +135 -135
  62. basilisk/render/post_process.py +132 -132
  63. basilisk/render/shader.py +110 -109
  64. basilisk/render/shader_handler.py +79 -79
  65. basilisk/render/sky.py +120 -120
  66. basilisk/scene.py +264 -264
  67. basilisk/shaders/batch.frag +276 -276
  68. basilisk/shaders/batch.vert +115 -115
  69. basilisk/shaders/crt.frag +31 -31
  70. basilisk/shaders/draw.frag +21 -21
  71. basilisk/shaders/draw.vert +21 -21
  72. basilisk/shaders/filter.frag +22 -22
  73. basilisk/shaders/frame.frag +12 -12
  74. basilisk/shaders/frame.vert +13 -13
  75. basilisk/shaders/geometry.frag +8 -8
  76. basilisk/shaders/geometry.vert +41 -41
  77. basilisk/shaders/normal.frag +59 -59
  78. basilisk/shaders/normal.vert +96 -96
  79. basilisk/shaders/particle.frag +71 -71
  80. basilisk/shaders/particle.vert +84 -84
  81. basilisk/shaders/sky.frag +9 -9
  82. basilisk/shaders/sky.vert +13 -13
  83. {basilisk_engine-0.1.10.dist-info → basilisk_engine-0.1.11.dist-info}/METADATA +38 -45
  84. basilisk_engine-0.1.11.dist-info/RECORD +103 -0
  85. {basilisk_engine-0.1.10.dist-info → basilisk_engine-0.1.11.dist-info}/WHEEL +1 -1
  86. basilisk_engine-0.1.10.dist-info/RECORD +0 -103
  87. {basilisk_engine-0.1.10.dist-info → basilisk_engine-0.1.11.dist-info}/top_level.txt +0 -0
@@ -1,96 +1,96 @@
1
- import glm
2
- from .node import Node
3
- from .helper import node_is
4
- from ..render.chunk_handler import ChunkHandler
5
- from ..mesh.mesh import Mesh
6
- from ..render.material import Material
7
-
8
-
9
- class NodeHandler():
10
- scene: ...
11
- """Back reference to the scene"""
12
- nodes: list[Node]
13
- """The list of root nodes in the scene"""
14
-
15
- def __init__(self, scene):
16
- """
17
- Contains all the nodes in the scene.
18
- Handles chunking and batching of nodes
19
- """
20
-
21
- self.scene = scene
22
- self.nodes = []
23
- self.chunk_handler = ChunkHandler(scene)
24
-
25
- def update(self):
26
- """
27
- Updates the nodes and chunks in the scene
28
- """
29
- dt = self.scene.engine.delta_time
30
- if dt < 0.5:
31
- for node in self.nodes:
32
- if not node.static: node.update(dt)
33
- self.chunk_handler.update()
34
-
35
- def render(self):
36
- """
37
- Updates the node meshes in the scene
38
- """
39
-
40
- self.chunk_handler.render()
41
-
42
- def add(self, node: Node) -> Node:
43
- """
44
- Adds a new node to the node handler
45
- """
46
- if node in self.nodes: return
47
-
48
- for n in node.get_all(): # gets all nodes including the node to be added
49
-
50
- # Update scene Handlers
51
- self.scene.shader_handler.add(n.shader)
52
- if not n.material: n.material = self.scene.material_handler.base
53
- self.scene.material_handler.add(n.material)
54
-
55
- # Update the node attributes
56
- n.init_scene(self.scene)
57
-
58
- # Add the node to internal data
59
- self.nodes.append(n)
60
- self.chunk_handler.add(n)
61
-
62
- return node
63
-
64
- 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:
65
- """
66
- Returns the first node with the given traits
67
- """
68
- for node in self.nodes:
69
- if node_is(node, position, scale, rotation, forward, mesh, material, velocity, rotational_velocity, physics, mass, collisions, static_friction, kinetic_friction, elasticity, collision_group, name, tags, static): return node
70
- return None
71
-
72
- 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]:
73
- """
74
- Returns all nodes with the given traits
75
- """
76
- nodes = []
77
- for node in self.nodes:
78
- if node_is(node, position, scale, rotation, forward, mesh, material, velocity, rotational_velocity, physics, mass, collisions, static_friction, kinetic_friction, elasticity, collision_group, name, tags, static): nodes.append(node)
79
- return nodes
80
-
81
- def remove(self, node: Node) -> None:
82
- """
83
- Removes a node and all of its children from their handlers
84
- """
85
-
86
- if node == None: return
87
-
88
- # TODO add support for recursive nodes
89
- if node in self.nodes:
90
- if node.physics_body: self.scene.physics_engine.remove(node.physics_body)
91
- if node.collider: self.scene.collider_handler.remove(node.collider)
92
- self.chunk_handler.remove(node)
93
- self.nodes.remove(node)
94
- node.node_handler = None
95
-
1
+ import glm
2
+ from .node import Node
3
+ from .helper import node_is
4
+ from ..render.chunk_handler import ChunkHandler
5
+ from ..mesh.mesh import Mesh
6
+ from ..render.material import Material
7
+
8
+
9
+ class NodeHandler():
10
+ scene: ...
11
+ """Back reference to the scene"""
12
+ nodes: list[Node]
13
+ """The list of root nodes in the scene"""
14
+
15
+ def __init__(self, scene):
16
+ """
17
+ Contains all the nodes in the scene.
18
+ Handles chunking and batching of nodes
19
+ """
20
+
21
+ self.scene = scene
22
+ self.nodes = []
23
+ self.chunk_handler = ChunkHandler(scene)
24
+
25
+ def update(self):
26
+ """
27
+ Updates the nodes and chunks in the scene
28
+ """
29
+ dt = self.scene.engine.delta_time
30
+ if dt < 0.5:
31
+ for node in self.nodes:
32
+ if not node.static: node.update(dt)
33
+ self.chunk_handler.update()
34
+
35
+ def render(self):
36
+ """
37
+ Updates the node meshes in the scene
38
+ """
39
+
40
+ self.chunk_handler.render()
41
+
42
+ def add(self, node: Node) -> Node:
43
+ """
44
+ Adds a new node to the node handler
45
+ """
46
+ if node in self.nodes: return
47
+
48
+ for n in node.get_all(): # gets all nodes including the node to be added
49
+
50
+ # Update scene Handlers
51
+ self.scene.shader_handler.add(n.shader)
52
+ if not n.material: n.material = self.scene.material_handler.base
53
+ self.scene.material_handler.add(n.material)
54
+
55
+ # Update the node attributes
56
+ n.init_scene(self.scene)
57
+
58
+ # Add the node to internal data
59
+ self.nodes.append(n)
60
+ self.chunk_handler.add(n)
61
+
62
+ return node
63
+
64
+ 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:
65
+ """
66
+ Returns the first node with the given traits
67
+ """
68
+ for node in self.nodes:
69
+ if node_is(node, position, scale, rotation, forward, mesh, material, velocity, rotational_velocity, physics, mass, collisions, static_friction, kinetic_friction, elasticity, collision_group, name, tags, static): return node
70
+ return None
71
+
72
+ 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]:
73
+ """
74
+ Returns all nodes with the given traits
75
+ """
76
+ nodes = []
77
+ for node in self.nodes:
78
+ if node_is(node, position, scale, rotation, forward, mesh, material, velocity, rotational_velocity, physics, mass, collisions, static_friction, kinetic_friction, elasticity, collision_group, name, tags, static): nodes.append(node)
79
+ return nodes
80
+
81
+ def remove(self, node: Node) -> None:
82
+ """
83
+ Removes a node and all of its children from their handlers
84
+ """
85
+
86
+ if node == None: return
87
+
88
+ # TODO add support for recursive nodes
89
+ if node in self.nodes:
90
+ if node.physics_body: self.scene.physics_engine.remove(node.physics_body)
91
+ if node.collider: self.scene.collider_handler.remove(node.collider)
92
+ self.chunk_handler.remove(node)
93
+ self.nodes.remove(node)
94
+ node.node_handler = None
95
+
96
96
  for child in node.children: self.remove(child)
@@ -1,64 +1,64 @@
1
- from .particle_renderer import ParticleRenderer
2
- from ..mesh.mesh import Mesh
3
- from ..render.material import Material
4
- from ..generic.input_validation import validate_tuple3, validate_float
5
-
6
- class ParticleHandler:
7
- def __init__(self, scene):
8
- """
9
- A handler for all particles in a scene
10
- """
11
-
12
- self.scene = scene
13
- self.cube = Mesh(scene.engine.root + '/bsk_assets/cube.obj')
14
- self.particle_renderers = {self.cube : ParticleRenderer(scene, self.cube)}
15
-
16
-
17
- def add(self, mesh: Mesh=None, life: float=1.0, position: tuple|float=0, material: Material=None, scale: float=1.0, velocity: tuple|float=0, acceleration: tuple|float=0) -> bool:
18
- """
19
- Add a new particle to the scene
20
- Args:
21
- mesh: Mesh
22
- The basilisk mesh of the particle
23
- life: float
24
- The duration of the particle in seconds
25
- position: tuple (x, y, z)
26
- The initial position of the particle
27
- color: tuple (r, g, b) (components out of 255)
28
- The color of the particle
29
- scale: float
30
- The overall scale factor of the particle
31
- velocity: tuple (x, y, z)
32
- The inital velocity of the particle as a vector
33
- acceleration: tuple (x, y, z)
34
- The permanent acceleration of the particle as a vector
35
- """
36
-
37
- # Get the mesh and make a new particle renderer if the mesh is new
38
- if mesh == None: mesh = self.cube
39
- elif not isinstance(mesh, Mesh): raise ValueError(f'particle_handler.add: invlaid mesh type for particle: {type(mesh)}')
40
- if mesh not in self.particle_renderers: self.particle_renderers[mesh] = ParticleRenderer(self.scene, mesh)
41
-
42
- # Get material ID
43
- if material == None: material_index = 0
44
- elif isinstance(material, Material):
45
- self.scene.material_handler.add(material)
46
- material_index = material.index
47
- else: raise ValueError(f'particle_handler.add: Invalid particle material type: {type(material)}')
48
-
49
- # Validate the 3-component vectors
50
- position = validate_tuple3('particle', 'add', position)
51
- velocity = validate_tuple3('particle', 'add', velocity)
52
- acceleration = validate_tuple3('particle', 'add', acceleration)
53
-
54
- # Validate float inputs
55
- life = validate_float('particle', 'add', life)
56
- scale = validate_float('particle', 'add', scale)
57
-
58
- # Add the particle to the renderer
59
- self.particle_renderers[mesh].add(life, position, material_index, scale, velocity, acceleration)
60
-
61
- def render(self) -> None:
62
- for renderer in self.particle_renderers.values(): renderer.render()
63
- def update(self) -> None:
1
+ from .particle_renderer import ParticleRenderer
2
+ from ..mesh.mesh import Mesh
3
+ from ..render.material import Material
4
+ from ..generic.input_validation import validate_tuple3, validate_float
5
+
6
+ class ParticleHandler:
7
+ def __init__(self, scene):
8
+ """
9
+ A handler for all particles in a scene
10
+ """
11
+
12
+ self.scene = scene
13
+ self.cube = Mesh(scene.engine.root + '/bsk_assets/cube.obj')
14
+ self.particle_renderers = {self.cube : ParticleRenderer(scene, self.cube)}
15
+
16
+
17
+ def add(self, mesh: Mesh=None, life: float=1.0, position: tuple|float=0, material: Material=None, scale: float=1.0, velocity: tuple|float=0, acceleration: tuple|float=0) -> bool:
18
+ """
19
+ Add a new particle to the scene
20
+ Args:
21
+ mesh: Mesh
22
+ The basilisk mesh of the particle
23
+ life: float
24
+ The duration of the particle in seconds
25
+ position: tuple (x, y, z)
26
+ The initial position of the particle
27
+ color: tuple (r, g, b) (components out of 255)
28
+ The color of the particle
29
+ scale: float
30
+ The overall scale factor of the particle
31
+ velocity: tuple (x, y, z)
32
+ The inital velocity of the particle as a vector
33
+ acceleration: tuple (x, y, z)
34
+ The permanent acceleration of the particle as a vector
35
+ """
36
+
37
+ # Get the mesh and make a new particle renderer if the mesh is new
38
+ if mesh == None: mesh = self.cube
39
+ elif not isinstance(mesh, Mesh): raise ValueError(f'particle_handler.add: invlaid mesh type for particle: {type(mesh)}')
40
+ if mesh not in self.particle_renderers: self.particle_renderers[mesh] = ParticleRenderer(self.scene, mesh)
41
+
42
+ # Get material ID
43
+ if material == None: material_index = 0
44
+ elif isinstance(material, Material):
45
+ self.scene.material_handler.add(material)
46
+ material_index = material.index
47
+ else: raise ValueError(f'particle_handler.add: Invalid particle material type: {type(material)}')
48
+
49
+ # Validate the 3-component vectors
50
+ position = validate_tuple3('particle', 'add', position)
51
+ velocity = validate_tuple3('particle', 'add', velocity)
52
+ acceleration = validate_tuple3('particle', 'add', acceleration)
53
+
54
+ # Validate float inputs
55
+ life = validate_float('particle', 'add', life)
56
+ scale = validate_float('particle', 'add', scale)
57
+
58
+ # Add the particle to the renderer
59
+ self.particle_renderers[mesh].add(life, position, material_index, scale, velocity, acceleration)
60
+
61
+ def render(self) -> None:
62
+ for renderer in self.particle_renderers.values(): renderer.render()
63
+ def update(self) -> None:
64
64
  for renderer in self.particle_renderers.values(): renderer.update()
@@ -1,87 +1,87 @@
1
- import numpy as np
2
- from ..render.shader import Shader
3
- from ..mesh.mesh import Mesh
4
- from ..render.material import Material
5
- from numba import njit
6
-
7
-
8
- @njit
9
- def update_particle_matrix(particle_instances, dt):
10
- particle_instances[:,6:9] += particle_instances[:,9:12] * dt
11
- particle_instances[:,:3] += particle_instances[:,6:9] * dt
12
- particle_instances[:,5] -= dt/3
13
- return particle_instances
14
-
15
- @njit
16
- def get_alive(particles):
17
- return particles[particles[:, 5] >= 0]
18
-
19
- update_particle_matrix(np.zeros(shape=(2, 12), dtype='f4'), 1)
20
- get_alive(np.zeros(shape=(2, 12), dtype='f4'))
21
-
22
-
23
- class ParticleRenderer:
24
- def __init__(self, scene: ..., mesh: Mesh) -> None:
25
- """
26
- Handels and renders the particles of a single mesh type
27
- """
28
-
29
- self.scene = scene
30
- self.ctx = scene.ctx
31
- root = scene.engine.root
32
- self.shader = Shader(scene.engine, vert=root + '/shaders/particle.vert', frag=root + '/shaders/particle.frag')
33
- scene.shader_handler.add(self.shader)
34
-
35
- self.particle_cube_size = 25
36
-
37
- self.particle_instances = np.zeros(shape=(1, 12), dtype='f4')
38
- self.instance_buffer = self.ctx.buffer(reserve=(12 * 3) * (self.particle_cube_size ** 3))
39
-
40
- self.vao = self.ctx.vertex_array( self.shader.program,
41
- [(self.ctx.buffer(mesh.data), '3f 2f 3f 3f 3f', *['in_position', 'in_uv', 'in_normal', 'in_tangent', 'in_bitangent']),
42
- (self.instance_buffer, '3f 1f 1f 1f /i', 'in_instance_pos', 'in_instance_mtl', 'scale', 'life')],
43
- skip_errors=True)
44
-
45
- def render(self) -> None:
46
- """
47
- Renders the alive particles in the scene
48
- """
49
-
50
- # Get the current particles
51
- alive_particles = get_alive(self.particle_instances)
52
- n = len(alive_particles)
53
-
54
- # Write and render
55
- self.instance_buffer.write(np.array(alive_particles[:,:6], order='C'))
56
- self.vao.render(instances=n)
57
-
58
- def update(self) -> None:
59
- """
60
- Updates the particle positions based on their given properties
61
- """
62
-
63
- self.particle_instances = get_alive(self.particle_instances)
64
- self.particle_instances = update_particle_matrix(self.particle_instances, self.scene.engine.delta_time)
65
-
66
- def add(self, life=1.0, position=(0, 0, 0), material: int=0, scale=1.0, velocity=(0, 3, 0), acceleration=(0, -10, 0)) -> bool:
67
- """
68
- Add a new particle to the scene
69
- Args:
70
- life: float
71
- The duration of the particle in seconds
72
- position: tuple (x, y, z)
73
- The initial position of the particle
74
- color: tuple (r, g, b) (components out of 255)
75
- The color of the particle
76
- scale: float
77
- The overall scale factor of the particle
78
- velocity: tuple (x, y, z)
79
- The inital velocity of the particle as a vector
80
- acceleration: tuple (x, y, z)
81
- The permanent acceleration of the particle as a vector
82
- """
83
- # Check if there is already the max number of particles
84
- if len(self.particle_instances) >= (self.particle_cube_size ** 3): return False
85
- # Create and add the particle to the scene
86
- new_particle = np.array([*position, material, scale, life, *velocity, *acceleration])
87
- self.particle_instances = np.vstack([new_particle, self.particle_instances], dtype='f4')
1
+ import numpy as np
2
+ from ..render.shader import Shader
3
+ from ..mesh.mesh import Mesh
4
+ from ..render.material import Material
5
+ from numba import njit
6
+
7
+
8
+ @njit
9
+ def update_particle_matrix(particle_instances, dt):
10
+ particle_instances[:,6:9] += particle_instances[:,9:12] * dt
11
+ particle_instances[:,:3] += particle_instances[:,6:9] * dt
12
+ particle_instances[:,5] -= dt/3
13
+ return particle_instances
14
+
15
+ @njit
16
+ def get_alive(particles):
17
+ return particles[particles[:, 5] >= 0]
18
+
19
+ update_particle_matrix(np.zeros(shape=(2, 12), dtype='f4'), 1)
20
+ get_alive(np.zeros(shape=(2, 12), dtype='f4'))
21
+
22
+
23
+ class ParticleRenderer:
24
+ def __init__(self, scene: ..., mesh: Mesh) -> None:
25
+ """
26
+ Handels and renders the particles of a single mesh type
27
+ """
28
+
29
+ self.scene = scene
30
+ self.ctx = scene.ctx
31
+ root = scene.engine.root
32
+ self.shader = Shader(scene.engine, vert=root + '/shaders/particle.vert', frag=root + '/shaders/particle.frag')
33
+ scene.shader_handler.add(self.shader)
34
+
35
+ self.particle_cube_size = 25
36
+
37
+ self.particle_instances = np.zeros(shape=(1, 12), dtype='f4')
38
+ self.instance_buffer = self.ctx.buffer(reserve=(12 * 3) * (self.particle_cube_size ** 3))
39
+
40
+ self.vao = self.ctx.vertex_array( self.shader.program,
41
+ [(self.ctx.buffer(mesh.data), '3f 2f 3f 3f 3f', *['in_position', 'in_uv', 'in_normal', 'in_tangent', 'in_bitangent']),
42
+ (self.instance_buffer, '3f 1f 1f 1f /i', 'in_instance_pos', 'in_instance_mtl', 'scale', 'life')],
43
+ skip_errors=True)
44
+
45
+ def render(self) -> None:
46
+ """
47
+ Renders the alive particles in the scene
48
+ """
49
+
50
+ # Get the current particles
51
+ alive_particles = get_alive(self.particle_instances)
52
+ n = len(alive_particles)
53
+
54
+ # Write and render
55
+ self.instance_buffer.write(np.array(alive_particles[:,:6], order='C'))
56
+ self.vao.render(instances=n)
57
+
58
+ def update(self) -> None:
59
+ """
60
+ Updates the particle positions based on their given properties
61
+ """
62
+
63
+ self.particle_instances = get_alive(self.particle_instances)
64
+ self.particle_instances = update_particle_matrix(self.particle_instances, self.scene.engine.delta_time)
65
+
66
+ def add(self, life=1.0, position=(0, 0, 0), material: int=0, scale=1.0, velocity=(0, 3, 0), acceleration=(0, -10, 0)) -> bool:
67
+ """
68
+ Add a new particle to the scene
69
+ Args:
70
+ life: float
71
+ The duration of the particle in seconds
72
+ position: tuple (x, y, z)
73
+ The initial position of the particle
74
+ color: tuple (r, g, b) (components out of 255)
75
+ The color of the particle
76
+ scale: float
77
+ The overall scale factor of the particle
78
+ velocity: tuple (x, y, z)
79
+ The inital velocity of the particle as a vector
80
+ acceleration: tuple (x, y, z)
81
+ The permanent acceleration of the particle as a vector
82
+ """
83
+ # Check if there is already the max number of particles
84
+ if len(self.particle_instances) >= (self.particle_cube_size ** 3): return False
85
+ # Create and add the particle to the scene
86
+ new_particle = np.array([*position, material, scale, life, *velocity, *acceleration])
87
+ self.particle_instances = np.vstack([new_particle, self.particle_instances], dtype='f4')