basilisk-engine 0.1.51__py3-none-any.whl → 0.1.53__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 (94) hide show
  1. basilisk/__init__.py +27 -27
  2. basilisk/audio/sound.py +40 -40
  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 +225 -225
  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 +53 -53
  18. basilisk/draw/draw.py +100 -100
  19. basilisk/draw/draw_handler.py +181 -181
  20. basilisk/draw/font_renderer.py +28 -28
  21. basilisk/engine.py +168 -168
  22. basilisk/generic/abstract_bvh.py +15 -15
  23. basilisk/generic/abstract_custom.py +133 -133
  24. basilisk/generic/collisions.py +70 -70
  25. basilisk/generic/input_validation.py +82 -82
  26. basilisk/generic/math.py +17 -17
  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 -91
  34. basilisk/input_output/clock.py +49 -49
  35. basilisk/input_output/keys.py +43 -43
  36. basilisk/input_output/mouse.py +90 -90
  37. basilisk/input_output/path.py +14 -14
  38. basilisk/mesh/cube.py +33 -33
  39. basilisk/mesh/mesh.py +233 -233
  40. basilisk/mesh/mesh_from_data.py +150 -150
  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 +709 -709
  47. basilisk/nodes/node_handler.py +107 -98
  48. basilisk/particles/particle_handler.py +69 -65
  49. basilisk/particles/particle_renderer.py +92 -93
  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 +103 -103
  54. basilisk/render/bloom.py +117 -117
  55. basilisk/render/camera.py +260 -260
  56. basilisk/render/chunk.py +113 -113
  57. basilisk/render/chunk_handler.py +167 -167
  58. basilisk/render/frame.py +130 -130
  59. basilisk/render/framebuffer.py +192 -192
  60. basilisk/render/image.py +128 -128
  61. basilisk/render/image_handler.py +120 -120
  62. basilisk/render/light.py +96 -96
  63. basilisk/render/light_handler.py +58 -58
  64. basilisk/render/material.py +232 -232
  65. basilisk/render/material_handler.py +133 -133
  66. basilisk/render/post_process.py +180 -180
  67. basilisk/render/shader.py +135 -135
  68. basilisk/render/shader_handler.py +109 -109
  69. basilisk/render/sky.py +119 -119
  70. basilisk/scene.py +295 -291
  71. basilisk/shaders/batch.frag +291 -291
  72. basilisk/shaders/batch.vert +117 -117
  73. basilisk/shaders/bloom_downsample.frag +23 -23
  74. basilisk/shaders/bloom_upsample.frag +33 -33
  75. basilisk/shaders/crt.frag +34 -34
  76. basilisk/shaders/draw.frag +27 -27
  77. basilisk/shaders/draw.vert +25 -25
  78. basilisk/shaders/filter.frag +22 -22
  79. basilisk/shaders/frame.frag +13 -13
  80. basilisk/shaders/frame.vert +13 -13
  81. basilisk/shaders/frame_hdr.frag +27 -27
  82. basilisk/shaders/geometry.frag +10 -10
  83. basilisk/shaders/geometry.vert +41 -41
  84. basilisk/shaders/normal.frag +62 -62
  85. basilisk/shaders/normal.vert +96 -96
  86. basilisk/shaders/particle.frag +81 -81
  87. basilisk/shaders/particle.vert +86 -86
  88. basilisk/shaders/sky.frag +23 -23
  89. basilisk/shaders/sky.vert +13 -13
  90. {basilisk_engine-0.1.51.dist-info → basilisk_engine-0.1.53.dist-info}/METADATA +82 -89
  91. basilisk_engine-0.1.53.dist-info/RECORD +110 -0
  92. {basilisk_engine-0.1.51.dist-info → basilisk_engine-0.1.53.dist-info}/WHEEL +1 -1
  93. basilisk_engine-0.1.51.dist-info/RECORD +0 -110
  94. {basilisk_engine-0.1.51.dist-info → basilisk_engine-0.1.53.dist-info}/top_level.txt +0 -0
@@ -1,98 +1,107 @@
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
- for child in node.children: self.remove(child)
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
+ for child in node.children: self.remove(child)
99
+
100
+ def clear(self) -> None:
101
+ """
102
+ Removes all nodes from the scene
103
+ TODO: this is prolly not the optimal solution lmao
104
+ """
105
+
106
+ while self.nodes:
107
+ self.remove(self.nodes[0])
@@ -1,65 +1,69 @@
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
- for renderer in self.particle_renderers.values(): renderer.update()
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
+
65
+ def update(self) -> None:
66
+ for renderer in self.particle_renderers.values(): renderer.update()
67
+
68
+ def clear(self) -> None:
69
+ self.particle_renderers.clear()
@@ -1,94 +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
-
35
- scene.engine.shader_handler.add(self.shader)
36
-
37
- self.particle_cube_size = 25
38
-
39
- self.particle_instances = np.zeros(shape=(1, 12), dtype='f4')
40
- self.instance_buffer = self.ctx.buffer(reserve=(12 * 3) * (self.particle_cube_size ** 3))
41
-
42
- self.vao = self.ctx.vertex_array( self.shader.program,
43
- [(self.ctx.buffer(mesh.data), '3f 2f 3f 3f 3f', *['in_position', 'in_uv', 'in_normal', 'in_tangent', 'in_bitangent']),
44
- (self.instance_buffer, '3f 1f 1f 1f /i', 'in_instance_pos', 'in_instance_mtl', 'scale', 'life')],
45
- skip_errors=True)
46
-
47
- def render(self) -> None:
48
- """
49
- Renders the alive particles in the scene
50
- """
51
-
52
- # Get the current particles
53
- alive_particles = get_alive(self.particle_instances)
54
- n = len(alive_particles)
55
-
56
- # Write and render
57
- self.instance_buffer.write(np.array(alive_particles[:,:6], order='C'))
58
- self.vao.render(instances=n)
59
-
60
- def update(self) -> None:
61
- """
62
- Updates the particle positions based on their given properties
63
- """
64
-
65
- self.particle_instances = get_alive(self.particle_instances)
66
- self.particle_instances = update_particle_matrix(self.particle_instances, self.scene.engine.delta_time)
67
-
68
- 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:
69
- """
70
- Add a new particle to the scene
71
- Args:
72
- life: float
73
- The duration of the particle in seconds
74
- position: tuple (x, y, z)
75
- The initial position of the particle
76
- color: tuple (r, g, b) (components out of 255)
77
- The color of the particle
78
- scale: float
79
- The overall scale factor of the particle
80
- velocity: tuple (x, y, z)
81
- The inital velocity of the particle as a vector
82
- acceleration: tuple (x, y, z)
83
- The permanent acceleration of the particle as a vector
84
- """
85
- # Check if there is already the max number of particles
86
- if len(self.particle_instances) >= (self.particle_cube_size ** 3): return False
87
- # Create and add the particle to the scene
88
- new_particle = np.array([*position, material, scale, life, *velocity, *acceleration])
89
- self.particle_instances = np.vstack([new_particle, self.particle_instances], dtype='f4')
90
-
91
-
92
- def __del__(self):
93
- 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
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
+
35
+ scene.engine.shader_handler.add(self.shader)
36
+
37
+ self.particle_cube_size = 25
38
+
39
+ self.particle_instances = np.zeros(shape=(1, 12), dtype='f4')
40
+ self.instance_buffer = self.ctx.buffer(reserve=(12 * 3) * (self.particle_cube_size ** 3))
41
+
42
+ self.vao = self.ctx.vertex_array( self.shader.program,
43
+ [(self.ctx.buffer(mesh.data), '3f 2f 3f 3f 3f', *['in_position', 'in_uv', 'in_normal', 'in_tangent', 'in_bitangent']),
44
+ (self.instance_buffer, '3f 1f 1f 1f /i', 'in_instance_pos', 'in_instance_mtl', 'scale', 'life')],
45
+ skip_errors=True)
46
+
47
+ def render(self) -> None:
48
+ """
49
+ Renders the alive particles in the scene
50
+ """
51
+
52
+ # Get the current particles
53
+ alive_particles = get_alive(self.particle_instances)
54
+ n = len(alive_particles)
55
+
56
+ # Write and render
57
+ self.instance_buffer.write(np.array(alive_particles[:,:6], order='C'))
58
+ self.vao.render(instances=n)
59
+
60
+ def update(self) -> None:
61
+ """
62
+ Updates the particle positions based on their given properties
63
+ """
64
+
65
+ self.particle_instances = get_alive(self.particle_instances)
66
+ self.particle_instances = update_particle_matrix(self.particle_instances, self.scene.engine.delta_time)
67
+
68
+ 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:
69
+ """
70
+ Add a new particle to the scene
71
+ Args:
72
+ life: float
73
+ The duration of the particle in seconds
74
+ position: tuple (x, y, z)
75
+ The initial position of the particle
76
+ color: tuple (r, g, b) (components out of 255)
77
+ The color of the particle
78
+ scale: float
79
+ The overall scale factor of the particle
80
+ velocity: tuple (x, y, z)
81
+ The inital velocity of the particle as a vector
82
+ acceleration: tuple (x, y, z)
83
+ The permanent acceleration of the particle as a vector
84
+ """
85
+ # Check if there is already the max number of particles
86
+ if len(self.particle_instances) >= (self.particle_cube_size ** 3): return False
87
+ # Create and add the particle to the scene
88
+ new_particle = np.array([*position, material, scale, life, *velocity, *acceleration])
89
+ self.particle_instances = np.vstack([new_particle, self.particle_instances], dtype='f4')
90
+
91
+ def __del__(self):
92
+ self.instance_buffer.release()
94
93
  self.vao.release()