gpu_particle_burst_07.py Full Listing#

gpu_particle_burst_07.py#
  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
  9
 10import arcade
 11import arcade.gl
 12
 13SCREEN_WIDTH = 1024
 14SCREEN_HEIGHT = 768
 15SCREEN_TITLE = "GPU Particle Explosion"
 16
 17PARTICLE_COUNT = 300
 18
 19MIN_FADE_TIME = 0.25
 20MAX_FADE_TIME = 1.5
 21
 22
 23@dataclass
 24class Burst:
 25    """ Track for each burst. """
 26    buffer: arcade.gl.Buffer
 27    vao: arcade.gl.Geometry
 28    start_time: float
 29
 30
 31class MyWindow(arcade.Window):
 32    """ Main window"""
 33    def __init__(self):
 34        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
 35        self.burst_list = []
 36
 37        # Program to visualize the points
 38        self.program = self.ctx.load_program(
 39            vertex_shader="vertex_shader_v4.glsl",
 40            fragment_shader="fragment_shader.glsl",
 41        )
 42
 43        self.ctx.enable_only(self.ctx.BLEND)
 44
 45    def on_draw(self):
 46        """ Draw everything """
 47        self.clear()
 48
 49        # Set the particle size
 50        self.ctx.point_size = 2 * self.get_pixel_ratio()
 51
 52        # Loop through each burst
 53        for burst in self.burst_list:
 54
 55            # Set the uniform data
 56            self.program['time'] = time.time() - burst.start_time
 57
 58            # Render the burst
 59            burst.vao.render(self.program, mode=self.ctx.POINTS)
 60
 61    def on_update(self, dt):
 62        """ Update game """
 63
 64        # Create a copy of our list, as we can't modify a list while iterating
 65        # it. Then see if any of the items have completely faded out and need
 66        # to be removed.
 67        temp_list = self.burst_list.copy()
 68        for burst in temp_list:
 69            if time.time() - burst.start_time > MAX_FADE_TIME:
 70                self.burst_list.remove(burst)
 71
 72    def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):
 73        """ User clicks mouse """
 74
 75        def _gen_initial_data(initial_x, initial_y):
 76            """ Generate data for each particle """
 77            for i in range(PARTICLE_COUNT):
 78                angle = random.uniform(0, 2 * math.pi)
 79                speed = abs(random.gauss(0, 1)) * .5
 80                dx = math.sin(angle) * speed
 81                dy = math.cos(angle) * speed
 82                red = random.uniform(0.5, 1.0)
 83                green = random.uniform(0, red)
 84                blue = 0
 85                fade_rate = random.uniform(
 86                    1 / MAX_FADE_TIME, 1 / MIN_FADE_TIME)
 87
 88                yield initial_x
 89                yield initial_y
 90                yield dx
 91                yield dy
 92                yield red
 93                yield green
 94                yield blue
 95                yield fade_rate
 96
 97        # Recalculate the coordinates from pixels to the OpenGL system with
 98        # 0, 0 at the center.
 99        x2 = x / self.width * 2. - 1.
100        y2 = y / self.height * 2. - 1.
101
102        # Get initial particle data
103        initial_data = _gen_initial_data(x2, y2)
104
105        # Create a buffer with that data
106        buffer = self.ctx.buffer(data=array('f', initial_data))
107
108        # Create a buffer description specifying the buffer's data format
109        buffer_description = arcade.gl.BufferDescription(
110            buffer,
111            '2f 2f 3f f',
112            ['in_pos', 'in_vel', 'in_color', 'in_fade_rate'])
113
114        # Create our Vertex Attribute Object
115        vao = self.ctx.geometry([buffer_description])
116
117        # Create the Burst object and add it to the list of bursts
118        burst = Burst(buffer=buffer, vao=vao, start_time=time.time())
119        self.burst_list.append(burst)
120
121
122if __name__ == "__main__":
123    window = MyWindow()
124    window.center_window()
125    arcade.run()