basilisk-engine 0.1.2__py3-none-any.whl → 0.1.3__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 (82) hide show
  1. basilisk/__init__.py +11 -11
  2. basilisk/bsk_assets/cube.obj +48 -48
  3. basilisk/collisions/broad/broad_aabb.py +102 -102
  4. basilisk/collisions/broad/broad_bvh.py +137 -137
  5. basilisk/collisions/collider.py +95 -83
  6. basilisk/collisions/collider_handler.py +225 -228
  7. basilisk/collisions/narrow/contact_manifold.py +90 -90
  8. basilisk/collisions/narrow/dataclasses.py +33 -27
  9. basilisk/collisions/narrow/deprecated.py +46 -46
  10. basilisk/collisions/narrow/epa.py +91 -91
  11. basilisk/collisions/narrow/gjk.py +66 -66
  12. basilisk/collisions/narrow/graham_scan.py +24 -24
  13. basilisk/collisions/narrow/helper.py +29 -29
  14. basilisk/collisions/narrow/line_intersections.py +106 -106
  15. basilisk/collisions/narrow/sutherland_hodgman.py +75 -75
  16. basilisk/config.py +2 -2
  17. basilisk/draw/draw.py +100 -100
  18. basilisk/draw/draw_handler.py +180 -210
  19. basilisk/draw/font_renderer.py +28 -28
  20. basilisk/engine.py +195 -195
  21. basilisk/generic/abstract_bvh.py +15 -15
  22. basilisk/generic/abstract_custom.py +133 -133
  23. basilisk/generic/collisions.py +70 -70
  24. basilisk/generic/input_validation.py +67 -28
  25. basilisk/generic/math.py +6 -6
  26. basilisk/generic/matrices.py +33 -33
  27. basilisk/generic/meshes.py +72 -72
  28. basilisk/generic/quat.py +137 -137
  29. basilisk/generic/quat_methods.py +7 -7
  30. basilisk/generic/raycast_result.py +24 -0
  31. basilisk/generic/vec3.py +143 -143
  32. basilisk/input/mouse.py +61 -59
  33. basilisk/mesh/cube.py +33 -33
  34. basilisk/mesh/mesh.py +230 -230
  35. basilisk/mesh/mesh_from_data.py +132 -132
  36. basilisk/mesh/model.py +271 -271
  37. basilisk/mesh/narrow_aabb.py +89 -89
  38. basilisk/mesh/narrow_bvh.py +91 -91
  39. basilisk/mesh/narrow_primative.py +23 -23
  40. basilisk/nodes/helper.py +29 -0
  41. basilisk/nodes/node.py +681 -617
  42. basilisk/nodes/node_handler.py +95 -118
  43. basilisk/particles/particle_handler.py +63 -54
  44. basilisk/particles/particle_renderer.py +87 -87
  45. basilisk/physics/impulse.py +112 -112
  46. basilisk/physics/physics_body.py +43 -43
  47. basilisk/physics/physics_engine.py +35 -35
  48. basilisk/render/batch.py +86 -86
  49. basilisk/render/camera.py +204 -199
  50. basilisk/render/chunk.py +99 -99
  51. basilisk/render/chunk_handler.py +154 -154
  52. basilisk/render/frame.py +181 -181
  53. basilisk/render/image.py +75 -75
  54. basilisk/render/image_handler.py +122 -122
  55. basilisk/render/light.py +96 -96
  56. basilisk/render/light_handler.py +58 -58
  57. basilisk/render/material.py +219 -219
  58. basilisk/render/material_handler.py +135 -135
  59. basilisk/render/shader.py +109 -109
  60. basilisk/render/shader_handler.py +79 -79
  61. basilisk/render/sky.py +120 -120
  62. basilisk/scene.py +250 -210
  63. basilisk/shaders/batch.frag +276 -276
  64. basilisk/shaders/batch.vert +115 -115
  65. basilisk/shaders/draw.frag +21 -21
  66. basilisk/shaders/draw.vert +21 -21
  67. basilisk/shaders/filter.frag +22 -22
  68. basilisk/shaders/frame.frag +12 -12
  69. basilisk/shaders/frame.vert +13 -13
  70. basilisk/shaders/geometry.frag +8 -8
  71. basilisk/shaders/geometry.vert +41 -41
  72. basilisk/shaders/normal.frag +59 -59
  73. basilisk/shaders/normal.vert +96 -96
  74. basilisk/shaders/particle.frag +71 -71
  75. basilisk/shaders/particle.vert +84 -84
  76. basilisk/shaders/sky.frag +9 -9
  77. basilisk/shaders/sky.vert +13 -13
  78. {basilisk_engine-0.1.2.dist-info → basilisk_engine-0.1.3.dist-info}/METADATA +45 -38
  79. basilisk_engine-0.1.3.dist-info/RECORD +97 -0
  80. {basilisk_engine-0.1.2.dist-info → basilisk_engine-0.1.3.dist-info}/WHEEL +1 -1
  81. basilisk_engine-0.1.2.dist-info/RECORD +0 -95
  82. {basilisk_engine-0.1.2.dist-info → basilisk_engine-0.1.3.dist-info}/top_level.txt +0 -0
@@ -1,119 +1,96 @@
1
- import glm
2
- from .node import Node
3
- from ..render.chunk_handler import ChunkHandler
4
- from ..mesh.mesh import Mesh
5
- from ..render.material import Material
6
-
7
-
8
- class NodeHandler():
9
- scene: ...
10
- """Back reference to the scene"""
11
- nodes: list[Node]
12
- """The list of root nodes in the scene"""
13
-
14
- def __init__(self, scene):
15
- """
16
- Contains all the nodes in the scene.
17
- Handles chunking and batching of nodes
18
- """
19
-
20
- self.scene = scene
21
- self.nodes = []
22
- self.chunk_handler = ChunkHandler(scene)
23
-
24
- def update(self):
25
- """
26
- Updates the nodes and chunks in the scene
27
- """
28
- for node in self.nodes:
29
- if node.static: continue
30
- node.update(self.scene.engine.delta_time)
31
- self.chunk_handler.update()
32
-
33
- def render(self):
34
- """
35
- Updates the node meshes in the scene
36
- """
37
-
38
- self.chunk_handler.render()
39
-
40
- def add(self, node: Node) -> Node:
41
- """
42
- Adds a new node to the node handler
43
- """
44
- if node in self.nodes: return
45
-
46
- for n in node.get_nodes(): # gets all nodes including the node to be added
47
-
48
- # Update scene Handlers
49
- self.scene.shader_handler.add(n.shader)
50
- if not n.material: n.material = self.scene.material_handler.base
51
- self.scene.material_handler.add(n.material)
52
-
53
- # Update the node attributes
54
- n.init_scene(self.scene)
55
-
56
- # Add the node to internal data
57
- self.nodes.append(n)
58
- self.chunk_handler.add(n)
59
-
60
- return node
61
-
62
- def node_is(self, node: Node, 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) -> bool:
63
- """
64
- Determine if a node meets the requirements given by the parameters. If a parameter is None, then the filter is not applied.
65
- """
66
- return all([
67
- position is None or position == node.position,
68
- scale is None or scale == node.scale,
69
- rotation is None or rotation == node.rotation,
70
- forward is None or forward == node.forward,
71
- mesh is None or mesh == node.mesh,
72
- material is None or material == node.material,
73
- velocity is None or velocity == node.velocity,
74
- rotational_velocity is None or rotational_velocity == node.rotational_velocity,
75
- physics is None or bool(node.physics_body) == physics,
76
- mass is None or (node.physics_body and mass == node.physics_body.mass),
77
- collisions is None or bool(node.collider) == collisions,
78
- static_friction is None or (node.collider and node.collider.static_friction == static_friction),
79
- kinetic_friction is None or (node.collider and node.collider.kinetic_friction == kinetic_friction),
80
- elasticity is None or (node.collider and node.collider.elasticity == elasticity),
81
- collision_group is None or (node.collider and node.collider.collision_group == collision_group),
82
- name is None or node.name == name,
83
- tags is None or all([tag in node.tags for tag in tags]),
84
- static is None or node.static == static
85
- ])
86
-
87
- 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:
88
- """
89
- Returns the first node with the given traits
90
- """
91
- for node in self.nodes:
92
- if self.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
93
- return None
94
-
95
- 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) -> Node:
96
- """
97
- Returns all nodes with the given traits
98
- """
99
- nodes = []
100
- for node in self.nodes:
101
- if self.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)
102
- return nodes
103
-
104
- def remove(self, node: Node) -> None:
105
- """
106
- Removes a node and all of its children from their handlers
107
- """
108
-
109
- if node == None: return
110
-
111
- # TODO add support for recursive nodes
112
- if node in self.nodes:
113
- if node.physics_body: self.scene.physics_engine.remove(node.physics_body)
114
- if node.collider: self.scene.collider_handler.remove(node.collider)
115
- self.chunk_handler.remove(node)
116
- self.nodes.remove(node)
117
- node.node_handler = None
118
-
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
+
119
96
  for child in node.children: self.remove(child)
@@ -1,55 +1,64 @@
1
- from .particle_renderer import ParticleRenderer
2
- from ..mesh.mesh import Mesh
3
- from ..render.material import Material
4
-
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=1.0, position=(0, 0, 0), material: Material=None, scale=1.0, velocity=(0, 3, 0), acceleration=(0, -10, 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
- # Add the particle to the renderer
50
- self.particle_renderers[mesh].add(life, position, material_index, scale, velocity, acceleration)
51
-
52
- def render(self) -> None:
53
- for renderer in self.particle_renderers.values(): renderer.render()
54
- 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:
55
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')