def on_mouse_press(self, x: float, y: float, button: int, modifiers: int): burst = Burst() x2 = x / self.width * 2. - 1. y2 = y / self.height * 2. - 1. burst.buffer = self.ctx.buffer(data=array('f', self.gen_initial_data(PARTICLE_COUNT, x2, y2))) # We also need to be able to visualize both versions (draw to the screen) buffer_description = BufferDescription(burst.buffer, '2f 2f f 3f', ['in_pos', 'in_vel', 'in_fade', 'in_color']) burst.vao = self.ctx.geometry([buffer_description]) burst.time = time.time() self.burst_list.append(burst)
def _create_device_objects(self): self._program = self._ctx.program( vertex_shader=self.VERTEX_SHADER_SRC, fragment_shader=self.FRAGMENT_SHADER_SRC, ) self._program["Texture"] = 0 self._vbo = self._ctx.buffer(reserve=imgui.VERTEX_SIZE * 65536) self._ibo = self._ctx.buffer(reserve=imgui.INDEX_SIZE * 65536) # NOTE: imgui.INDEX_SIZE is type size for the index buffer. We might need to support 8 and 16 bit # but right now we are assuming 32 bit self._vao = self._ctx.geometry( [ BufferDescription(self._vbo, "2f 2f 4f1", ("Position", "UV", "Color"), normalized=("Color", )), ], index_buffer=self._ibo, )
def create_line_generic_with_colors(point_list: PointList, color_list: Iterable[Color], shape_mode: int, line_width: float = 1) -> Shape: """ This function is used by ``create_line_strip`` and ``create_line_loop``, just changing the OpenGL type for the line drawing. :param PointList point_list: :param Iterable[Color] color_list: :param float shape_mode: :param float line_width: :Returns Shape: """ window = get_window() ctx = window.ctx program = ctx.line_generic_with_colors_program buffer_type = np.dtype([('vertex', '2f4'), ('color', '4B')]) data = np.zeros(len(point_list), dtype=buffer_type) data['vertex'] = point_list data['color'] = [get_four_byte_color(color) for color in color_list] vbo = ctx.buffer(data=data.tobytes()) vao_content = [ BufferDescription( vbo, '2f 4f1', ('in_vert', 'in_color'), normalized=['in_color'] ) ] vao = ctx.geometry(vao_content) shape = Shape() shape.vao = vao shape.vbo = vbo shape.program = program shape.mode = shape_mode shape.line_width = line_width return shape
def _refresh_shape(self, group): # Create a buffer large enough to hold all the shapes buffers batch = self.batches[group] total_vbo_bytes = sum(s.vbo.size for s in batch.items) vbo = self.ctx.buffer(reserve=total_vbo_bytes) offset = 0 # Copy all the shapes buffer in our own vbo for shape in batch.items: vbo.copy_from_buffer(shape.vbo, offset=offset) offset += shape.vbo.size # Create an index buffer object. It should count starting from 0. We need to # use a reset_idx to indicate that a new shape will start. reset_idx = 2 ** 32 - 1 indices = [] counter = itertools.count() for shape in batch.items: indices.extend(itertools.islice(counter, shape.vao.num_vertices)) indices.append(reset_idx) del indices[-1] indices = np.array(indices) ibo = self.ctx.buffer(data=indices.astype('i4').tobytes()) vao_content = [ BufferDescription( vbo, '2f 4f1', ('in_vert', 'in_color'), normalized=['in_color'] ) ] vao = self.ctx.geometry(vao_content, ibo) self.program['Position'] = [self.center_x, self.center_y] self.program['Angle'] = self.angle batch.shape.vao = vao batch.shape.vbo = vbo batch.shape.ibo = ibo batch.shape.program = self.program mode, line_width = group batch.shape.mode = mode batch.shape.line_width = line_width
def quad_2d(size: Tuple[float, float] = (1.0, 1.0), pos: Tuple[float, float] = (0.0, 0.0)) -> Geometry: """ Creates 2D quad Geometry using 2 triangle strip with texture coordinates. :param tuple size: width and height :param float pos: Center position x and y :rtype: A :py:class:`~arcade.gl.geometry.Geometry` instance. """ ctx = _get_active_context() width, height = size x_pos, y_pos = pos data = array.array('f', [ x_pos - width / 2.0, y_pos + height / 2.0, 0.0, 1.0, x_pos - width / 2.0, y_pos - height / 2.0, 0.0, 0.0, x_pos + width / 2.0, y_pos + height / 2.0, 1.0, 1.0, x_pos + width / 2.0, y_pos - height / 2.0, 1.0, 0.0, ]) return ctx.geometry([ BufferDescription( ctx.buffer(data=data), '2f 2f', ['in_vert', 'in_uv'], ) ], mode=ctx.TRIANGLE_STRIP)
def screen_rectangle(bottom_left_x: float, bottom_left_y: float, width: float, height: float) -> Geometry: """ Creates screen rectangle using 2 triangle strip with texture coordinates. :param float bottom_left_x: Bottom left x position :param float bottom_left_y: Bottom left y position :param float width: Width of the rectangle :param float height: Height of the rectangle """ ctx = _get_active_context() data = array.array('f', [ bottom_left_x, bottom_left_y + height, 0.0, 1.0, bottom_left_x, bottom_left_y, 0.0, 0.0, bottom_left_x + width, bottom_left_y + height, 1.0, 1.0, bottom_left_x + width, bottom_left_y, 1.0, 0.0, ]) return ctx.geometry([ BufferDescription( ctx.buffer(data=data), '2f 2f', ['in_vert', 'in_uv'], ) ], mode=ctx.TRIANGLE_STRIP)
def __init__(self, window: pyglet.window.Window): """ :param pyglet.window.Window window: The pyglet window """ super().__init__(window) # Set up a default orthogonal projection for sprites and shapes self._projection_2d_buffer = self.buffer(reserve=64) self._projection_2d_buffer.bind_to_uniform_block(0) self._projection_2d_matrix = None self.projection_2d = ( 0, self.screen.width, 0, self.screen.height, ) # --- Pre-load system shaders here --- # FIXME: These pre-created resources needs to be packaged nicely # Just having them globally in the context is probably not a good idea self.line_vertex_shader = self.load_program( vertex_shader=":resources:shaders/shapes/line/line_vertex_shader_vs.glsl", fragment_shader=":resources:shaders/shapes/line/line_vertex_shader_fs.glsl", ) self.line_generic_with_colors_program = self.load_program( vertex_shader=":resources:shaders/shapes/line/line_generic_with_colors_vs.glsl", fragment_shader=":resources:shaders/shapes/line/line_generic_with_colors_fs.glsl", ) self.shape_element_list_program = self.load_program( vertex_shader=":resources:shaders/shape_element_list_vs.glsl", fragment_shader=":resources:shaders/shape_element_list_fs.glsl", ) # self.sprite_list_program = self.load_program( # vertex_shader=':resources:shaders/sprites/sprite_list_instanced_vs.glsl', # fragment_shader=':resources:shaders/sprites/sprite_list_instanced_fs.glsl', # ) self.sprite_list_program_no_cull = self.load_program( vertex_shader=":resources:shaders/sprites/sprite_list_geometry_vs.glsl", geometry_shader=":resources:shaders/sprites/sprite_list_geometry_no_cull_geo.glsl", fragment_shader=":resources:shaders/sprites/sprite_list_geometry_fs.glsl", ) self.sprite_list_program_cull = self.load_program( vertex_shader=":resources:shaders/sprites/sprite_list_geometry_vs.glsl", geometry_shader=":resources:shaders/sprites/sprite_list_geometry_cull_geo.glsl", fragment_shader=":resources:shaders/sprites/sprite_list_geometry_fs.glsl", ) # Shapes self.shape_line_program = self.load_program( vertex_shader=":resources:/shaders/shapes/line/unbuffered_vs.glsl", fragment_shader=":resources:/shaders/shapes/line/unbuffered_fs.glsl", geometry_shader=":resources:/shaders/shapes/line/unbuffered_geo.glsl", ) self.shape_ellipse_filled_unbuffered_program = self.load_program( vertex_shader=":resources:/shaders/shapes/ellipse/filled_unbuffered_vs.glsl", fragment_shader=":resources:/shaders/shapes/ellipse/filled_unbuffered_fs.glsl", geometry_shader=":resources:/shaders/shapes/ellipse/filled_unbuffered_geo.glsl", ) self.shape_ellipse_outline_unbuffered_program = self.load_program( vertex_shader=":resources:/shaders/shapes/ellipse/outline_unbuffered_vs.glsl", fragment_shader=":resources:/shaders/shapes/ellipse/outline_unbuffered_fs.glsl", geometry_shader=":resources:/shaders/shapes/ellipse/outline_unbuffered_geo.glsl", ) self.shape_rectangle_filled_unbuffered_program = self.load_program( vertex_shader=":resources:/shaders/shapes/rectangle/filled_unbuffered_vs.glsl", fragment_shader=":resources:/shaders/shapes/rectangle/filled_unbuffered_fs.glsl", geometry_shader=":resources:/shaders/shapes/rectangle/filled_unbuffered_geo.glsl", ) # --- Pre-created geometry and buffers for unbuffered draw calls ---- # FIXME: These pre-created resources needs to be packaged nicely # Just having them globally in the context is probably not a good idea self.generic_draw_line_strip_color = self.buffer(reserve=4 * 1000) self.generic_draw_line_strip_vbo = self.buffer(reserve=8 * 1000) self.generic_draw_line_strip_geometry = self.geometry( [ BufferDescription(self.generic_draw_line_strip_vbo, "2f", ["in_vert"]), BufferDescription( self.generic_draw_line_strip_color, "4f1", ["in_color"], normalized=["in_color"], ), ] ) # Shape line(s) # Reserve space for 1000 lines (2f pos, 4f color) # TODO: Different version for buffered and unbuffered # TODO: Make round-robin buffers self.shape_line_buffer_pos = self.buffer(reserve=8 * 10) # self.shape_line_buffer_color = self.buffer(reserve=4 * 10) self.shape_line_geometry = self.geometry( [ BufferDescription(self.shape_line_buffer_pos, "2f", ["in_vert"]), # BufferDescription(self.shape_line_buffer_color, '4f1', ['in_color'], normalized=['in_color']) ] ) # ellipse/circle filled self.shape_ellipse_unbuffered_buffer = self.buffer(reserve=8) self.shape_ellipse_unbuffered_geometry = self.geometry( [BufferDescription(self.shape_ellipse_unbuffered_buffer, "2f", ["in_vert"])] ) # ellipse/circle outline self.shape_ellipse_outline_unbuffered_buffer = self.buffer(reserve=8) self.shape_ellipse_outline_unbuffered_geometry = self.geometry( [ BufferDescription( self.shape_ellipse_outline_unbuffered_buffer, "2f", ["in_vert"] ) ] ) # rectangle filled self.shape_rectangle_filled_unbuffered_buffer = self.buffer(reserve=8) self.shape_rectangle_filled_unbuffered_geometry = self.geometry( [ BufferDescription( self.shape_rectangle_filled_unbuffered_buffer, "2f", ["in_vert"] ) ] )
def __init__(self, width, height, title): super().__init__(width, height, title, resizable=True) self.time = 0 # Program to visualize the points self.points_program = self.ctx.program( vertex_shader=""" #version 330 in vec2 in_pos; void main() { // Just pass the position to the geometry shader gl_Position = vec4(in_pos, 0.0, 1.0); } """, geometry_shader=""" #version 330 uniform Projection { mat4 matrix; } proj; // We are receiving points and emitting triangle strip (4 vertices making a quad) layout (points) in; layout (triangle_strip, max_vertices = 4) out; const float P_SIZE = 10.0; out vec2 uv; void main() { // The input point is the center of the quad vec2 center = gl_in[0].gl_Position.xy; // Emit 4 vertices making a triangle strip representing a quad gl_Position = proj.matrix * vec4(center + vec2(-P_SIZE, P_SIZE), 0.0, 1.0); uv = vec2(0, 1); EmitVertex(); gl_Position = proj.matrix * vec4(center + vec2(-P_SIZE, -P_SIZE), 0.0, 1.0); uv = vec2(0, 0); EmitVertex(); gl_Position = proj.matrix * vec4(center + vec2( P_SIZE, P_SIZE), 0.0, 1.0); uv = vec2(1, 1); EmitVertex(); gl_Position = proj.matrix * vec4(center + vec2( P_SIZE, -P_SIZE), 0.0, 1.0); uv = vec2(1, 0); EmitVertex(); EndPrimitive(); } """, fragment_shader=""" #version 330 // Texture coordinates generated in geometry shader in vec2 uv; // The pixel we are writing to in the framebuffer out vec4 fragColor; void main() { float l = length(uv * 2.0 - vec2(1.0)); if (l > 1.0) discard; fragColor = vec4(0.1); } """, ) # A program tranforming points being affected by a gravity point self.gravity_program = self.ctx.program(vertex_shader=""" #version 330 // Delta time (since last frame) uniform float dt; // Strength of gravity uniform float force; // Position of gravity uniform vec2 gravity_pos; // The format of the data in our tranform buffer(s) in vec2 in_pos; in vec2 in_vel; // We are writing to a buffer of the same format out vec2 out_pos; out vec2 out_vel; void main() { // Simplified gravity calculations vec2 dir = normalize(gravity_pos - in_pos) * force; vec2 vel = in_vel + dir / length(dir) * 1.0; // Write to the output buffer out_vel = vel; out_pos = in_pos + vel * dt; } """, ) N = 10_000 # Make two buffers we tranform between so we can work on the previous result self.buffer_1 = self.ctx.buffer( data=array('f', self.gen_initial_data(N))) self.buffer_2 = self.ctx.buffer(reserve=self.buffer_1.size) # We also need to be able to visualize both versions (draw to the screen) self.vao_1 = self.ctx.geometry( [BufferDescription(self.buffer_1, '2f 2x4', ['in_pos'])]) self.vao_2 = self.ctx.geometry( [BufferDescription(self.buffer_2, '2f 2x4', ['in_pos'])]) # We need to be able to tranform both buffers (ping-pong) self.gravity_1 = self.ctx.geometry( [BufferDescription(self.buffer_1, '2f 2f', ['in_pos', 'in_vel'])]) self.gravity_2 = self.ctx.geometry( [BufferDescription(self.buffer_2, '2f 2f', ['in_pos', 'in_vel'])]) # Set up blending states self.ctx.enable_only(self.ctx.BLEND) self.ctx.blend_func = self.ctx.BLEND_ADDITIVE self.mouse_pos = SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2 self.on_resize(*self.get_framebuffer_size()) self.time = time.time()
def __init__(self, width, height, title): """ Set up the application. """ self.time = 0 super().__init__(width, height, title, resizable=True) self.program = self.ctx.program( vertex_shader=""" #version 330 in vec2 in_vert; void main() { gl_Position = vec4(in_vert, 0.0, 1.0); } """, geometry_shader=""" #version 330 layout (points) in; // Emit 4 vertices per point (Makes two triangles with a triangle strip) layout (triangle_strip, max_vertices = 4) out; uniform float time; out vec3 gs_color; void main() { // Get the position emitted from the vertex shader // We get the 0th element because point primitives only have 1 position. vec4 pos = gl_in[0].gl_Position; float offset = 0.02 + sin(time * 5.0 + gl_PrimitiveIDIn) / 200.0; float rot = time + length(pos) * 10.0; mat2 rotate = mat2( cos(rot), -sin(rot), sin(rot), cos(rot) ); vec3 color = vec3( (sin(time + gl_PrimitiveIDIn) + 1.0) / 2.0, (sin(time + 2 + gl_PrimitiveIDIn) + 1.0) / 2.0, (sin(time + 3 + gl_PrimitiveIDIn) + 1.0) / 2.0 ); // Emit 4 vertices around the original vertex position making a quad gl_Position = pos + vec4(rotate * vec2(-offset, offset), 0.0, 0.0); gs_color = color; EmitVertex(); gl_Position = pos + vec4(rotate * vec2(-offset, -offset), 0.0, 0.0); gs_color = color; EmitVertex(); gl_Position = pos + vec4(rotate * vec2(offset, offset), 0.0, 0.0); gs_color = color; EmitVertex(); gl_Position = pos + vec4(rotate * vec2(offset, -offset), 0.0, 0.0); gs_color = color; EmitVertex(); // Done with the primitive EndPrimitive(); } """, fragment_shader=""" #version 330 out vec4 out_color; in vec3 gs_color; void main() { out_color = vec4(gs_color, 1.0); } """, ) num_points = 1000 self.points = self.ctx.geometry([ BufferDescription( self.ctx.buffer(data=array( 'f', [random.uniform(-1.0, 1.0) for _ in range(num_points * 2)])), '2f', ['in_vert'], ) ])
def __init__(self, width, height, title): super().__init__(width, height, title, resizable=True) self.time = 0 # Program to visualize the points self.points_progran = self.ctx.program( vertex_shader=""" #version 330 in vec2 in_pos; out vec3 color; void main() { // Let's just give them a "random" color based on the vertex id color = vec3( mod((gl_VertexID * 100 % 11) / 10.0, 1.0), mod((gl_VertexID * 100 % 27) / 10.0, 1.0), mod((gl_VertexID * 100 % 71) / 10.0, 1.0)); // Pass the point position to primitive assembly gl_Position = vec4(in_pos, 0.0, 1.0); } """, fragment_shader=""" #version 330 // Color passed in from the vertex shader in vec3 color; // The pixel we are writing to in the framebuffer out vec4 fragColor; void main() { // Fill the point fragColor = vec4(color, 1.0); } """, ) # A program tranforming points being affected by a gravity point self.gravity_program = self.ctx.program(vertex_shader=""" #version 330 // Delta time (since last frame) uniform float dt; // Strength of gravity uniform float force; // Position of gravity uniform vec2 gravity_pos; // The format of the data in our tranform buffer(s) in vec2 in_pos; in vec2 in_vel; // We are writing to a buffer of the same format out vec2 out_pos; out vec2 out_vel; void main() { // Simplified gravity calculations vec2 dir = normalize(gravity_pos - in_pos) * force; vec2 vel = in_vel + dir / length(dir) * 0.01; // Write to the output buffer out_vel = vel; out_pos = in_pos + vel * dt; } """, ) N = 50_000 # Make two buffers we tranform between so we can work on the previous result self.buffer_1 = self.ctx.buffer( data=array('f', self.gen_initial_data(N))) self.buffer_2 = self.ctx.buffer(reserve=self.buffer_1.size) # We also need to be able to visualize both versions (draw to the screen) self.vao_1 = self.ctx.geometry( [BufferDescription(self.buffer_1, '2f 2x4', ['in_pos'])]) self.vao_2 = self.ctx.geometry( [BufferDescription(self.buffer_2, '2f 2x4', ['in_pos'])]) # We need to be able to tranform both buffers (ping-pong) self.gravity_1 = self.ctx.geometry( [BufferDescription(self.buffer_1, '2f 2f', ['in_pos', 'in_vel'])]) self.gravity_2 = self.ctx.geometry( [BufferDescription(self.buffer_2, '2f 2f', ['in_pos', 'in_vel'])]) self.ctx.enable_only() # Ensure no context flags are set self.time = time.time()
def __init__(self, width, height, title): super().__init__(width, height, title) self.program = self.ctx.program(vertex_shader=""" #version 330 in vec2 in_position; in vec3 in_color; out vec3 v_color; void main() { gl_Position = vec4(in_position, 0.0, 1.0); v_color = in_color; } """, fragment_shader=""" #version 330 out vec4 out_color; in vec3 v_color; void main() { out_color = vec4(v_color, 1.0); } """) # Vertices self.vertex_buffer = self.ctx.buffer(data=array( 'f', [ # x y r g b -1.0, -1.0, 1.0, 0.0, 0.0, # lower left 1, -1.0, 0.0, 1.0, 0.0, # lower right 1.0, 1.0, 0.0, 0.0, 1.0, # upper right -1.0, 1.0, 0.0, 1.0, 1.0, # upper left 0, 0, 0, 0, 0, # dummy data for testing ])) self.ibo_8 = self.ctx.buffer(data=array('B', [3, 0, 2, 1])) self.ibo_16 = self.ctx.buffer(data=array('H', [3, 0, 2, 1])) self.ibo_32 = self.ctx.buffer(data=array('I', [3, 0, 2, 1])) self.vao_32 = self.ctx.geometry([ BufferDescription(self.vertex_buffer, "2f 3f", ["in_position", "in_color"]), ], index_buffer=self.ibo_32, index_element_size=4, mode=self.ctx.TRIANGLE_STRIP) self.vao_16 = self.ctx.geometry([ BufferDescription(self.vertex_buffer, "2f 3f", ["in_position", "in_color"]), ], index_buffer=self.ibo_16, index_element_size=2, mode=self.ctx.TRIANGLE_STRIP) self.vao_8 = self.ctx.geometry([ BufferDescription(self.vertex_buffer, "2f 3f", ["in_position", "in_color"]), ], index_buffer=self.ibo_16, index_element_size=2, mode=self.ctx.TRIANGLE_STRIP) self.frames = 0
def cube(size=(1.0, 1.0, 1.0), center=(0.0, 0.0, 0.0)) -> Geometry: """Creates a cube with normals and texture coordinates. :param tuple size: size of the cube as a 3-component tuple :param tuple center: center of the cube as a 3-component tuple :rtype: arcade.gl.Geometry :returns: A cube """ ctx = _get_active_context() width, height, depth = size width, height, depth = width / 2.0, height / 2.0, depth / 2.0 pos = array('f', [ center[0] + width, center[1] - height, center[2] + depth, center[0] + width, center[1] + height, center[2] + depth, center[0] - width, center[1] - height, center[2] + depth, center[0] + width, center[1] + height, center[2] + depth, center[0] - width, center[1] + height, center[2] + depth, center[0] - width, center[1] - height, center[2] + depth, center[0] + width, center[1] - height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] + width, center[1] - height, center[2] + depth, center[0] + width, center[1] + height, center[2] - depth, center[0] + width, center[1] + height, center[2] + depth, center[0] + width, center[1] - height, center[2] + depth, center[0] + width, center[1] - height, center[2] - depth, center[0] + width, center[1] - height, center[2] + depth, center[0] - width, center[1] - height, center[2] + depth, center[0] + width, center[1] - height, center[2] - depth, center[0] - width, center[1] - height, center[2] + depth, center[0] - width, center[1] - height, center[2] - depth, center[0] - width, center[1] - height, center[2] + depth, center[0] - width, center[1] + height, center[2] + depth, center[0] - width, center[1] + height, center[2] - depth, center[0] - width, center[1] - height, center[2] + depth, center[0] - width, center[1] + height, center[2] - depth, center[0] - width, center[1] - height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] + width, center[1] - height, center[2] - depth, center[0] - width, center[1] - height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] - width, center[1] - height, center[2] - depth, center[0] - width, center[1] + height, center[2] - depth, center[0] + width, center[1] + height, center[2] - depth, center[0] - width, center[1] + height, center[2] - depth, center[0] + width, center[1] + height, center[2] + depth, center[0] - width, center[1] + height, center[2] - depth, center[0] - width, center[1] + height, center[2] + depth, center[0] + width, center[1] + height, center[2] + depth, ]) normal = array('f', [ -0, 0, 1, -0, 0, 1, -0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, -1, -0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, ]) uv = array('f', [ 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0 ]) return ctx.geometry([ BufferDescription(ctx.buffer(data=pos), '3f', ['in_position']), BufferDescription(ctx.buffer(data=normal), '3f', ['in_normal']), BufferDescription(ctx.buffer(data=uv), '2f', ['in_uv']), ])
def __init__(self, width, height, title): """ Set up the application. """ self.time = 0 super().__init__(width, height, title) self.program = self.ctx.program(vertex_shader=""" #version 330 uniform float time; in vec2 in_vert; // Per instance data in vec2 in_offset; in vec4 in_color; out vec4 v_color; void main() { // Create rotation based on what instance we are drawing float angle = time * 0.5 + gl_InstanceID; mat2 rot = mat2( cos(angle), sin(angle), -sin(angle), cos(angle) ); // scale down triangle, offset and rotate vec2 pos = (rot * (in_vert * 0.07)) + in_offset; gl_Position = vec4(pos, 0.0, 1.0); v_color = in_color; } """, fragment_shader=""" #version 330 in vec4 v_color; out vec4 out_color; void main() { out_color = v_color; } """) self.instances = 1_000 # Create triangle vertices = array( "f", [ # x, y 0.0, 0.8, -0.8, -0.8, 0.8, -0.8, ]) # Generat per instance data. We'll create a generator function for this (less messy) def gen_instance_data(instances): random.seed(123456) for _ in range(instances): # random x and y yield random.uniform(-1.0, 1.0) yield random.uniform(-1.0, 1.0) # random rgba color yield random.uniform(0.1, 1.0) yield random.uniform(0.1, 1.0) yield random.uniform(0.1, 1.0) yield random.uniform(0.1, 1.0) per_instance = array("f", gen_instance_data(self.instances)) self.geometry = self.ctx.geometry( [ # Base geometry BufferDescription( self.ctx.buffer(data=vertices), '2f', ['in_vert'], ), # Per instance buffer BufferDescription( self.ctx.buffer(data=per_instance), '2f 4f', ['in_offset', 'in_color'], instanced=True, ), ], mode=self.ctx.TRIANGLES)