text: import math
  
  import zengl
  from objloader import Obj
  
  from window import Window
  
  window = Window(1280, 720)
  ctx = zengl.context()
  
  image = ctx.image(window.size, 'rgba8unorm', samples=4)
  depth = ctx.image(window.size, 'depth24plus', samples=4)
  image.clear_value = (1.0, 1.0, 1.0, 1.0)
  
  model = Obj.open('examples/data/box.obj').pack('vx vy vz nx ny nz tx ty')
  vertex_buffer = ctx.buffer(model)
  
  texture_ms = ctx.image((256, 256), 'rgba8unorm', samples=4)
  texture = ctx.image((256, 256), 'rgba8unorm')
  texture_ms.clear_value = (0.5, 0.5, 0.5, 1.0)
  
  uniform_buffer = ctx.buffer(size=80)
  
  triangle = ctx.pipeline(
      vertex_shader='''
          #version 330
  
          out vec3 v_color;
  
          vec2 positions[3] = vec2[](
              vec2(0.0, 0.7),
              vec2(-0.7, -0.7),
              vec2(0.7, -0.7)
          );
  
          vec3 colors[3] = vec3[](
              vec3(1.0, 0.0, 0.0),
              vec3(0.0, 1.0, 0.0),
              vec3(0.0, 0.0, 1.0)
          );
  
          void main() {
              gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
              v_color = colors[gl_VertexID];
          }
      ''',
      fragment_shader='''
          #version 330
  
          in vec3 v_color;
  
          layout (location = 0) out vec4 out_color;
  
          void main() {
              out_color = vec4(v_color, 1.0);
          }
      ''',
      framebuffer=[texture_ms],
      topology='triangles',
      vertex_count=3,
  )
  
  crate = ctx.pipeline(
      vertex_shader='''
          #version 330
  
          uniform Common {
              mat4 mvp;
              vec3 light;
          };
  
          layout (location = 0) in vec3 in_vert;
          layout (location = 1) in vec3 in_norm;
          layout (location = 2) in vec2 in_text;
  
          out vec3 v_vert;
          out vec3 v_norm;
          out vec2 v_text;
  
          void main() {
              gl_Position = mvp * vec4(in_vert, 1.0);
              v_vert = in_vert;
              v_norm = in_norm;
              v_text = in_text;
          }
      ''',
      fragment_shader='''
          #version 330
  
          uniform Common {
              mat4 mvp;
              vec3 light;
          };
  
          uniform sampler2D Texture;
  
          in vec3 v_vert;
          in vec3 v_norm;
          in vec2 v_text;
  
          layout (location = 0) out vec4 out_color;
  
          void main() {
              float lum = clamp(dot(normalize(light - v_vert), normalize(v_norm)), 0.0, 1.0) * 0.6 + 0.4;
              out_color = vec4(texture(Texture, v_text).rgb * lum, 1.0);
          }
      ''',
      layout=[
          {
              'name': 'Common',
              'binding': 0,
          },
          {
              'name': 'Texture',
              'binding': 0,
          },
      ],
      resources=[
          {
              'type': 'uniform_buffer',
              'binding': 0,
              'buffer': uniform_buffer,
          },
          {
              'type': 'sampler',
              'binding': 0,
              'image': texture,
          }
      ],
      framebuffer=[image, depth],
      topology='triangles',
      cull_face='back',
      vertex_buffers=zengl.bind(vertex_buffer, '3f 3f 2f', 0, 1, 2),
      vertex_count=vertex_buffer.size // zengl.calcsize('3f 3f 2f'),
  )
  
  texture_ms.clear()
  triangle.render()
  texture_ms.blit(texture)
  
  while window.update():
      x, y = math.sin(window.time * 0.5) * 3.0, math.cos(window.time * 0.5) * 3.0
      camera = zengl.camera((x, y, 2.0), (0.0, 0.0, 0.5), aspect=window.aspect, fov=45.0)
  
      uniform_buffer.write(camera)
      uniform_buffer.write(zengl.pack(x, y, 2.0, 0.0), offset=64)
  
      image.clear()
      depth.clear()
      crate.render()
      image.blit()
  