1"""
2Example showing how to create particle explosions via the GPU.
3"""
4import random
5import time
6import math
7from array import array
8from dataclasses import dataclass
9import arcade
10import arcade.gl
11
12SCREEN_WIDTH = 1024
13SCREEN_HEIGHT = 768
14SCREEN_TITLE = "GPU Particle Explosion"
15
16PARTICLE_COUNT = 300
17
18@dataclass
19class Burst:
20 """ Track for each burst. """
21 buffer: arcade.gl.Buffer
22 vao: arcade.gl.Geometry
23 start_time: float
24
25class MyWindow(arcade.Window):
26 """ Main window"""
27 def __init__(self):
28 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
29 self.burst_list = []
30
31 # Program to visualize the points
32 self.program = self.ctx.load_program(
33 vertex_shader="vertex_shader_v2.glsl",
34 fragment_shader="fragment_shader.glsl",
35 )
36
37 self.ctx.enable_only()
38
39 def on_draw(self):
40 """ Draw everything """
41 self.clear()
42
43 # Set the particle size
44 self.ctx.point_size = 2 * self.get_pixel_ratio()
45
46 # Loop through each burst
47 for burst in self.burst_list:
48
49 # Set the uniform data
50 self.program['time'] = time.time() - burst.start_time
51
52 # Render the burst
53 burst.vao.render(self.program, mode=self.ctx.POINTS)
54
55 def on_update(self, dt):
56 """ Update everything """
57 pass
58
59 def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):
60 """ User clicks mouse """
61
62 def _gen_initial_data(initial_x, initial_y):
63 """ Generate data for each particle """
64 for i in range(PARTICLE_COUNT):
65 angle = random.uniform(0, 2 * math.pi)
66 speed = abs(random.gauss(0, 1)) * .5
67 dx = math.sin(angle) * speed
68 dy = math.cos(angle) * speed
69 yield initial_x
70 yield initial_y
71 yield dx
72 yield dy
73
74 # Recalculate the coordinates from pixels to the OpenGL system with
75 # 0, 0 at the center.
76 x2 = x / self.width * 2. - 1.
77 y2 = y / self.height * 2. - 1.
78
79 # Get initial particle data
80 initial_data = _gen_initial_data(x2, y2)
81
82 # Create a buffer with that data
83 buffer = self.ctx.buffer(data=array('f', initial_data))
84
85 # Create a buffer description that says how the buffer data is formatted.
86 buffer_description = arcade.gl.BufferDescription(buffer,
87 '2f 2f',
88 ['in_pos', 'in_vel'])
89 # Create our Vertex Attribute Object
90 vao = self.ctx.geometry([buffer_description])
91
92 # Create the Burst object and add it to the list of bursts
93 burst = Burst(buffer=buffer, vao=vao, start_time=time.time())
94 self.burst_list.append(burst)
95
96
97if __name__ == "__main__":
98 window = MyWindow()
99 window.center_window()
100 arcade.run()