basilisk-engine 0.1.23__py3-none-any.whl → 0.1.24__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 (93) 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 +3 -3
  18. basilisk/draw/draw.py +100 -100
  19. basilisk/draw/draw_handler.py +175 -175
  20. basilisk/draw/font_renderer.py +28 -28
  21. basilisk/engine.py +169 -164
  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_output/IO_handler.py +91 -92
  34. basilisk/input_output/clock.py +49 -49
  35. basilisk/input_output/keys.py +43 -43
  36. basilisk/input_output/mouse.py +90 -62
  37. basilisk/input_output/path.py +14 -14
  38. basilisk/mesh/cube.py +33 -33
  39. basilisk/mesh/mesh.py +230 -230
  40. basilisk/mesh/mesh_from_data.py +130 -130
  41. basilisk/mesh/model.py +271 -271
  42. basilisk/mesh/narrow_aabb.py +89 -89
  43. basilisk/mesh/narrow_bvh.py +91 -91
  44. basilisk/mesh/narrow_primative.py +23 -23
  45. basilisk/nodes/helper.py +28 -28
  46. basilisk/nodes/node.py +686 -686
  47. basilisk/nodes/node_handler.py +97 -97
  48. basilisk/particles/particle_handler.py +64 -64
  49. basilisk/particles/particle_renderer.py +92 -92
  50. basilisk/physics/impulse.py +112 -112
  51. basilisk/physics/physics_body.py +43 -43
  52. basilisk/physics/physics_engine.py +35 -35
  53. basilisk/render/batch.py +105 -105
  54. basilisk/render/camera.py +258 -267
  55. basilisk/render/chunk.py +106 -106
  56. basilisk/render/chunk_handler.py +165 -165
  57. basilisk/render/frame.py +95 -99
  58. basilisk/render/framebuffer.py +289 -132
  59. basilisk/render/image.py +87 -87
  60. basilisk/render/image_handler.py +120 -120
  61. basilisk/render/light.py +96 -96
  62. basilisk/render/light_handler.py +58 -58
  63. basilisk/render/material.py +219 -219
  64. basilisk/render/material_handler.py +131 -131
  65. basilisk/render/post_process.py +139 -139
  66. basilisk/render/shader.py +109 -109
  67. basilisk/render/shader_handler.py +83 -83
  68. basilisk/render/sky.py +120 -120
  69. basilisk/scene.py +279 -279
  70. basilisk/shaders/batch.frag +276 -276
  71. basilisk/shaders/batch.vert +115 -115
  72. basilisk/shaders/crt.frag +31 -31
  73. basilisk/shaders/draw.frag +22 -22
  74. basilisk/shaders/draw.vert +25 -25
  75. basilisk/shaders/filter.frag +22 -22
  76. basilisk/shaders/frame.frag +12 -12
  77. basilisk/shaders/frame.vert +13 -13
  78. basilisk/shaders/geometry.frag +8 -8
  79. basilisk/shaders/geometry.vert +41 -41
  80. basilisk/shaders/normal.frag +59 -59
  81. basilisk/shaders/normal.vert +96 -96
  82. basilisk/shaders/particle.frag +71 -71
  83. basilisk/shaders/particle.vert +84 -84
  84. basilisk/shaders/sky.frag +9 -9
  85. basilisk/shaders/sky.vert +13 -13
  86. {basilisk_engine-0.1.23.dist-info → basilisk_engine-0.1.24.dist-info}/METADATA +38 -45
  87. basilisk_engine-0.1.24.dist-info/RECORD +106 -0
  88. {basilisk_engine-0.1.23.dist-info → basilisk_engine-0.1.24.dist-info}/WHEEL +1 -1
  89. basilisk/input/__init__.py +0 -0
  90. basilisk/input/mouse.py +0 -62
  91. basilisk/input/path.py +0 -14
  92. basilisk_engine-0.1.23.dist-info/RECORD +0 -109
  93. {basilisk_engine-0.1.23.dist-info → basilisk_engine-0.1.24.dist-info}/top_level.txt +0 -0
@@ -1,98 +1,98 @@
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.engine = scene.engine
23
- self.nodes = []
24
- self.chunk_handler = ChunkHandler(scene)
25
-
26
- def update(self):
27
- """
28
- Updates the nodes and chunks in the scene
29
- """
30
- dt = self.scene.engine.delta_time
31
- if dt < 0.5:
32
- for node in self.nodes:
33
- # if not node.static: TODO determine better solution to this line
34
- node.update(dt)
35
- self.chunk_handler.update()
36
-
37
- def render(self):
38
- """
39
- Updates the node meshes in the scene
40
- """
41
-
42
- self.chunk_handler.render()
43
-
44
- def add(self, node: Node) -> Node:
45
- """
46
- Adds a new node to the node handler
47
- """
48
- if node in self.nodes: return
49
-
50
- for n in node.get_all(): # gets all nodes including the node to be added
51
-
52
- # Update scene Handlers
53
- self.engine.shader_handler.add(n.shader)
54
- if not n.material: n.material = self.engine.material_handler.base
55
- self.engine.material_handler.add(n.material)
56
-
57
- # Update the node attributes
58
- n.init_scene(self.scene)
59
-
60
- # Add the node to internal data
61
- self.nodes.append(n)
62
- self.chunk_handler.add(n)
63
-
64
- return node
65
-
66
- 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:
67
- """
68
- Returns the first node with the given traits
69
- """
70
- for node in self.nodes:
71
- 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
72
- return None
73
-
74
- 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]:
75
- """
76
- Returns all nodes with the given traits
77
- """
78
- nodes = []
79
- for node in self.nodes:
80
- 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)
81
- return nodes
82
-
83
- def remove(self, node: Node) -> None:
84
- """
85
- Removes a node and all of its children from their handlers
86
- """
87
-
88
- if node == None: return
89
-
90
- # TODO add support for recursive nodes
91
- if node in self.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
- self.chunk_handler.remove(node)
95
- self.nodes.remove(node)
96
- node.node_handler = None
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.engine = scene.engine
23
+ self.nodes = []
24
+ self.chunk_handler = ChunkHandler(scene)
25
+
26
+ def update(self):
27
+ """
28
+ Updates the nodes and chunks in the scene
29
+ """
30
+ dt = self.scene.engine.delta_time
31
+ if dt < 0.5:
32
+ for node in self.nodes:
33
+ # if not node.static: TODO determine better solution to this line
34
+ node.update(dt)
35
+ self.chunk_handler.update()
36
+
37
+ def render(self):
38
+ """
39
+ Updates the node meshes in the scene
40
+ """
41
+
42
+ self.chunk_handler.render()
43
+
44
+ def add(self, node: Node) -> Node:
45
+ """
46
+ Adds a new node to the node handler
47
+ """
48
+ if node in self.nodes: return
49
+
50
+ for n in node.get_all(): # gets all nodes including the node to be added
51
+
52
+ # Update scene Handlers
53
+ self.engine.shader_handler.add(n.shader)
54
+ if not n.material: n.material = self.engine.material_handler.base
55
+ self.engine.material_handler.add(n.material)
56
+
57
+ # Update the node attributes
58
+ n.init_scene(self.scene)
59
+
60
+ # Add the node to internal data
61
+ self.nodes.append(n)
62
+ self.chunk_handler.add(n)
63
+
64
+ return node
65
+
66
+ 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:
67
+ """
68
+ Returns the first node with the given traits
69
+ """
70
+ for node in self.nodes:
71
+ 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
72
+ return None
73
+
74
+ 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]:
75
+ """
76
+ Returns all nodes with the given traits
77
+ """
78
+ nodes = []
79
+ for node in self.nodes:
80
+ 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)
81
+ return nodes
82
+
83
+ def remove(self, node: Node) -> None:
84
+ """
85
+ Removes a node and all of its children from their handlers
86
+ """
87
+
88
+ if node == None: return
89
+
90
+ # TODO add support for recursive nodes
91
+ if node in self.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
+ self.chunk_handler.remove(node)
95
+ self.nodes.remove(node)
96
+ node.node_handler = None
97
+
98
98
  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.engine.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.engine.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.engine.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.engine.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()