code: from pathlib import Path
  import random
  import numpy
  from pyrr import matrix44
  
  import moderngl
  import moderngl_window
  from moderngl_window.opengl.vao import VAO
  
  
  class Boids(moderngl_window.WindowConfig):
      """
      An attempt to make something boid-list with GL3.3.
      Not currently working as intended, but still creates
      and interesting result.
  
      For this to properly work we need to split the calculations
      into several passes.
  
      We are doing this the O(n^2) way with the gpu using transform feedback.
      To make the data avaialble to the vertex shader (looping through it)
      we copy the vertex buffer every frame to a texture.
  
      A better way in the future is to use compute shader.
      """
      title = "Boids"
      resource_dir = (Path(__file__) / '../../resources').absolute()
      aspect_ratio = 3440 / 1440
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
  
          MAX_TEX_WIDTH = 8192
          N = MAX_TEX_WIDTH * 1
  
          def gen_initial_data(n, x_area=2.0, y_area=2.0):
              for n in range(n):
                  # position
                  yield (random.random() - 0.5) * x_area
                  yield (random.random() - 0.5) * y_area
                  # Velocity
                  yield (random.random() - 0.5)
                  yield (random.random() - 0.5)
  
          # Create geometry data
          gen = gen_initial_data(N, x_area=self.aspect_ratio * 2 * 0.9, y_area=2.0 * 0.95)
          data = numpy.fromiter(gen, count=N * 4, dtype='f4')
          self.boids_buffer_1 = self.ctx.buffer(data.tobytes())
          self.boids_buffer_2 = self.ctx.buffer(data=self.boids_buffer_1.read())
  
          self.boids_vao_1 = VAO(name='boids_1', mode=moderngl.POINTS)
          self.boids_vao_1.buffer(self.boids_buffer_1, '2f 2f', ['in_position', 'in_velocity'])
  
          self.boids_vao_2 = VAO(name='boids_2', mode=moderngl.POINTS)
          self.boids_vao_2.buffer(self.boids_buffer_2, '2f 2f', ['in_position', 'in_velocity'])
  
          self.boids_texture = self.ctx.texture((MAX_TEX_WIDTH, N * 2 // MAX_TEX_WIDTH), components=2, dtype='f4')
  
          # Programs
          self.boids_render_program = self.load_program('programs/boids/boids_render.glsl')
          self.boids_transform_program = self.load_program('programs/boids/boids_transform.glsl')
  
          # Prepare for rendering
          self.m_proj = matrix44.create_orthogonal_projection(
              -self.aspect_ratio, self.aspect_ratio,
              -1.0, 1.0,
              -1.0, 1.0,
              dtype='f4',
          )
          self.boids_render_program['m_proj'].write(self.m_proj.tobytes())
          self.boids_transform_program['data'].value = 0
          self.boids_transform_program['num_boids'].value = N
          self.boids_transform_program['tex_width'].value = MAX_TEX_WIDTH
  
      def render(self, time, frame_time):
  
          self.boids_texture.use(location=0)
          self.boids_transform_program['timedelta'].value = frame_time  # max(frame_time, 1.0 / 60.0)
          self.boids_vao_1.transform(self.boids_transform_program, self.boids_buffer_2)
          self.boids_vao_2.render(self.boids_render_program)
  
          # Swap around ..
          self.boids_vao_1, self.boids_vao_2 = self.boids_vao_2, self.boids_vao_1
          self.boids_buffer_1, self.boids_buffer_2 = self.boids_buffer_2, self.boids_buffer_1
  
          # Write vertex data into texture so we can interate it in shader
          self.boids_texture.write(self.boids_buffer_1.read())
  
  
  if __name__ == '__main__':
      moderngl_window.run_window_config(Boids)
  
apis: numpy.fromiter