basilisk-engine 0.1.17__py3-none-any.whl → 0.1.18__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 +15 -15
  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 +26 -26
  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 +684 -684
  44. basilisk/nodes/node_handler.py +96 -95
  45. basilisk/particles/particle_handler.py +64 -64
  46. basilisk/particles/particle_renderer.py +92 -92
  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 -105
  51. basilisk/render/camera.py +211 -211
  52. basilisk/render/chunk.py +106 -106
  53. basilisk/render/chunk_handler.py +165 -165
  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 -110
  64. basilisk/render/shader_handler.py +80 -80
  65. basilisk/render/sky.py +120 -120
  66. basilisk/scene.py +276 -276
  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.17.dist-info → basilisk_engine-0.1.18.dist-info}/METADATA +45 -38
  84. basilisk_engine-0.1.18.dist-info/RECORD +103 -0
  85. {basilisk_engine-0.1.17.dist-info → basilisk_engine-0.1.18.dist-info}/WHEEL +1 -1
  86. basilisk_engine-0.1.17.dist-info/RECORD +0 -103
  87. {basilisk_engine-0.1.17.dist-info → basilisk_engine-0.1.18.dist-info}/top_level.txt +0 -0
@@ -1,96 +1,97 @@
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: TODO determine better solution to this line
33
+ node.update(dt)
34
+ self.chunk_handler.update()
35
+
36
+ def render(self):
37
+ """
38
+ Updates the node meshes in the scene
39
+ """
40
+
41
+ self.chunk_handler.render()
42
+
43
+ def add(self, node: Node) -> Node:
44
+ """
45
+ Adds a new node to the node handler
46
+ """
47
+ if node in self.nodes: return
48
+
49
+ for n in node.get_all(): # gets all nodes including the node to be added
50
+
51
+ # Update scene Handlers
52
+ self.scene.shader_handler.add(n.shader)
53
+ if not n.material: n.material = self.scene.material_handler.base
54
+ self.scene.material_handler.add(n.material)
55
+
56
+ # Update the node attributes
57
+ n.init_scene(self.scene)
58
+
59
+ # Add the node to internal data
60
+ self.nodes.append(n)
61
+ self.chunk_handler.add(n)
62
+
63
+ return node
64
+
65
+ 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:
66
+ """
67
+ Returns the first node with the given traits
68
+ """
69
+ for node in self.nodes:
70
+ 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
71
+ return None
72
+
73
+ 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]:
74
+ """
75
+ Returns all nodes with the given traits
76
+ """
77
+ nodes = []
78
+ for node in self.nodes:
79
+ 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)
80
+ return nodes
81
+
82
+ def remove(self, node: Node) -> None:
83
+ """
84
+ Removes a node and all of its children from their handlers
85
+ """
86
+
87
+ if node == None: return
88
+
89
+ # TODO add support for recursive nodes
90
+ if node in self.nodes:
91
+ if node.physics_body: self.scene.physics_engine.remove(node.physics_body)
92
+ if node.collider: self.scene.collider_handler.remove(node.collider)
93
+ self.chunk_handler.remove(node)
94
+ self.nodes.remove(node)
95
+ node.node_handler = None
96
+
96
97
  for child in node.children: self.remove(child)
@@ -1,65 +1,65 @@
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, shader=None):
8
- """
9
- A handler for all particles in a scene
10
- """
11
-
12
- self.scene = scene
13
- self.shader = shader
14
- self.cube = Mesh(scene.engine.root + '/bsk_assets/cube.obj')
15
- self.particle_renderers = {self.cube : ParticleRenderer(scene, self.cube, self.shader)}
16
-
17
-
18
- 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:
19
- """
20
- Add a new particle to the scene
21
- Args:
22
- mesh: Mesh
23
- The basilisk mesh of the particle
24
- life: float
25
- The duration of the particle in seconds
26
- position: tuple (x, y, z)
27
- The initial position of the particle
28
- color: tuple (r, g, b) (components out of 255)
29
- The color of the particle
30
- scale: float
31
- The overall scale factor of the particle
32
- velocity: tuple (x, y, z)
33
- The inital velocity of the particle as a vector
34
- acceleration: tuple (x, y, z)
35
- The permanent acceleration of the particle as a vector
36
- """
37
-
38
- # Get the mesh and make a new particle renderer if the mesh is new
39
- if mesh == None: mesh = self.cube
40
- elif not isinstance(mesh, Mesh): raise ValueError(f'particle_handler.add: invlaid mesh type for particle: {type(mesh)}')
41
- if mesh not in self.particle_renderers: self.particle_renderers[mesh] = ParticleRenderer(self.scene, mesh, self.shader)
42
-
43
- # Get material ID
44
- if material == None: material_index = 0
45
- elif isinstance(material, Material):
46
- self.scene.material_handler.add(material)
47
- material_index = material.index
48
- else: raise ValueError(f'particle_handler.add: Invalid particle material type: {type(material)}')
49
-
50
- # Validate the 3-component vectors
51
- position = validate_tuple3('particle', 'add', position)
52
- velocity = validate_tuple3('particle', 'add', velocity)
53
- acceleration = validate_tuple3('particle', 'add', acceleration)
54
-
55
- # Validate float inputs
56
- life = validate_float('particle', 'add', life)
57
- scale = validate_float('particle', 'add', scale)
58
-
59
- # Add the particle to the renderer
60
- self.particle_renderers[mesh].add(life, position, material_index, scale, velocity, acceleration)
61
-
62
- def render(self) -> None:
63
- for renderer in self.particle_renderers.values(): renderer.render()
64
- 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, shader=None):
8
+ """
9
+ A handler for all particles in a scene
10
+ """
11
+
12
+ self.scene = scene
13
+ self.shader = shader
14
+ self.cube = Mesh(scene.engine.root + '/bsk_assets/cube.obj')
15
+ self.particle_renderers = {self.cube : ParticleRenderer(scene, self.cube, self.shader)}
16
+
17
+
18
+ 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:
19
+ """
20
+ Add a new particle to the scene
21
+ Args:
22
+ mesh: Mesh
23
+ The basilisk mesh of the particle
24
+ life: float
25
+ The duration of the particle in seconds
26
+ position: tuple (x, y, z)
27
+ The initial position of the particle
28
+ color: tuple (r, g, b) (components out of 255)
29
+ The color of the particle
30
+ scale: float
31
+ The overall scale factor of the particle
32
+ velocity: tuple (x, y, z)
33
+ The inital velocity of the particle as a vector
34
+ acceleration: tuple (x, y, z)
35
+ The permanent acceleration of the particle as a vector
36
+ """
37
+
38
+ # Get the mesh and make a new particle renderer if the mesh is new
39
+ if mesh == None: mesh = self.cube
40
+ elif not isinstance(mesh, Mesh): raise ValueError(f'particle_handler.add: invlaid mesh type for particle: {type(mesh)}')
41
+ if mesh not in self.particle_renderers: self.particle_renderers[mesh] = ParticleRenderer(self.scene, mesh, self.shader)
42
+
43
+ # Get material ID
44
+ if material == None: material_index = 0
45
+ elif isinstance(material, Material):
46
+ self.scene.material_handler.add(material)
47
+ material_index = material.index
48
+ else: raise ValueError(f'particle_handler.add: Invalid particle material type: {type(material)}')
49
+
50
+ # Validate the 3-component vectors
51
+ position = validate_tuple3('particle', 'add', position)
52
+ velocity = validate_tuple3('particle', 'add', velocity)
53
+ acceleration = validate_tuple3('particle', 'add', acceleration)
54
+
55
+ # Validate float inputs
56
+ life = validate_float('particle', 'add', life)
57
+ scale = validate_float('particle', 'add', scale)
58
+
59
+ # Add the particle to the renderer
60
+ self.particle_renderers[mesh].add(life, position, material_index, scale, velocity, acceleration)
61
+
62
+ def render(self) -> None:
63
+ for renderer in self.particle_renderers.values(): renderer.render()
64
+ def update(self) -> None:
65
65
  for renderer in self.particle_renderers.values(): renderer.update()
@@ -1,93 +1,93 @@
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, shader: Shader=None) -> 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
- if shader: self.shader = shader
33
- else: self.shader = Shader(scene.engine, vert=root + '/shaders/particle.vert', frag=root + '/shaders/particle.frag')
34
- scene.shader_handler.add(self.shader)
35
-
36
- self.particle_cube_size = 25
37
-
38
- self.particle_instances = np.zeros(shape=(1, 12), dtype='f4')
39
- self.instance_buffer = self.ctx.buffer(reserve=(12 * 3) * (self.particle_cube_size ** 3))
40
-
41
- self.vao = self.ctx.vertex_array( self.shader.program,
42
- [(self.ctx.buffer(mesh.data), '3f 2f 3f 3f 3f', *['in_position', 'in_uv', 'in_normal', 'in_tangent', 'in_bitangent']),
43
- (self.instance_buffer, '3f 1f 1f 1f /i', 'in_instance_pos', 'in_instance_mtl', 'scale', 'life')],
44
- skip_errors=True)
45
-
46
- def render(self) -> None:
47
- """
48
- Renders the alive particles in the scene
49
- """
50
-
51
- # Get the current particles
52
- alive_particles = get_alive(self.particle_instances)
53
- n = len(alive_particles)
54
-
55
- # Write and render
56
- self.instance_buffer.write(np.array(alive_particles[:,:6], order='C'))
57
- self.vao.render(instances=n)
58
-
59
- def update(self) -> None:
60
- """
61
- Updates the particle positions based on their given properties
62
- """
63
-
64
- self.particle_instances = get_alive(self.particle_instances)
65
- self.particle_instances = update_particle_matrix(self.particle_instances, self.scene.engine.delta_time)
66
-
67
- 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:
68
- """
69
- Add a new particle to the scene
70
- Args:
71
- life: float
72
- The duration of the particle in seconds
73
- position: tuple (x, y, z)
74
- The initial position of the particle
75
- color: tuple (r, g, b) (components out of 255)
76
- The color of the particle
77
- scale: float
78
- The overall scale factor of the particle
79
- velocity: tuple (x, y, z)
80
- The inital velocity of the particle as a vector
81
- acceleration: tuple (x, y, z)
82
- The permanent acceleration of the particle as a vector
83
- """
84
- # Check if there is already the max number of particles
85
- if len(self.particle_instances) >= (self.particle_cube_size ** 3): return False
86
- # Create and add the particle to the scene
87
- new_particle = np.array([*position, material, scale, life, *velocity, *acceleration])
88
- self.particle_instances = np.vstack([new_particle, self.particle_instances], dtype='f4')
89
-
90
-
91
- def __del__(self):
92
- self.instance_buffer.release()
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, shader: Shader=None) -> 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
+ if shader: self.shader = shader
33
+ else: self.shader = Shader(scene.engine, vert=root + '/shaders/particle.vert', frag=root + '/shaders/particle.frag')
34
+ scene.shader_handler.add(self.shader)
35
+
36
+ self.particle_cube_size = 25
37
+
38
+ self.particle_instances = np.zeros(shape=(1, 12), dtype='f4')
39
+ self.instance_buffer = self.ctx.buffer(reserve=(12 * 3) * (self.particle_cube_size ** 3))
40
+
41
+ self.vao = self.ctx.vertex_array( self.shader.program,
42
+ [(self.ctx.buffer(mesh.data), '3f 2f 3f 3f 3f', *['in_position', 'in_uv', 'in_normal', 'in_tangent', 'in_bitangent']),
43
+ (self.instance_buffer, '3f 1f 1f 1f /i', 'in_instance_pos', 'in_instance_mtl', 'scale', 'life')],
44
+ skip_errors=True)
45
+
46
+ def render(self) -> None:
47
+ """
48
+ Renders the alive particles in the scene
49
+ """
50
+
51
+ # Get the current particles
52
+ alive_particles = get_alive(self.particle_instances)
53
+ n = len(alive_particles)
54
+
55
+ # Write and render
56
+ self.instance_buffer.write(np.array(alive_particles[:,:6], order='C'))
57
+ self.vao.render(instances=n)
58
+
59
+ def update(self) -> None:
60
+ """
61
+ Updates the particle positions based on their given properties
62
+ """
63
+
64
+ self.particle_instances = get_alive(self.particle_instances)
65
+ self.particle_instances = update_particle_matrix(self.particle_instances, self.scene.engine.delta_time)
66
+
67
+ 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:
68
+ """
69
+ Add a new particle to the scene
70
+ Args:
71
+ life: float
72
+ The duration of the particle in seconds
73
+ position: tuple (x, y, z)
74
+ The initial position of the particle
75
+ color: tuple (r, g, b) (components out of 255)
76
+ The color of the particle
77
+ scale: float
78
+ The overall scale factor of the particle
79
+ velocity: tuple (x, y, z)
80
+ The inital velocity of the particle as a vector
81
+ acceleration: tuple (x, y, z)
82
+ The permanent acceleration of the particle as a vector
83
+ """
84
+ # Check if there is already the max number of particles
85
+ if len(self.particle_instances) >= (self.particle_cube_size ** 3): return False
86
+ # Create and add the particle to the scene
87
+ new_particle = np.array([*position, material, scale, life, *velocity, *acceleration])
88
+ self.particle_instances = np.vstack([new_particle, self.particle_instances], dtype='f4')
89
+
90
+
91
+ def __del__(self):
92
+ self.instance_buffer.release()
93
93
  self.vao.release()