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_v3.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 red = random.uniform(0.5, 1.0)
70 green = random.uniform(0, red)
71 blue = 0
72 yield initial_x
73 yield initial_y
74 yield dx
75 yield dy
76 yield red
77 yield green
78 yield blue
79
80 # Recalculate the coordinates from pixels to the OpenGL system with
81 # 0, 0 at the center.
82 x2 = x / self.width * 2. - 1.
83 y2 = y / self.height * 2. - 1.
84
85 # Get initial particle data
86 initial_data = _gen_initial_data(x2, y2)
87
88 # Create a buffer with that data
89 buffer = self.ctx.buffer(data=array('f', initial_data))
90
91 # Create a buffer description that says how the buffer data is formatted.
92 buffer_description = arcade.gl.BufferDescription(buffer,
93 '2f 2f 3f',
94 ['in_pos', 'in_vel', 'in_color'])
95 # Create our Vertex Attribute Object
96 vao = self.ctx.geometry([buffer_description])
97
98 # Create the Burst object and add it to the list of bursts
99 burst = Burst(buffer=buffer, vao=vao, start_time=time.time())
100 self.burst_list.append(burst)
101
102
103if __name__ == "__main__":
104 window = MyWindow()
105 window.center_window()
106 arcade.run()