changeset 522:f2c97bdbb0b3

did: rename and update older demos to work with new APIs
author Sam <sam@basx.dev>
date Tue, 24 Jan 2023 10:22:38 +0700
parents a25325bec7f2
children 311ee4e58032
files examples/E01_hello_triangle.nim examples/E02_squares.nim examples/E03_hello_cube.nim examples/E04_input.nim examples/alotof_triangles.nim examples/hello_cube.nim examples/hello_triangle.nim examples/input.nim examples/squares.nim src/semicongine/engine.nim src/semicongine/thing.nim src/semicongine/vertex.nim
diffstat 12 files changed, 648 insertions(+), 633 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/E01_hello_triangle.nim	Tue Jan 24 10:22:38 2023 +0700
@@ -0,0 +1,55 @@
+import std/times
+import std/strutils
+import std/enumerate
+
+import semicongine
+
+type
+  # define type of vertex
+  VertexDataA = object
+    position: PositionAttribute[Vec2]
+    color: ColorAttribute[Vec3]
+
+var pipeline: RenderPipeline[VertexDataA, void]
+
+proc globalUpdate(engine: var Engine, dt: float32) =
+  discard
+
+# vertex data (types must match the above VertexAttributes)
+const
+  triangle_pos = @[
+    Vec2([0.0'f32, -0.5'f32]),
+    Vec2([0.5'f32, 0.5'f32]),
+    Vec2([-0.5'f32, 0.5'f32]),
+  ]
+  triangle_color = @[
+    Vec3([1.0'f32, 0.0'f32, 0.0'f32]),
+    Vec3([0.0'f32, 1.0'f32, 0.0'f32]),
+    Vec3([0.0'f32, 0.0'f32, 1.0'f32]),
+  ]
+
+when isMainModule:
+  var myengine = igniteEngine("Hello triangle")
+
+  # build a mesh
+  var trianglemesh = new Mesh[VertexDataA]
+  trianglemesh.vertexData = VertexDataA(
+    position: PositionAttribute[Vec2](data: triangle_pos),
+    color: ColorAttribute[Vec3](data: triangle_color),
+  )
+  # build a single-object scene graph
+  var triangle = newThing("triangle", trianglemesh)
+
+  # upload data, prepare shaders, etc
+  const vertexShader = generateVertexShaderCode[VertexDataA, void]()
+  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
+  pipeline = setupPipeline[VertexDataA, void, void](
+    myengine,
+    triangle,
+    vertexShader,
+    fragmentShader
+  )
+  # show something
+  myengine.run(pipeline, globalUpdate)
+  pipeline.trash()
+  myengine.trash()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/E02_squares.nim	Tue Jan 24 10:22:38 2023 +0700
@@ -0,0 +1,94 @@
+import std/times
+import std/strutils
+import std/math
+import std/random
+
+import semicongine
+
+type
+  VertexDataA = object
+    position11: PositionAttribute[Vec2]
+    color22: ColorAttribute[Vec3]
+    index: GenericAttribute[uint32]
+  Uniforms = object
+    t: Descriptor[float32]
+
+var
+  pipeline: RenderPipeline[VertexDataA, Uniforms]
+  uniformdata = Uniforms(t: Descriptor[float32](value: 0'f32))
+
+proc globalUpdate(engine: var Engine, dt: float32) =
+  uniformdata.t.value += dt
+  engine.vulkan.device.updateUniformData(pipeline, uniformdata)
+
+when isMainModule:
+  randomize()
+  var myengine = igniteEngine("Squares")
+  const
+    COLUMNS = 10
+    ROWS = 10
+    WIDTH = 2'f32 / COLUMNS
+    HEIGHT = 2'f32 / ROWS
+  var
+    vertices: array[COLUMNS * ROWS * 4, Vec2]
+    colors: array[COLUMNS * ROWS * 4, Vec3]
+    iValues: array[COLUMNS * ROWS * 4, uint32]
+    indices: array[COLUMNS * ROWS * 2, array[3, uint16]]
+
+  for row in 0 ..< ROWS:
+    for col in 0 ..< COLUMNS:
+      let
+        y: float32 = (row * 2 / COLUMNS) - 1
+        x: float32 = (col * 2 / ROWS) - 1
+        color = Vec3([(x + 1) / 2, (y + 1) / 2, 0'f32])
+        squareIndex = row * COLUMNS + col
+        vertIndex = squareIndex * 4
+      vertices[vertIndex + 0] = Vec2([x, y])
+      vertices[vertIndex + 1] = Vec2([x + WIDTH, y])
+      vertices[vertIndex + 2] = Vec2([x + WIDTH, y + HEIGHT])
+      vertices[vertIndex + 3] = Vec2([x, y + HEIGHT])
+      colors[vertIndex + 0] = color
+      colors[vertIndex + 1] = color
+      colors[vertIndex + 2] = color
+      colors[vertIndex + 3] = color
+      iValues[vertIndex + 0] = uint32(squareIndex)
+      iValues[vertIndex + 1] = uint32(squareIndex)
+      iValues[vertIndex + 2] = uint32(squareIndex)
+      iValues[vertIndex + 3] = uint32(squareIndex)
+      indices[squareIndex * 2 + 0] = [uint16(vertIndex + 0), uint16(vertIndex +
+          1), uint16(vertIndex + 2)]
+      indices[squareIndex * 2 + 1] = [uint16(vertIndex + 2), uint16(vertIndex +
+          3), uint16(vertIndex + 0)]
+
+
+  type PIndexedMesh = IndexedMesh[VertexDataA,
+      uint16] # required so we can use ctor with ref/on heap
+  var squaremesh = PIndexedMesh(
+    vertexData: VertexDataA(
+      position11: PositionAttribute[Vec2](data: @vertices),
+      color22: ColorAttribute[Vec3](data: @colors),
+      index: GenericAttribute[uint32](data: @iValues),
+    ),
+    indices: @indices
+  )
+  var scene = newThing("scene", newThing("squares", squaremesh))
+
+  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms](
+    """
+    float pos_weight = index / 100.0; // add some gamma correction?
+    float t = sin(uniforms.t * 0.5) * 0.5 + 0.5;
+    float v = min(1, max(0, pow(pos_weight - t, 2)));
+    v = pow(1 - v, 3000);
+    out_color = vec3(in_color.r, in_color.g, v * 0.5);
+    """
+  )
+  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
+  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
+    myengine,
+    scene,
+    vertexShader,
+    fragmentShader
+  )
+  myengine.run(pipeline, globalUpdate)
+  pipeline.trash()
+  myengine.trash()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/E03_hello_cube.nim	Tue Jan 24 10:22:38 2023 +0700
@@ -0,0 +1,110 @@
+#
+#   TODO: Needs Depth-Buffer first!
+#
+#
+#
+#
+#
+#
+
+
+import std/times
+import std/strutils
+
+import semicongine
+
+type
+  # define type of vertex
+  VertexDataA = object
+    position: PositionAttribute[Vec3]
+    color: ColorAttribute[Vec3]
+  Uniforms = object
+    model: Descriptor[Mat44]
+    view: Descriptor[Mat44]
+    projection: Descriptor[Mat44]
+
+var
+  pipeline: RenderPipeline[VertexDataA, Uniforms]
+  uniforms: Uniforms
+  t: float32
+
+
+proc globalUpdate(engine: var Engine, dt: float32) =
+  let ratio = float32(engine.vulkan.frameSize.y) / float32(
+      engine.vulkan.frameSize.x)
+  t += dt
+  uniforms.model.value = translate3d(0'f32, 0'f32, 10'f32) * rotate3d(t,
+      Yf32) #  * rotate3d(float32(PI), Yf32)
+
+  uniforms.view.value = Unit44f32
+  uniforms.projection.value = Mat44(data: [
+    ratio, 0'f32, 0'f32, 0'f32,
+    0'f32, 1'f32, 0'f32, 0'f32,
+    0'f32, 0'f32, 1'f32, 0'f32,
+    0'f32, 0'f32, 0'f32, 1'f32,
+  ])
+  uniforms.projection.value = perspective(float32(PI / 4), float32(
+      engine.vulkan.frameSize.x) / float32(
+      engine.vulkan.frameSize.y), 0.1'f32, 100'f32)
+  engine.vulkan.device.updateUniformData(pipeline, uniforms)
+
+const
+  TopLeftFront = Vec3([-0.5'f32, -0.5'f32, -0.5'f32])
+  TopRightFront = Vec3([0.5'f32, -0.5'f32, -0.5'f32])
+  BottomRightFront = Vec3([0.5'f32, 0.5'f32, -0.5'f32])
+  BottomLeftFront = Vec3([-0.5'f32, 0.5'f32, -0.5'f32])
+  TopLeftBack = Vec3([0.5'f32, -0.5'f32, 0.5'f32])
+  TopRightBack = Vec3([-0.5'f32, -0.5'f32, 0.5'f32])
+  BottomRightBack = Vec3([-0.5'f32, 0.5'f32, 0.5'f32])
+  BottomLeftBack = Vec3([0.5'f32, 0.5'f32, 0.5'f32])
+const
+  cube_pos = @[
+    TopLeftFront, TopRightFront, BottomRightFront, BottomLeftFront, # front
+    TopLeftBack, TopRightBack, BottomRightBack, BottomLeftBack, # back
+    TopLeftBack, TopLeftFront, BottomLeftFront, BottomLeftBack, # left
+    TopRightBack, TopRightFront, BottomRightFront, BottomRightBack, # right
+    TopLeftBack, TopRightBack, TopRightFront, TopLeftFront, # top
+    BottomLeftFront, BottomRightFront, BottomRightBack, BottomLeftBack, # bottom
+  ]
+  cube_color = @[
+    Rf32, Rf32, Rf32, Rf32,
+    Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32,
+    Gf32, Gf32, Gf32, Gf32,
+    Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32,
+    Bf32, Bf32, Bf32, Bf32,
+    Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32,
+  ]
+var
+  tris: seq[array[3, uint16]]
+for i in 0'u16 ..< 6'u16:
+  let off = i * 4
+  tris.add [off + 0'u16, off + 1'u16, off + 2'u16]
+  tris.add [off + 2'u16, off + 3'u16, off + 0'u16]
+
+when isMainModule:
+  var myengine = igniteEngine("Hello cube")
+
+  # build a mesh
+  var trianglemesh = new IndexedMesh[VertexDataA, uint16]
+  trianglemesh.vertexData = VertexDataA(
+    position: PositionAttribute[Vec3](data: cube_pos),
+    color: ColorAttribute[Vec3](data: cube_color),
+  )
+  trianglemesh.indices = tris
+  var cube = newThing("cube", trianglemesh)
+
+  # upload data, prepare shaders, etc
+  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]("""
+  out_position = (uniforms.projection * uniforms.view * uniforms.model) * vec4(in_position, 1);
+  """)
+  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
+  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
+    myengine,
+    cube,
+    vertexShader,
+    fragmentShader
+  )
+  # show something
+  myengine.run(pipeline, globalUpdate)
+  pipeline.trash()
+  myengine.trash()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/E04_input.nim	Tue Jan 24 10:22:38 2023 +0700
@@ -0,0 +1,117 @@
+import std/strutils
+import std/times
+
+import semicongine
+
+type
+  # define type of vertex
+  VertexDataA = object
+    position: PositionAttribute[Vec2]
+    color: ColorAttribute[Vec3]
+    transform: ModelTransformAttribute
+  Uniforms = object
+    projection: Descriptor[Mat44]
+    cursor: Descriptor[Vec2]
+
+var
+  pipeline: RenderPipeline[VertexDataA, Uniforms]
+  uniforms: Uniforms
+  scene: Thing
+  time: float
+
+
+proc globalUpdate(engine: var Engine, dt: float32) =
+  time += dt
+  uniforms.cursor.value = engine.input.mousePos
+  uniforms.projection.value = ortho[float32](
+    0'f32, float32(engine.vulkan.frameSize.x),
+    0'f32, float32(engine.vulkan.frameSize.y),
+    0'f32, 1'f32,
+  )
+  engine.vulkan.device.updateUniformData(pipeline, uniforms)
+
+  let cursor = firstPartWithName[Mesh[VertexDataA]](scene, "cursor")
+  if cursor != nil:
+    for c in cursor.vertexData.color.data.mitems:
+      c[1] = (sin(time * 8) * 0.5 + 0.5) * 0.2
+      c[2] = (sin(time * 8) * 0.5 + 0.5) * 0.2
+    engine.vulkan.device.updateVertexData(cursor.vertexData.color)
+
+    var trans = translate3d(engine.input.mousePos.x, engine.input.mousePos.y, 0'f32)
+    # cursor.vertexData.transform.data = @[trans.transposed()]
+    engine.vulkan.device.updateVertexData(cursor.vertexData.transform)
+
+
+const
+  shape = @[
+    Vec2([ - 1'f32, - 1'f32]),
+    Vec2([1'f32, - 1'f32]),
+    Vec2([-0.3'f32, -0.3'f32]),
+    Vec2([-0.3'f32, -0.3'f32]),
+    Vec2([ - 1'f32, 1'f32]),
+    Vec2([ - 1'f32, - 1'f32]),
+  ]
+  colors = @[
+    Vec3([1'f32, 0'f32, 0'f32]),
+    Vec3([1'f32, 0'f32, 0'f32]),
+    Vec3([1'f32, 0'f32, 0'f32]),
+    Vec3([0.8'f32, 0'f32, 0'f32]),
+    Vec3([0.8'f32, 0'f32, 0'f32]),
+    Vec3([0.8'f32, 0'f32, 0'f32]),
+  ]
+
+when isMainModule:
+  var myengine = igniteEngine("Input")
+
+  var cursormesh = new Mesh[VertexDataA]
+  cursormesh.vertexData = VertexDataA(
+    position: PositionAttribute[Vec2](data: shape, useOnDeviceMemory: true),
+    color: ColorAttribute[Vec3](data: colors),
+    transform: ModelTransformAttribute(data: @[Unit44]),
+  )
+  # transform the cursor a bit to make it look nice
+  for i in 0 ..< cursormesh.vertexData.position.data.len:
+    let cursorscale = (
+      scale2d(20'f32, 20'f32) *
+      translate2d(1'f32, 1'f32) *
+      rotate2d(-float32(PI) / 4'f32) *
+      scale2d(0.5'f32, 1'f32) *
+      rotate2d(float32(PI) / 4'f32)
+    )
+    let pos = Vec3([cursormesh.vertexData.position.data[i][0],
+        cursormesh.vertexData.position.data[i][1], 1'f32])
+    cursormesh.vertexData.position.data[i] = (cursorscale * pos).xy
+
+  var boxmesh = new Mesh[VertexDataA]
+  boxmesh.vertexData = VertexDataA(
+    position: PositionAttribute[Vec2](data: shape),
+    color: ColorAttribute[Vec3](data: colors),
+    transform: ModelTransformAttribute(data: @[Unit44]),
+  )
+  for i in 0 ..< boxmesh.vertexData.position.data.len:
+    let boxscale = translate2d(100'f32, 100'f32) * scale2d(100'f32, 100'f32)
+    let pos = Vec3([boxmesh.vertexData.position.data[i][0],
+        boxmesh.vertexData.position.data[i][1], 1'f32])
+    boxmesh.vertexData.position.data[i] = (boxscale * pos).xy
+
+  scene = newThing("scene")
+  scene.add newThing("cursor", cursormesh)
+  scene.add newThing("a box", boxmesh, newTransform(Unit44), newTransform(
+      translate3d(1'f32, 0'f32, 0'f32)))
+  scene.add newTransform(scale3d(1.5'f32, 1.5'f32, 1.5'f32))
+
+  # upload data, prepare shaders, etc
+  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]("""
+    out_position = uniforms.projection * transform * vec4(position, 0, 1);
+  """)
+  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
+  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
+    myengine,
+    scene,
+    vertexShader,
+    fragmentShader
+  )
+  # show something
+  myengine.run(pipeline, globalUpdate)
+  pipeline.trash()
+  myengine.trash()
--- a/examples/alotof_triangles.nim	Sun Jan 22 22:46:53 2023 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-import std/times
-import std/strutils
-import std/math
-import std/random
-
-import semicongine
-
-type
-  VertexDataA = object
-    position11: PositionAttribute[Vec2]
-    color22: ColorAttribute[Vec3]
-  Uniforms = object
-    dt: Descriptor[float32]
-
-proc globalUpdate(engine: var Engine, dt: float32) =
-  discard
-
-proc randomtransform(): Mat33 =
-  let randomscale = scale2d(float32(rand(1.0) + 0.5), float32(rand(1.0) + 0.5))
-  let randomrotate = rotate2d(float32(rand(2 * PI)))
-  let randomtranslate = translate2d(float32(rand(1.6) - 0.8), float32(rand(
-      1.6) - 0.8))
-  result = randomtranslate * randomrotate * randomscale
-
-when isMainModule:
-  randomize()
-  var myengine = igniteEngine("A lot of triangles")
-  const baseTriangle = [
-    Vec3([-0.1'f32, -0.1'f32, 1'f32]),
-    Vec3([0.1'f32, 0.1'f32, 1'f32]),
-    Vec3([-0.1'f32, 0.1'f32, 1'f32]),
-  ]
-
-  var scene = newThing("scene")
-
-  for i in 1 .. 300:
-    var randommesh = new Mesh[VertexDataA]
-    let randomcolor1 = Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))])
-    let transform1 = randomtransform()
-    randommesh.vertexData = VertexDataA(
-      position11: PositionAttribute[Vec2](
-        data: @[
-          Vec2(transform1 * baseTriangle[0]),
-          Vec2(transform1 * baseTriangle[1]),
-          Vec2(transform1 * baseTriangle[2]),
-      ]
-    ),
-      color22: ColorAttribute[Vec3](
-        data: @[randomcolor1, randomcolor1, randomcolor1]
-      )
-    )
-
-    let randomcolor2 = Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))])
-    let transform2 = randomtransform()
-    var randomindexedmesh = new IndexedMesh[VertexDataA, uint16]
-    randomindexedmesh.vertexData = VertexDataA(
-      position11: PositionAttribute[Vec2](
-        data: @[
-          Vec2(transform2 * baseTriangle[0]),
-          Vec2(transform2 * baseTriangle[1]),
-          Vec2(transform2 * baseTriangle[2]),
-      ]
-    ),
-      color22: ColorAttribute[Vec3](
-        data: @[randomcolor2, randomcolor2, randomcolor2]
-      )
-    )
-    randomindexedmesh.indices = @[[0'u16, 1'u16, 2'u16]]
-    scene.add newThing("randommesh", randommesh, randomindexedmesh)
-
-  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]()
-  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
-  var pipeline = setupPipeline[VertexDataA, float32, uint16](
-    myengine,
-    scene,
-    vertexShader,
-    fragmentShader
-  )
-  myengine.run(pipeline, globalUpdate)
-  pipeline.trash()
-  myengine.trash()
--- a/examples/hello_cube.nim	Sun Jan 22 22:46:53 2023 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-#
-#   TODO: Needs Depth-Buffer first!
-#
-#
-#
-#
-#
-#
-
-
-import std/times
-import std/strutils
-
-import semicongine
-
-type
-  # define type of vertex
-  VertexDataA = object
-    position: PositionAttribute[Vec3]
-    color: ColorAttribute[Vec3]
-  Uniforms = object
-    model: Descriptor[Mat44]
-    view: Descriptor[Mat44]
-    projection: Descriptor[Mat44]
-
-var
-  pipeline: RenderPipeline[VertexDataA, Uniforms]
-  uniforms: Uniforms
-  t: float32
-
-
-proc globalUpdate(engine: var Engine, dt: float32) =
-  let ratio = float32(engine.vulkan.frameDimension.height) / float32(
-      engine.vulkan.frameDimension.width)
-  t += dt
-  uniforms.model.value = translate3d(0'f32, 0'f32, 10'f32) * rotate3d(t,
-      Yf32) #  * rotate3d(float32(PI), Yf32)
-
-  uniforms.view.value = Unit44f32
-  uniforms.projection.value = Mat44(data: [
-    ratio, 0'f32, 0'f32, 0'f32,
-    0'f32, 1'f32, 0'f32, 0'f32,
-    0'f32, 0'f32, 1'f32, 0'f32,
-    0'f32, 0'f32, 0'f32, 1'f32,
-  ])
-  uniforms.projection.value = perspective(float32(PI / 4), float32(
-      engine.vulkan.frameDimension.width) / float32(
-      engine.vulkan.frameDimension.height), 0.1'f32, 100'f32)
-  pipeline.updateUniformValues(uniforms)
-
-const
-  TopLeftFront = Vec3([-0.5'f32, -0.5'f32, -0.5'f32])
-  TopRightFront = Vec3([0.5'f32, -0.5'f32, -0.5'f32])
-  BottomRightFront = Vec3([0.5'f32, 0.5'f32, -0.5'f32])
-  BottomLeftFront = Vec3([-0.5'f32, 0.5'f32, -0.5'f32])
-  TopLeftBack = Vec3([0.5'f32, -0.5'f32, 0.5'f32])
-  TopRightBack = Vec3([-0.5'f32, -0.5'f32, 0.5'f32])
-  BottomRightBack = Vec3([-0.5'f32, 0.5'f32, 0.5'f32])
-  BottomLeftBack = Vec3([0.5'f32, 0.5'f32, 0.5'f32])
-const
-  cube_pos = @[
-    TopLeftFront, TopRightFront, BottomRightFront, BottomLeftFront, # front
-    TopLeftBack, TopRightBack, BottomRightBack, BottomLeftBack, # back
-    TopLeftBack, TopLeftFront, BottomLeftFront, BottomLeftBack, # left
-    TopRightBack, TopRightFront, BottomRightFront, BottomRightBack, # right
-    TopLeftBack, TopRightBack, TopRightFront, TopLeftFront, # top
-    BottomLeftFront, BottomRightFront, BottomRightBack, BottomLeftBack, # bottom
-  ]
-  cube_color = @[
-    Rf32, Rf32, Rf32, Rf32,
-    Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32,
-    Gf32, Gf32, Gf32, Gf32,
-    Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32,
-    Bf32, Bf32, Bf32, Bf32,
-    Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32,
-  ]
-var
-  tris: seq[array[3, uint16]]
-for i in 0'u16 ..< 6'u16:
-  let off = i * 4
-  tris.add [off + 0'u16, off + 1'u16, off + 2'u16]
-  tris.add [off + 2'u16, off + 3'u16, off + 0'u16]
-
-when isMainModule:
-  var myengine = igniteEngine("Hello cube")
-
-  # build a mesh
-  var trianglemesh = new IndexedMesh[VertexDataA, uint16]
-  trianglemesh.vertexData = VertexDataA(
-    position: PositionAttribute[Vec3](data: cube_pos),
-    color: ColorAttribute[Vec3](data: cube_color),
-  )
-  trianglemesh.indices = tris
-  var cube = newThing("cube", trianglemesh)
-
-  # upload data, prepare shaders, etc
-  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]("""
-  out_position = (uniforms.projection * uniforms.view * uniforms.model) * vec4(in_position, 1);
-  """)
-  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
-  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
-    myengine,
-    cube,
-    vertexShader,
-    fragmentShader
-  )
-  # show something
-  myengine.run(pipeline, globalUpdate)
-  pipeline.trash()
-  myengine.trash()
--- a/examples/hello_triangle.nim	Sun Jan 22 22:46:53 2023 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-import std/times
-import std/strutils
-import std/enumerate
-
-import semicongine
-
-type
-  # define type of vertex
-  VertexDataA = object
-    position: PositionAttribute[Vec2]
-    color: ColorAttribute[Vec3]
-
-var pipeline: RenderPipeline[VertexDataA, void]
-
-proc globalUpdate(engine: var Engine, dt: float32) =
-  discard
-
-# vertex data (types must match the above VertexAttributes)
-const
-  triangle_pos = @[
-    Vec2([0.0'f32, -0.5'f32]),
-    Vec2([0.5'f32, 0.5'f32]),
-    Vec2([-0.5'f32, 0.5'f32]),
-  ]
-  triangle_color = @[
-    Vec3([1.0'f32, 0.0'f32, 0.0'f32]),
-    Vec3([0.0'f32, 1.0'f32, 0.0'f32]),
-    Vec3([0.0'f32, 0.0'f32, 1.0'f32]),
-  ]
-
-when isMainModule:
-  var myengine = igniteEngine("Hello triangle")
-
-  # build a mesh
-  var trianglemesh = new Mesh[VertexDataA]
-  trianglemesh.vertexData = VertexDataA(
-    position: PositionAttribute[Vec2](data: triangle_pos),
-    color: ColorAttribute[Vec3](data: triangle_color),
-  )
-  # build a single-object scene graph
-  var triangle = newThing("triangle", trianglemesh)
-
-  # upload data, prepare shaders, etc
-  const vertexShader = generateVertexShaderCode[VertexDataA, void]()
-  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
-  pipeline = setupPipeline[VertexDataA, void, void](
-    myengine,
-    triangle,
-    vertexShader,
-    fragmentShader
-  )
-  # show something
-  myengine.run(pipeline, globalUpdate)
-  pipeline.trash()
-  myengine.trash()
--- a/examples/input.nim	Sun Jan 22 22:46:53 2023 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-import std/strutils
-import std/times
-
-import semicongine
-
-type
-  # define type of vertex
-  VertexDataA = object
-    position: PositionAttribute[Vec2]
-    color: ColorAttribute[Vec3]
-    # transform: ModelTransformAttribute
-    # TODO: make this somehow a single vertex attribute
-    m1: GenericInstanceAttribute[Vec4]
-    m2: GenericInstanceAttribute[Vec4]
-    m3: GenericInstanceAttribute[Vec4]
-    m4: GenericInstanceAttribute[Vec4]
-  Uniforms = object
-    projection: Descriptor[Mat44]
-    cursor: Descriptor[Vec2]
-
-var
-  pipeline: RenderPipeline[VertexDataA, Uniforms]
-  uniforms: Uniforms
-  scene: Thing
-  time: float
-
-
-proc globalUpdate(engine: var Engine, dt: float32) =
-  time += dt
-  uniforms.cursor.value = engine.input.mousePos
-  uniforms.projection.value = ortho[float32](
-    0'f32, float32(engine.vulkan.frameSize.x),
-    0'f32, float32(engine.vulkan.frameSize.y),
-    0'f32, 1'f32,
-  )
-  engine.vulkan.device.updateUniformData(pipeline, uniforms)
-
-  let cursor = firstPartWithName[Mesh[VertexDataA]](scene, "cursor")
-  if cursor != nil:
-    for c in cursor.vertexData.color.data.mitems:
-      c[1] = (sin(time * 8) * 0.5 + 0.5) * 0.2
-      c[2] = (sin(time * 8) * 0.5 + 0.5) * 0.2
-    engine.vulkan.device.updateVertexData(cursor.vertexData.color)
-    var trans = Unit44 * translate3d(engine.input.mousePos.x,
-        engine.input.mousePos.y, 0'f32)
-    cursor.vertexData.m1.data = @[trans.col(0)]
-    cursor.vertexData.m2.data = @[trans.col(1)]
-    cursor.vertexData.m3.data = @[trans.col(2)]
-    cursor.vertexData.m4.data = @[trans.col(3)]
-    engine.vulkan.device.updateVertexData(cursor.vertexData.m1)
-    engine.vulkan.device.updateVertexData(cursor.vertexData.m2)
-    engine.vulkan.device.updateVertexData(cursor.vertexData.m3)
-    engine.vulkan.device.updateVertexData(cursor.vertexData.m4)
-
-
-const
-  shape = @[
-    Vec2([ - 1'f32, - 1'f32]),
-    Vec2([1'f32, - 1'f32]),
-    Vec2([-0.3'f32, -0.3'f32]),
-    Vec2([-0.3'f32, -0.3'f32]),
-    Vec2([ - 1'f32, 1'f32]),
-    Vec2([ - 1'f32, - 1'f32]),
-  ]
-  colors = @[
-    Vec3([1'f32, 0'f32, 0'f32]),
-    Vec3([1'f32, 0'f32, 0'f32]),
-    Vec3([1'f32, 0'f32, 0'f32]),
-    Vec3([0.8'f32, 0'f32, 0'f32]),
-    Vec3([0.8'f32, 0'f32, 0'f32]),
-    Vec3([0.8'f32, 0'f32, 0'f32]),
-  ]
-
-when isMainModule:
-  var myengine = igniteEngine("Input")
-
-  var cursormesh = new Mesh[VertexDataA]
-  cursormesh.vertexData = VertexDataA(
-    position: PositionAttribute[Vec2](data: shape, useOnDeviceMemory: true),
-    color: ColorAttribute[Vec3](data: colors),
-    # transform: ModelTransformAttribute(data: @[Unit44]),
-    m1: GenericInstanceAttribute[Vec4](data: @[Unit44.row(0)]),
-    m2: GenericInstanceAttribute[Vec4](data: @[Unit44.row(1)]),
-    m3: GenericInstanceAttribute[Vec4](data: @[Unit44.row(2)]),
-    m4: GenericInstanceAttribute[Vec4](data: @[Unit44.row(3)]),
-  )
-  # transform the cursor a bit to make it look nice
-  for i in 0 ..< cursormesh.vertexData.position.data.len:
-    let cursorscale = (
-      scale2d(20'f32, 20'f32) *
-      translate2d(1'f32, 1'f32) *
-      rotate2d(-float32(PI) / 4'f32) *
-      scale2d(0.5'f32, 1'f32) *
-      rotate2d(float32(PI) / 4'f32)
-    )
-    let pos = Vec3([cursormesh.vertexData.position.data[i][0],
-        cursormesh.vertexData.position.data[i][1], 1'f32])
-    cursormesh.vertexData.position.data[i] = (cursorscale * pos).xy
-
-  var boxmesh = new Mesh[VertexDataA]
-  boxmesh.vertexData = VertexDataA(
-    position: PositionAttribute[Vec2](data: shape),
-    color: ColorAttribute[Vec3](data: colors),
-    # transform: ModelTransformAttribute(data: @[Unit44]),
-    m1: GenericInstanceAttribute[Vec4](data: @[Unit44.row(0)]),
-    m2: GenericInstanceAttribute[Vec4](data: @[Unit44.row(1)]),
-    m3: GenericInstanceAttribute[Vec4](data: @[Unit44.row(2)]),
-    m4: GenericInstanceAttribute[Vec4](data: @[Unit44.row(3)]),
-  )
-  for i in 0 ..< boxmesh.vertexData.position.data.len:
-    let boxscale = translate2d(100'f32, 100'f32) * scale2d(100'f32, 100'f32)
-    let pos = Vec3([boxmesh.vertexData.position.data[i][0],
-        boxmesh.vertexData.position.data[i][1], 1'f32])
-    boxmesh.vertexData.position.data[i] = (boxscale * pos).xy
-  echo boxmesh.vertexData.position.data
-
-  scene = newThing("scene")
-  scene.add newThing("cursor", cursormesh)
-  scene.add newThing("a box", boxmesh, newTransform(Unit44), newTransform(
-      translate3d(1'f32, 0'f32, 0'f32)))
-  scene.add newTransform(scale3d(1.5'f32, 1.5'f32, 1.5'f32))
-
-  # upload data, prepare shaders, etc
-  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]("""
-    mat4 mat = mat4(m1, m2, m3, m4);
-    out_position = uniforms.projection * mat * vec4(position, 0, 1);
-  """)
-  echo vertexShader
-  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
-  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
-    myengine,
-    scene,
-    vertexShader,
-    fragmentShader
-  )
-  # show something
-  myengine.run(pipeline, globalUpdate)
-  pipeline.trash()
-  myengine.trash()
--- a/examples/squares.nim	Sun Jan 22 22:46:53 2023 +0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-import std/times
-import std/strutils
-import std/math
-import std/random
-
-import semicongine
-
-type
-  VertexDataA = object
-    position11: PositionAttribute[Vec2]
-    color22: ColorAttribute[Vec3]
-    index: GenericAttribute[uint32]
-  Uniforms = object
-    t: Descriptor[float32]
-
-var
-  pipeline: RenderPipeline[VertexDataA, Uniforms]
-  uniformdata = Uniforms(t: Descriptor[float32](value: 0'f32))
-
-proc globalUpdate(engine: var Engine, dt: float32) =
-  uniformdata.t.value += dt
-  pipeline.updateUniformValues(uniformdata)
-
-when isMainModule:
-  randomize()
-  var myengine = igniteEngine("Squares")
-  const
-    COLUMNS = 10
-    ROWS = 10
-    WIDTH = 2'f32 / COLUMNS
-    HEIGHT = 2'f32 / ROWS
-  var
-    vertices: array[COLUMNS * ROWS * 4, Vec2]
-    colors: array[COLUMNS * ROWS * 4, Vec3]
-    iValues: array[COLUMNS * ROWS * 4, uint32]
-    indices: array[COLUMNS * ROWS * 2, array[3, uint16]]
-
-  for row in 0 ..< ROWS:
-    for col in 0 ..< COLUMNS:
-      let
-        y: float32 = (row * 2 / COLUMNS) - 1
-        x: float32 = (col * 2 / ROWS) - 1
-        color = Vec3([(x + 1) / 2, (y + 1) / 2, 0'f32])
-        squareIndex = row * COLUMNS + col
-        vertIndex = squareIndex * 4
-      vertices[vertIndex + 0] = Vec2([x, y])
-      vertices[vertIndex + 1] = Vec2([x + WIDTH, y])
-      vertices[vertIndex + 2] = Vec2([x + WIDTH, y + HEIGHT])
-      vertices[vertIndex + 3] = Vec2([x, y + HEIGHT])
-      colors[vertIndex + 0] = color
-      colors[vertIndex + 1] = color
-      colors[vertIndex + 2] = color
-      colors[vertIndex + 3] = color
-      iValues[vertIndex + 0] = uint32(squareIndex)
-      iValues[vertIndex + 1] = uint32(squareIndex)
-      iValues[vertIndex + 2] = uint32(squareIndex)
-      iValues[vertIndex + 3] = uint32(squareIndex)
-      indices[squareIndex * 2 + 0] = [uint16(vertIndex + 0), uint16(vertIndex +
-          1), uint16(vertIndex + 2)]
-      indices[squareIndex * 2 + 1] = [uint16(vertIndex + 2), uint16(vertIndex +
-          3), uint16(vertIndex + 0)]
-
-
-  type PIndexedMesh = ref IndexedMesh[VertexDataA,
-      uint16] # required so we can use ctor with ref/on heap
-  var squaremesh = PIndexedMesh(
-    vertexData: VertexDataA(
-      position11: PositionAttribute[Vec2](data: @vertices),
-      color22: ColorAttribute[Vec3](data: @colors),
-      index: GenericAttribute[uint32](data: @iValues),
-    ),
-    indices: @indices
-  )
-  var scene = newThing("scene", newThing("squares", squaremesh))
-
-  const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms](
-    """
-    float pos_weight = index / 100.0; // add some gamma correction?
-    float t = sin(uniforms.t * 0.5) * 0.5 + 0.5;
-    float v = min(1, max(0, pow(pos_weight - t, 2)));
-    v = pow(1 - v, 3000);
-    out_color = vec3(in_color.r, in_color.g, v * 0.5);
-    """
-  )
-  const fragmentShader = generateFragmentShaderCode[VertexDataA]()
-  pipeline = setupPipeline[VertexDataA, Uniforms, uint16](
-    myengine,
-    scene,
-    vertexShader,
-    fragmentShader
-  )
-  myengine.run(pipeline, globalUpdate)
-  pipeline.trash()
-  myengine.trash()
--- a/src/semicongine/engine.nim	Sun Jan 22 22:46:53 2023 +0700
+++ b/src/semicongine/engine.nim	Tue Jan 24 10:22:38 2023 +0700
@@ -95,33 +95,40 @@
     currentscenedata*: Thing
     input*: Input
 
-proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] =
+proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[
+    PhysicalDevice] =
   for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance):
-    var device = PhysicalDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice))
+    var device = PhysicalDevice(device: vulkanPhysicalDevice,
+        extensions: getDeviceExtensions(vulkanPhysicalDevice))
     vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties))
     vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features))
     device.formats = vulkanPhysicalDevice.getDeviceSurfaceFormats(surface)
     device.presentModes = vulkanPhysicalDevice.getDeviceSurfacePresentModes(surface)
 
     debug(&"Physical device nr {int(vulkanPhysicalDevice)} {cleanString(device.properties.deviceName)}")
-    for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)):
+    for i, queueFamilyProperty in enumerate(getQueueFamilies(
+        vulkanPhysicalDevice)):
       var hasSurfaceSupport: VkBool32 = VK_FALSE
-      checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport))
-      device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport)))
+      checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice,
+          uint32(i), surface, addr(hasSurfaceSupport))
+      device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty,
+          hasSurfaceSupport: bool(hasSurfaceSupport)))
       debug(&"  Queue family {i} {queueFamilyProperty}")
 
     result.add(device)
 
 proc filterForDevice(devices: seq[PhysicalDevice]): seq[(PhysicalDevice, uint32, uint32)] =
   for device in devices:
-    if not (device.formats.len > 0 and device.presentModes.len > 0 and "VK_KHR_swapchain" in device.extensions):
+    if not (device.formats.len > 0 and device.presentModes.len > 0 and
+        "VK_KHR_swapchain" in device.extensions):
       continue
     var graphicsQueueFamily = high(uint32)
     var presentationQueueFamily = high(uint32)
     for i, queueFamily in enumerate(device.queueFamilies):
       if queueFamily.hasSurfaceSupport:
         presentationQueueFamily = uint32(i)
-      if bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT)):
+      if bool(uint32(queueFamily.properties.queueFlags) and ord(
+          VK_QUEUE_GRAPHICS_BIT)):
         graphicsQueueFamily = uint32(i)
     if graphicsQueueFamily != high(uint32) and presentationQueueFamily != high(uint32):
       result.add((device, graphicsQueueFamily, presentationQueueFamily))
@@ -130,15 +137,19 @@
     debug(&"Viable device: {cleanString(device.properties.deviceName)} (graphics queue family {graphicsQueueFamily}, presentation queue family {presentationQueueFamily})")
 
 
-proc getFrameDimension(window: NativeWindow, device: VkPhysicalDevice, surface: VkSurfaceKHR): TVec2[uint32] =
+proc getFrameDimension(window: NativeWindow, device: VkPhysicalDevice,
+    surface: VkSurfaceKHR): TVec2[uint32] =
   let capabilities = device.getSurfaceCapabilities(surface)
   if capabilities.currentExtent.width != high(uint32):
-    return TVec2[uint32]([capabilities.currentExtent.width, capabilities.currentExtent.height])
+    return TVec2[uint32]([capabilities.currentExtent.width,
+        capabilities.currentExtent.height])
   else:
     let (width, height) = window.size()
     return TVec2[uint32]([
-      min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width),
-      min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height),
+      min(max(uint32(width), capabilities.minImageExtent.width),
+          capabilities.maxImageExtent.width),
+      min(max(uint32(height), capabilities.minImageExtent.height),
+          capabilities.maxImageExtent.height),
     ])
 
 when DEBUG_LOG:
@@ -158,9 +169,11 @@
       pfnUserCallback: debugCallback,
       pUserData: nil,
     )
-    checkVkResult instance.vkCreateDebugUtilsMessengerEXT(addr(createInfo), nil, addr(result))
+    checkVkResult instance.vkCreateDebugUtilsMessengerEXT(addr(createInfo), nil,
+        addr(result))
 
-proc setupVulkanDeviceAndQueues(instance: VkInstance, surface: VkSurfaceKHR): Device =
+proc setupVulkanDeviceAndQueues(instance: VkInstance,
+    surface: VkSurfaceKHR): Device =
   let usableDevices = instance.getAllPhysicalDevices(surface).filterForDevice()
   if len(usableDevices) == 0:
     raise newException(Exception, "No suitable graphics device found")
@@ -169,7 +182,7 @@
   result.presentationQueueFamily = usableDevices[0][2]
 
   debug(&"Chose device {cleanString(result.physicalDevice.properties.deviceName)}")
-  
+
   (result.device, result.graphicsQueue, result.presentationQueue) = getVulcanDevice(
     result.physicalDevice.device,
     result.physicalDevice.features,
@@ -177,7 +190,9 @@
     result.presentationQueueFamily,
   )
 
-proc setupSwapChain(device: VkDevice, physicalDevice: PhysicalDevice, surface: VkSurfaceKHR, dimension: TVec2[uint32], surfaceFormat: VkSurfaceFormatKHR): Swapchain =
+proc setupSwapChain(device: VkDevice, physicalDevice: PhysicalDevice,
+    surface: VkSurfaceKHR, dimension: TVec2[uint32],
+    surfaceFormat: VkSurfaceFormatKHR): Swapchain =
 
   let capabilities = physicalDevice.device.getSurfaceCapabilities(surface)
   var selectedPresentationMode = getPresentMode(physicalDevice.presentModes)
@@ -202,7 +217,8 @@
     clipped: VK_TRUE,
     oldSwapchain: VkSwapchainKHR(0),
   )
-  checkVkResult device.vkCreateSwapchainKHR(addr(swapchainCreateInfo), nil, addr(result.swapchain))
+  checkVkResult device.vkCreateSwapchainKHR(addr(swapchainCreateInfo), nil,
+      addr(result.swapchain))
   result.images = device.getSwapChainImages(result.swapchain)
 
   # setup swapchian image views
@@ -228,7 +244,8 @@
         layerCount: 1,
       ),
     )
-    checkVkResult device.vkCreateImageView(addr(imageViewCreateInfo), nil, addr(result.imageviews[i]))
+    checkVkResult device.vkCreateImageView(addr(imageViewCreateInfo), nil, addr(
+        result.imageviews[i]))
 
 proc setupRenderPass(device: VkDevice, format: VkFormat): VkRenderPass =
   var
@@ -254,9 +271,11 @@
     dependency = VkSubpassDependency(
       srcSubpass: VK_SUBPASS_EXTERNAL,
       dstSubpass: 0,
-      srcStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
+      srcStageMask: VkPipelineStageFlags(
+          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
       srcAccessMask: VkAccessFlags(0),
-      dstStageMask: VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
+      dstStageMask: VkPipelineStageFlags(
+          VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT),
       dstAccessMask: VkAccessFlags(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
     )
     renderPassCreateInfo = VkRenderPassCreateInfo(
@@ -270,11 +289,15 @@
     )
   checkVkResult device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result))
 
-proc initRenderPipeline[VertexType, Uniforms](device: VkDevice, frameSize: TVec2[uint32], renderPass: VkRenderPass, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, Uniforms] =
+proc initRenderPipeline[VertexType, Uniforms](device: VkDevice,
+    frameSize: TVec2[uint32], renderPass: VkRenderPass, vertexShader,
+    fragmentShader: static string): RenderPipeline[VertexType, Uniforms] =
   # load shaders
   result.device = device
-  result.shaders.add(initShaderProgram[VertexType, Uniforms](device, VK_SHADER_STAGE_VERTEX_BIT, vertexShader))
-  result.shaders.add(initShaderProgram[VertexType, Uniforms](device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader))
+  result.shaders.add(initShaderProgram[VertexType, Uniforms](device,
+      VK_SHADER_STAGE_VERTEX_BIT, vertexShader))
+  result.shaders.add(initShaderProgram[VertexType, Uniforms](device,
+      VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShader))
 
   var
     # define which parts can be dynamic (pipeline is fixed after setup)
@@ -356,8 +379,9 @@
       blendConstants: [0.0'f, 0.0'f, 0.0'f, 0.0'f],
     )
 
-  result.descriptorSetLayout = device.createUniformDescriptorLayout(VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), 0)
-  var 
+  result.descriptorSetLayout = device.createUniformDescriptorLayout(
+      VkShaderStageFlags(VK_SHADER_STAGE_VERTEX_BIT), 0)
+  var
     # "globals" that go into the shader, uniforms etc.
     pipelineLayoutInfo = VkPipelineLayoutCreateInfo(
       sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
@@ -366,7 +390,8 @@
       pushConstantRangeCount: 0,
       pPushConstantRanges: nil,
     )
-  checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil, addr(result.layout))
+  checkVkResult vkCreatePipelineLayout(device, addr(pipelineLayoutInfo), nil,
+      addr(result.layout))
 
   var stages: seq[VkPipelineShaderStageCreateInfo]
   for shader in result.shaders:
@@ -398,7 +423,8 @@
     addr(result.pipeline)
   )
 
-proc setupFramebuffers(device: VkDevice, swapchain: var Swapchain, renderPass: VkRenderPass, dimension: TVec2[uint32]): seq[VkFramebuffer] =
+proc setupFramebuffers(device: VkDevice, swapchain: var Swapchain,
+    renderPass: VkRenderPass, dimension: TVec2[uint32]): seq[VkFramebuffer] =
   result = newSeq[VkFramebuffer](swapchain.images.len)
   for i, imageview in enumerate(swapchain.imageviews):
     var framebufferInfo = VkFramebufferCreateInfo(
@@ -410,9 +436,11 @@
       height: dimension[1],
       layers: 1,
     )
-    checkVkResult device.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(result[i]))
-  
-proc trash(device: VkDevice, swapchain: Swapchain, framebuffers: seq[VkFramebuffer]) =
+    checkVkResult device.vkCreateFramebuffer(addr(framebufferInfo), nil, addr(
+        result[i]))
+
+proc trash(device: VkDevice, swapchain: Swapchain, framebuffers: seq[
+    VkFramebuffer]) =
   for framebuffer in framebuffers:
     device.vkDestroyFramebuffer(framebuffer, nil)
   for imageview in swapchain.imageviews:
@@ -440,7 +468,8 @@
   )
 
 
-proc setupCommandBuffers(device: VkDevice, graphicsQueueFamily: uint32): (VkCommandPool, array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer]) =
+proc setupCommandBuffers(device: VkDevice, graphicsQueueFamily: uint32): (
+    VkCommandPool, array[MAX_FRAMES_IN_FLIGHT, VkCommandBuffer]) =
   # set up command buffer
   var poolInfo = VkCommandPoolCreateInfo(
     sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
@@ -462,14 +491,17 @@
     array[MAX_FRAMES_IN_FLIGHT, VkSemaphore],
     array[MAX_FRAMES_IN_FLIGHT, VkFence],
 ) =
-  var semaphoreInfo = VkSemaphoreCreateInfo(sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO)
+  var semaphoreInfo = VkSemaphoreCreateInfo(
+      sType: VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO)
   var fenceInfo = VkFenceCreateInfo(
     sType: VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
     flags: VkFenceCreateFlags(VK_FENCE_CREATE_SIGNALED_BIT)
   )
   for i in 0 ..< MAX_FRAMES_IN_FLIGHT:
-    checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result[0][i]))
-    checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(result[1][i]))
+    checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(
+        result[0][i]))
+    checkVkResult device.vkCreateSemaphore(addr(semaphoreInfo), nil, addr(
+        result[1][i]))
     checkVkResult device.vkCreateFence(addr(fenceInfo), nil, addr(result[2][i]))
 
 proc igniteEngine*(windowTitle: string): Engine =
@@ -489,11 +521,13 @@
   when DEBUG_LOG:
     result.vulkan.debugMessenger = result.vulkan.instance.setupDebugLog()
   result.vulkan.surface = result.vulkan.instance.createVulkanSurface(result.window)
-  result.vulkan.device = result.vulkan.instance.setupVulkanDeviceAndQueues(result.vulkan.surface)
+  result.vulkan.device = result.vulkan.instance.setupVulkanDeviceAndQueues(
+      result.vulkan.surface)
 
   # get basic frame information
   result.vulkan.surfaceFormat = result.vulkan.device.physicalDevice.formats.getSuitableSurfaceFormat()
-  result.vulkan.frameSize = result.window.getFrameDimension(result.vulkan.device.physicalDevice.device, result.vulkan.surface)
+  result.vulkan.frameSize = result.window.getFrameDimension(
+      result.vulkan.device.physicalDevice.device, result.vulkan.surface)
 
   # setup swapchain and render pipeline
   result.vulkan.swapchain = result.vulkan.device.device.setupSwapChain(
@@ -502,7 +536,8 @@
     result.vulkan.frameSize,
     result.vulkan.surfaceFormat
   )
-  result.vulkan.renderPass = result.vulkan.device.device.setupRenderPass(result.vulkan.surfaceFormat.format)
+  result.vulkan.renderPass = result.vulkan.device.device.setupRenderPass(
+      result.vulkan.surfaceFormat.format)
   result.vulkan.framebuffers = result.vulkan.device.device.setupFramebuffers(
     result.vulkan.swapchain,
     result.vulkan.renderPass,
@@ -511,7 +546,8 @@
   (
     result.vulkan.device.commandPool,
     result.vulkan.device.commandBuffers,
-  ) = result.vulkan.device.device.setupCommandBuffers(result.vulkan.device.graphicsQueueFamily)
+  ) = result.vulkan.device.device.setupCommandBuffers(
+      result.vulkan.device.graphicsQueueFamily)
 
   (
     result.vulkan.imageAvailableSemaphores,
@@ -520,7 +556,9 @@
   ) = result.vulkan.device.device.setupSyncPrimitives()
 
 
-proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32](engine: var Engine, scenedata: Thing, vertexShader, fragmentShader: static string): RenderPipeline[VertexType, UniformType] =
+proc setupPipeline*[VertexType; UniformType; IndexType: uint16|uint32](
+  engine: var Engine, scenedata: Thing, vertexShader,
+    fragmentShader: static string): RenderPipeline[VertexType, UniformType] =
   engine.currentscenedata = scenedata
   result = initRenderPipeline[VertexType, UniformType](
     engine.vulkan.device.device,
@@ -532,16 +570,22 @@
 
   # vertex buffers
   for mesh in allPartsOfType[Mesh[VertexType]](engine.currentscenedata):
-    result.vertexBuffers.add createVertexBuffers(mesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.device.commandPool, engine.vulkan.device.graphicsQueue)
+    result.vertexBuffers.add createVertexBuffers(mesh, result.device,
+        engine.vulkan.device.physicalDevice.device,
+        engine.vulkan.device.commandPool, engine.vulkan.device.graphicsQueue)
 
   # vertex buffers with indexes
   when not (IndexType is void):
-    for mesh in allPartsOfType[IndexedMesh[VertexType, IndexType]](engine.currentscenedata):
-      result.indexedVertexBuffers.add createIndexedVertexBuffers(mesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.device.commandPool, engine.vulkan.device.graphicsQueue)
+    for mesh in allPartsOfType[IndexedMesh[VertexType, IndexType]](
+        engine.currentscenedata):
+      result.indexedVertexBuffers.add createIndexedVertexBuffers(mesh,
+          result.device, engine.vulkan.device.physicalDevice.device,
+          engine.vulkan.device.commandPool, engine.vulkan.device.graphicsQueue)
 
   # uniform buffers
   when not (UniformType is void):
-    result.uniformBuffers = createUniformBuffers[MAX_FRAMES_IN_FLIGHT, UniformType](
+    result.uniformBuffers = createUniformBuffers[MAX_FRAMES_IN_FLIGHT,
+        UniformType](
       result.device,
       engine.vulkan.device.physicalDevice.device
     )
@@ -557,7 +601,8 @@
       pPoolSizes: addr(poolSize),
       maxSets: uint32(MAX_FRAMES_IN_FLIGHT),
     )
-  checkVkResult vkCreateDescriptorPool(result.device, addr(poolInfo), nil, addr(result.descriptorPool))
+  checkVkResult vkCreateDescriptorPool(result.device, addr(poolInfo), nil, addr(
+      result.descriptorPool))
 
   var layouts: array[MAX_FRAMES_IN_FLIGHT, VkDescriptorSetLayout]
   for i in 0 ..< MAX_FRAMES_IN_FLIGHT:
@@ -569,10 +614,12 @@
     pSetLayouts: addr(layouts[0]),
   )
 
-  checkVkResult vkAllocateDescriptorSets(result.device, addr(allocInfo), addr(result.descriptors[0]))
+  checkVkResult vkAllocateDescriptorSets(result.device, addr(allocInfo), addr(
+      result.descriptors[0]))
 
   when not (UniformType is void):
-    var bufferInfos: array[MAX_FRAMES_IN_FLIGHT, array[1, VkDescriptorBufferInfo]] # because we use only one Uniform atm
+    var bufferInfos: array[MAX_FRAMES_IN_FLIGHT, array[1,
+        VkDescriptorBufferInfo]] # because we use only one Uniform atm
     for i in 0 ..< MAX_FRAMES_IN_FLIGHT:
       bufferInfos[i][0] = VkDescriptorBufferInfo(
         buffer: result.uniformBuffers[i].vkBuffer,
@@ -586,7 +633,8 @@
           dstArrayElement: 0,
           descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
           descriptorCount: 1,
-          pBufferInfo: cast[ptr ptr VkDescriptorBufferInfo](addr(bufferInfos[i][0])),
+          pBufferInfo: cast[ptr ptr VkDescriptorBufferInfo](addr(bufferInfos[i][
+              0])),
         )
       vkUpdateDescriptorSets(result.device, 1, addr(descriptorWrite), 0, nil)
 
@@ -601,25 +649,32 @@
   if not (HostVisible in buffer.memoryProperties):
     if not (TransferDst in buffer.bufferTypes):
       raise newException(Exception, "Buffer cannot be updated")
-    var stagingBuffer = device.device.InitBuffer(device.physicalDevice.device, uint64(size), {TransferSrc}, {HostVisible, HostCoherent})
+    var stagingBuffer = device.device.InitBuffer(device.physicalDevice.device,
+        uint64(size), {TransferSrc}, {HostVisible, HostCoherent})
     copyMem(stagingBuffer.data, dataptr, size)
-    transferBuffer(device.commandPool, device.graphicsQueue, stagingBuffer, buffer, uint64(size))
+    transferBuffer(device.commandPool, device.graphicsQueue, stagingBuffer,
+        buffer, uint64(size))
     stagingBuffer.trash()
   else:
     copyMem(buffer.data, dataptr, size)
 
-proc updateVertexData*[T: VertexAttribute](device: Device, vertexAttribute: var T) =
+proc updateVertexData*[T: VertexAttribute](device: Device,
+    vertexAttribute: var T) =
   device.updateBufferData(vertexAttribute.buffer, vertexAttribute.data)
 
-proc updateUniformData*[VertexType, Uniforms](device: Device, pipeline: RenderPipeline[VertexType, Uniforms], data: var Uniforms) =
+proc updateUniformData*[VertexType, Uniforms](device: Device,
+    pipeline: RenderPipeline[VertexType, Uniforms], data: var Uniforms) =
   for buffer in pipeline.uniformBuffers:
     device.updateBufferData(buffer, data)
 
 
-proc runPipeline[VertexType; Uniforms](commandBuffer: VkCommandBuffer, pipeline: var RenderPipeline[VertexType, Uniforms], currentFrame: int) =
-  vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline)
+proc runPipeline[VertexType; Uniforms](commandBuffer: VkCommandBuffer,
+    pipeline: var RenderPipeline[VertexType, Uniforms], currentFrame: int) =
+  vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+      pipeline.pipeline)
 
-  vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil)
+  vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
+      pipeline.layout, 0, 1, addr(pipeline.descriptors[currentFrame]), 0, nil)
   for (vertexBufferSet, vertexCount) in pipeline.vertexBuffers:
     var
       vertexBuffers: seq[VkBuffer]
@@ -628,10 +683,14 @@
       vertexBuffers.add buffer.vkBuffer
       offsets.add VkDeviceSize(0)
 
-    vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=uint32(vertexBuffers.len), pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0]))
-    vkCmdDraw(commandBuffer, vertexCount=vertexCount, instanceCount=1'u32, firstVertex=0'u32, firstInstance=0'u32)
+    vkCmdBindVertexBuffers(commandBuffer, firstBinding = 0'u32,
+        bindingCount = uint32(vertexBuffers.len), pBuffers = addr(vertexBuffers[
+        0]), pOffsets = addr(offsets[0]))
+    vkCmdDraw(commandBuffer, vertexCount = vertexCount, instanceCount = 1'u32,
+        firstVertex = 0'u32, firstInstance = 0'u32)
 
-  for (vertexBufferSet, indexBuffer, indicesCount, indexType) in pipeline.indexedVertexBuffers:
+  for (vertexBufferSet, indexBuffer, indicesCount, indexType) in
+    pipeline.indexedVertexBuffers:
     var
       vertexBuffers: seq[VkBuffer]
       offsets: seq[VkDeviceSize]
@@ -639,17 +698,22 @@
       vertexBuffers.add buffer.vkBuffer
       offsets.add VkDeviceSize(0)
 
-    vkCmdBindVertexBuffers(commandBuffer, firstBinding=0'u32, bindingCount=uint32(vertexBuffers.len), pBuffers=addr(vertexBuffers[0]), pOffsets=addr(offsets[0]))
+    vkCmdBindVertexBuffers(commandBuffer, firstBinding = 0'u32,
+        bindingCount = uint32(vertexBuffers.len), pBuffers = addr(vertexBuffers[
+        0]), pOffsets = addr(offsets[0]))
     vkCmdBindIndexBuffer(commandBuffer, indexBuffer.vkBuffer, VkDeviceSize(0), indexType)
     vkCmdDrawIndexed(commandBuffer, indicesCount, 1, 0, 0, 0)
 
-proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: var RenderPipeline, commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer, frameSize: TVec2[uint32], currentFrame: int) =
+proc recordCommandBuffer(renderPass: VkRenderPass, pipeline: var RenderPipeline,
+    commandBuffer: VkCommandBuffer, framebuffer: VkFramebuffer,
+    frameSize: TVec2[uint32], currentFrame: int) =
   var
     beginInfo = VkCommandBufferBeginInfo(
       sType: VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
       pInheritanceInfo: nil,
     )
-    clearColor = VkClearValue(color: VkClearColorValue(float32: [0.2'f, 0.2'f, 0.2'f, 1.0'f]))
+    clearColor = VkClearValue(color: VkClearColorValue(float32: [0.2'f, 0.2'f,
+        0.2'f, 1.0'f]))
     renderPassInfo = VkRenderPassBeginInfo(
       sType: VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
       renderPass: renderPass,
@@ -664,8 +728,8 @@
     viewport = VkViewport(
       x: 0.0,
       y: 0.0,
-      width: (float) frameSize.x,
-      height: (float) frameSize.y,
+      width: (float)frameSize.x,
+      height: (float)frameSize.y,
       minDepth: 0.0,
       maxDepth: 1.0,
     )
@@ -676,14 +740,16 @@
   checkVkResult vkBeginCommandBuffer(commandBuffer, addr(beginInfo))
   block:
     vkCmdBeginRenderPass(commandBuffer, addr(renderPassInfo), VK_SUBPASS_CONTENTS_INLINE)
-    vkCmdSetViewport(commandBuffer, firstViewport=0, viewportCount=1, addr(viewport))
-    vkCmdSetScissor(commandBuffer, firstScissor=0, scissorCount=1, addr(scissor))
+    vkCmdSetViewport(commandBuffer, firstViewport = 0, viewportCount = 1, addr(viewport))
+    vkCmdSetScissor(commandBuffer, firstScissor = 0, scissorCount = 1, addr(scissor))
     runPipeline(commandBuffer, pipeline, currentFrame)
     vkCmdEndRenderPass(commandBuffer)
   checkVkResult vkEndCommandBuffer(commandBuffer)
 
-proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int, resized: bool, pipeline: var RenderPipeline) =
-  checkVkResult vkWaitForFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64))
+proc drawFrame(window: NativeWindow, vulkan: var Vulkan, currentFrame: int,
+    resized: bool, pipeline: var RenderPipeline) =
+  checkVkResult vkWaitForFences(vulkan.device.device, 1, addr(
+      vulkan.inFlightFences[currentFrame]), VK_TRUE, high(uint64))
   var bufferImageIndex: uint32
   let nextImageResult = vkAcquireNextImageKHR(
     vulkan.device.device,
@@ -694,14 +760,19 @@
     addr(bufferImageIndex)
   )
   if nextImageResult == VK_ERROR_OUT_OF_DATE_KHR:
-    vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface)
+    vulkan.frameSize = window.getFrameDimension(
+        vulkan.device.physicalDevice.device, vulkan.surface)
     (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain()
   elif not (nextImageResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]):
-    raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " & $nextImageResult)
-  checkVkResult vkResetFences(vulkan.device.device, 1, addr(vulkan.inFlightFences[currentFrame]))
+    raise newException(Exception, "Vulkan error: vkAcquireNextImageKHR returned " &
+        $nextImageResult)
+  checkVkResult vkResetFences(vulkan.device.device, 1, addr(
+      vulkan.inFlightFences[currentFrame]))
 
-  checkVkResult vkResetCommandBuffer(vulkan.device.commandBuffers[currentFrame], VkCommandBufferResetFlags(0))
-  vulkan.renderPass.recordCommandBuffer(pipeline, vulkan.device.commandBuffers[currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameSize, currentFrame)
+  checkVkResult vkResetCommandBuffer(vulkan.device.commandBuffers[currentFrame],
+      VkCommandBufferResetFlags(0))
+  vulkan.renderPass.recordCommandBuffer(pipeline, vulkan.device.commandBuffers[
+      currentFrame], vulkan.framebuffers[bufferImageIndex], vulkan.frameSize, currentFrame)
   var
     waitSemaphores = [vulkan.imageAvailableSemaphores[currentFrame]]
     waitStages = [VkPipelineStageFlags(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT)]
@@ -716,7 +787,8 @@
       signalSemaphoreCount: 1,
       pSignalSemaphores: addr(signalSemaphores[0]),
     )
-  checkVkResult vkQueueSubmit(vulkan.device.graphicsQueue, 1, addr(submitInfo), vulkan.inFlightFences[currentFrame])
+  checkVkResult vkQueueSubmit(vulkan.device.graphicsQueue, 1, addr(submitInfo),
+      vulkan.inFlightFences[currentFrame])
 
   var presentInfo = VkPresentInfoKHR(
     sType: VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
@@ -729,12 +801,15 @@
   )
   let presentResult = vkQueuePresentKHR(vulkan.device.presentationQueue, addr(presentInfo))
 
-  if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult == VK_SUBOPTIMAL_KHR or resized:
-    vulkan.frameSize = window.getFrameDimension(vulkan.device.physicalDevice.device, vulkan.surface)
+  if presentResult == VK_ERROR_OUT_OF_DATE_KHR or presentResult ==
+      VK_SUBOPTIMAL_KHR or resized:
+    vulkan.frameSize = window.getFrameDimension(
+        vulkan.device.physicalDevice.device, vulkan.surface)
     (vulkan.swapchain, vulkan.framebuffers) = vulkan.recreateSwapchain()
 
 
-proc run*(engine: var Engine, pipeline: var RenderPipeline, globalUpdate: proc(engine: var Engine, dt: float32)) =
+proc run*(engine: var Engine, pipeline: var RenderPipeline, globalUpdate: proc(
+    engine: var Engine, dt: float32)) =
   var
     currentFrame = 0
     resized = false
@@ -807,19 +882,25 @@
 
 proc trash*(engine: var Engine) =
   checkVkResult vkDeviceWaitIdle(engine.vulkan.device.device)
-  engine.vulkan.device.device.trash(engine.vulkan.swapchain, engine.vulkan.framebuffers)
+  engine.vulkan.device.device.trash(engine.vulkan.swapchain,
+      engine.vulkan.framebuffers)
 
   for i in 0 ..< MAX_FRAMES_IN_FLIGHT:
-    engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.imageAvailableSemaphores[i], nil)
-    engine.vulkan.device.device.vkDestroySemaphore(engine.vulkan.renderFinishedSemaphores[i], nil)
+    engine.vulkan.device.device.vkDestroySemaphore(
+        engine.vulkan.imageAvailableSemaphores[i], nil)
+    engine.vulkan.device.device.vkDestroySemaphore(
+        engine.vulkan.renderFinishedSemaphores[i], nil)
     engine.vulkan.device.device.vkDestroyFence(engine.vulkan.inFlightFences[i], nil)
 
   engine.vulkan.device.device.vkDestroyRenderPass(engine.vulkan.renderPass, nil)
-  engine.vulkan.device.device.vkDestroyCommandPool(engine.vulkan.device.commandPool, nil)
+  engine.vulkan.device.device.vkDestroyCommandPool(
+      engine.vulkan.device.commandPool, nil)
 
   engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil)
   engine.vulkan.device.device.vkDestroyDevice(nil)
   when DEBUG_LOG:
-    engine.vulkan.instance.vkDestroyDebugUtilsMessengerEXT(engine.vulkan.debugMessenger, nil)
+    engine.vulkan.instance.vkDestroyDebugUtilsMessengerEXT(
+        engine.vulkan.debugMessenger, nil)
   engine.window.trash()
-  engine.vulkan.instance.vkDestroyInstance(nil) # needs to happen after window is trashed as the driver might have a hook registered for the window destruction
+  engine.vulkan.instance.vkDestroyInstance(
+      nil) # needs to happen after window is trashed as the driver might have a hook registered for the window destruction
--- a/src/semicongine/thing.nim	Sun Jan 22 22:46:53 2023 +0700
+++ b/src/semicongine/thing.nim	Tue Jan 24 10:22:38 2023 +0700
@@ -25,9 +25,6 @@
   result = new Transform
   result.mat = mat
 
-method update*(thing: Thing, dt: float32) {.base.} = discard
-method update*(part: Part, dt: float32) {.base.} = discard
-
 proc add*(thing: Thing, child: Thing) =
   child.parent = thing
   thing.children.add child
@@ -43,12 +40,13 @@
     part.thing = thing
     thing.parts.add part
 
-func newThing*(name: string=""): Thing =
+func newThing*(name: string = ""): Thing =
   result = new Thing
   result.name = name
   if result.name == "":
     result.name = &"Thing[{$(cast[ByteAddress](result))}]"
-func newThing*(name: string, firstChild: Thing, children: varargs[Thing]): Thing =
+func newThing*(name: string, firstChild: Thing, children: varargs[
+    Thing]): Thing =
   result = new Thing
   result.add firstChild
   for child in children:
@@ -73,7 +71,7 @@
       if part of Transform:
         result = Transform(part).mat * result
     currentThing = currentThing.parent
-    
+
 iterator allPartsOfType*[T: Part](root: Thing): T =
   var queue = @[root]
   while queue.len > 0:
@@ -131,3 +129,17 @@
     for child in next.children:
       queue.add child
     yield next
+
+method update*(thing: Thing, dt: float32) {.base.} =
+  echo "The update"
+  let transform = thing.getModelTransform()
+  for part in thing.parts:
+    echo "  The part ", part
+    for name, value in part[].fieldPairs:
+      echo "    attribute ", name
+      when typeof(value) is ModelTransformAttribute:
+        value.data = @[transform.transposed()]
+        echo "updated stuff"
+
+method update*(part: Part, dt: float32) {.base.} = discard
+
--- a/src/semicongine/vertex.nim	Sun Jan 22 22:46:53 2023 +0700
+++ b/src/semicongine/vertex.nim	Tue Jan 24 10:22:38 2023 +0700
@@ -14,19 +14,19 @@
   VertexAttributeType = SomeNumber|TVec|TMat
   # useOnDeviceMemory can be used to make sure the attribute buffer memory will
   # be on the device. Data will be faster to access but much slower to update
-  GenericAttribute*[T:VertexAttributeType] = object
+  GenericAttribute*[T: VertexAttributeType] = object
     data*: seq[T]
     buffer*: Buffer
     useOnDeviceMemory*: bool
-  PositionAttribute*[T:TVec] = object
+  PositionAttribute*[T: TVec] = object
     data*: seq[T]
     buffer*: Buffer
     useOnDeviceMemory*: bool
-  ColorAttribute*[T:TVec] = object
+  ColorAttribute*[T: TVec] = object
     data*: seq[T]
     buffer*: Buffer
     useOnDeviceMemory*: bool
-  GenericInstanceAttribute*[T:VertexAttributeType] = object
+  GenericInstanceAttribute*[T: VertexAttributeType] = object
     data*: seq[T]
     buffer*: Buffer
     useOnDeviceMemory*: bool
@@ -41,7 +41,8 @@
 
 # from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html
 func nLocationSlots[T: VertexAttributeType](): int =
-  when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or T is TVec4[float64]):
+  when (T is TVec3[float64] or T is TVec3[uint64] or T is TVec4[float64] or
+      T is TVec4[float64]):
     2
   elif T is SomeNumber or T is TVec:
     1
@@ -50,64 +51,83 @@
 
 # numbers
 func getVkFormat[T: VertexAttributeType](): VkFormat =
-  when T is uint8:         VK_FORMAT_R8_UINT
-  elif T is int8:          VK_FORMAT_R8_SINT
-  elif T is uint16:        VK_FORMAT_R16_UINT
-  elif T is int16:         VK_FORMAT_R16_SINT
-  elif T is uint32:        VK_FORMAT_R32_UINT
-  elif T is int32:         VK_FORMAT_R32_SINT
-  elif T is uint64:        VK_FORMAT_R64_UINT
-  elif T is int64:         VK_FORMAT_R64_SINT
-  elif T is float32:       VK_FORMAT_R32_SFLOAT
-  elif T is float64:       VK_FORMAT_R64_SFLOAT
-  elif T is TVec2[uint8]:   VK_FORMAT_R8G8_UINT
-  elif T is TVec2[int8]:    VK_FORMAT_R8G8_SINT
-  elif T is TVec2[uint16]:  VK_FORMAT_R16G16_UINT
-  elif T is TVec2[int16]:   VK_FORMAT_R16G16_SINT
-  elif T is TVec2[uint32]:  VK_FORMAT_R32G32_UINT
-  elif T is TVec2[int32]:   VK_FORMAT_R32G32_SINT
-  elif T is TVec2[uint64]:  VK_FORMAT_R64G64_UINT
-  elif T is TVec2[int64]:   VK_FORMAT_R64G64_SINT
+  when T is uint8: VK_FORMAT_R8_UINT
+  elif T is int8: VK_FORMAT_R8_SINT
+  elif T is uint16: VK_FORMAT_R16_UINT
+  elif T is int16: VK_FORMAT_R16_SINT
+  elif T is uint32: VK_FORMAT_R32_UINT
+  elif T is int32: VK_FORMAT_R32_SINT
+  elif T is uint64: VK_FORMAT_R64_UINT
+  elif T is int64: VK_FORMAT_R64_SINT
+  elif T is float32: VK_FORMAT_R32_SFLOAT
+  elif T is float64: VK_FORMAT_R64_SFLOAT
+  elif T is TVec2[uint8]: VK_FORMAT_R8G8_UINT
+  elif T is TVec2[int8]: VK_FORMAT_R8G8_SINT
+  elif T is TVec2[uint16]: VK_FORMAT_R16G16_UINT
+  elif T is TVec2[int16]: VK_FORMAT_R16G16_SINT
+  elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT
+  elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT
+  elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT
+  elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT
   elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT
   elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT
-  elif T is TVec3[uint8]:   VK_FORMAT_R8G8B8_UINT
-  elif T is TVec3[int8]:    VK_FORMAT_R8G8B8_SINT
-  elif T is TVec3[uint16]:  VK_FORMAT_R16G16B16_UINT
-  elif T is TVec3[int16]:   VK_FORMAT_R16G16B16_SINT
-  elif T is TVec3[uint32]:  VK_FORMAT_R32G32B32_UINT
-  elif T is TVec3[int32]:   VK_FORMAT_R32G32B32_SINT
-  elif T is TVec3[uint64]:  VK_FORMAT_R64G64B64_UINT
-  elif T is TVec3[int64]:   VK_FORMAT_R64G64B64_SINT
+  elif T is TVec3[uint8]: VK_FORMAT_R8G8B8_UINT
+  elif T is TVec3[int8]: VK_FORMAT_R8G8B8_SINT
+  elif T is TVec3[uint16]: VK_FORMAT_R16G16B16_UINT
+  elif T is TVec3[int16]: VK_FORMAT_R16G16B16_SINT
+  elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT
+  elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT
+  elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT
+  elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT
   elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT
   elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT
-  elif T is TVec4[uint8]:   VK_FORMAT_R8G8B8A8_UINT
-  elif T is TVec4[int8]:    VK_FORMAT_R8G8B8A8_SINT
-  elif T is TVec4[uint16]:  VK_FORMAT_R16G16B16A16_UINT
-  elif T is TVec4[int16]:   VK_FORMAT_R16G16B16A16_SINT
-  elif T is TVec4[uint32]:  VK_FORMAT_R32G32B32A32_UINT
-  elif T is TVec4[int32]:   VK_FORMAT_R32G32B32A32_SINT
-  elif T is TVec4[uint64]:  VK_FORMAT_R64G64B64A64_UINT
-  elif T is TVec4[int64]:   VK_FORMAT_R64G64B64A64_SINT
+  elif T is TVec4[uint8]: VK_FORMAT_R8G8B8A8_UINT
+  elif T is TVec4[int8]: VK_FORMAT_R8G8B8A8_SINT
+  elif T is TVec4[uint16]: VK_FORMAT_R16G16B16A16_UINT
+  elif T is TVec4[int16]: VK_FORMAT_R16G16B16A16_SINT
+  elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT
+  elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT
+  elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT
+  elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT
   elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT
   elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT
   else: {.error: "Unsupported vertex attribute type".}
 
 
-
 func VertexCount*[T](t: T): uint32 =
   for name, value in t.fieldPairs:
-    when typeof(value) is VertexAttribute and not (typeof(value) is InstanceAttribute):
+    when typeof(value) is VertexAttribute and not (typeof(
+        value) is InstanceAttribute):
       if result == 0:
         result = uint32(value.data.len)
       else:
         assert result == uint32(value.data.len)
 
+
 func hasAttributeType*[T, AT](t: T): uint32 =
   for name, value in t.fieldPairs:
     when typeof(value) is AT:
       return true
   return false
 
+# helper function to make sure matrices are correctly
+# divided into multiple vector attributes
+template compositeAttributeType[T]() =
+  when T is TMat33[float32]:
+    Vec3
+  elif T is TMat44[float32]:
+    Vec4
+  else:
+    T
+func compositeAttributesNumber[T](): int =
+  when T is TMat33[float32]:
+    3
+  elif T is TMat44[float32]:
+    4
+  else:
+    1
+
+
 func generateGLSLVertexDeclarations*[T](): string =
   var stmtList: seq[string]
   var i = 0
@@ -116,46 +136,51 @@
       let glsltype = getGLSLType[getAttributeType(value)]()
       let n = name
       stmtList.add(&"layout(location = {i}) in {glsltype} {n};")
-      i += nLocationSlots[getAttributeType(value)]()
+      for j in 0 ..< compositeAttributesNumber[getAttributeType(value)]():
+        i += nLocationSlots[compositeAttributeType(getAttributeType(value))]()
 
   return stmtList.join("\n")
 
-func generateInputVertexBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputBindingDescription] =
+
+func generateInputVertexBinding*[T](bindingoffset: int = 0,
+    locationoffset: int = 0): seq[VkVertexInputBindingDescription] =
   # packed attribute data, not interleaved (aks "struct of arrays")
   var binding = bindingoffset
   for name, value in T().fieldPairs:
     when typeof(value) is InstanceAttribute:
-      result.add(
-        VkVertexInputBindingDescription(
-          binding: uint32(binding),
-          stride: uint32(sizeof(getAttributeType(value))),
-          inputRate: VK_VERTEX_INPUT_RATE_INSTANCE,
-        )
-      )
-      binding += 1
+      let inputRate = VK_VERTEX_INPUT_RATE_INSTANCE
     elif typeof(value) is VertexAttribute:
-      result.add(
-        VkVertexInputBindingDescription(
-          binding: uint32(binding),
-          stride: uint32(sizeof(getAttributeType(value))),
-          inputRate: VK_VERTEX_INPUT_RATE_VERTEX,
-        )
+      let inputRate = VK_VERTEX_INPUT_RATE_VERTEX
+    else:
+      {.error: "Unsupported vertex attribute type".}
+    result.add(
+      VkVertexInputBindingDescription(
+        binding: uint32(binding),
+        stride: uint32(sizeof(getAttributeType(value))),
+        inputRate: inputRate,
       )
-      binding += 1
+    )
+    binding += 1
+
 
-func generateInputAttributeBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputAttributeDescription] =
-  # packed attribute data, not interleaved (aks "struct of arrays")
+func generateInputAttributeBinding*[T](bindingoffset: int = 0,
+    locationoffset: int = 0): seq[VkVertexInputAttributeDescription] =
+  # TODO:
   var location = 0
   var binding = bindingoffset
   for name, value in T().fieldPairs:
     when typeof(value) is VertexAttribute:
-      result.add(
-        VkVertexInputAttributeDescription(
-          binding: uint32(binding),
-          location: uint32(location),
-          format: getVkFormat[getAttributeType(value)](),
-          offset: 0,
+      for i in 0 ..< compositeAttributesNumber[getAttributeType(value)]():
+        result.add(
+          VkVertexInputAttributeDescription(
+            binding: uint32(binding),
+            location: uint32(location),
+            format: getVkFormat[compositeAttributeType(getAttributeType(
+                value))](),
+            offset: uint32(i * sizeof(compositeAttributeType(getAttributeType(
+                value)))),
+          )
         )
-      )
-      location += nLocationSlots[getAttributeType(value)]()
+        location += nLocationSlots[compositeAttributeType(getAttributeType(
+            value))]()
       binding += 1