changeset 584:9e7bcc8e0e52

did: refactor mesh code, prepare for instance-data
author Sam <sam@basx.dev>
date Mon, 10 Apr 2023 00:53:16 +0700
parents 90537a8887ae
children 2df00e84757d
files src/semicongine/gpu_data.nim src/semicongine/mesh.nim src/semicongine/scene.nim tests/test_vulkan_wrapper.nim
diffstat 4 files changed, 343 insertions(+), 128 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/gpu_data.nim	Sun Apr 09 01:04:54 2023 +0700
+++ b/src/semicongine/gpu_data.nim	Mon Apr 10 00:53:16 2023 +0700
@@ -6,9 +6,7 @@
 import ./math
 
 type
-  GPUType = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64]
-
-type
+  GPUType* = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64]
   DataType* = enum
     Float32
     Float64
@@ -96,6 +94,51 @@
     of Mat43F64: mat43f64: TMat43[float64]
     of Mat4F32: mat4f32: TMat4[float32]
     of Mat4F64: mat4f64: TMat4[float64]
+  DataList* = object
+    len*: uint32
+    case thetype*: DataType
+    of Float32: float32: seq[float32]
+    of Float64: float64: seq[float64]
+    of Int8: int8: seq[int8]
+    of Int16: int16: seq[int16]
+    of Int32: int32: seq[int32]
+    of Int64: int64: seq[int64]
+    of UInt8: uint8: seq[uint8]
+    of UInt16: uint16: seq[uint16]
+    of UInt32: uint32: seq[uint32]
+    of UInt64: uint64: seq[uint64]
+    of Vec2I32: vec2i32: seq[TVec2[int32]]
+    of Vec2I64: vec2i64: seq[TVec2[int64]]
+    of Vec3I32: vec3i32: seq[TVec3[int32]]
+    of Vec3I64: vec3i64: seq[TVec3[int64]]
+    of Vec4I32: vec4i32: seq[TVec4[int32]]
+    of Vec4I64: vec4i64: seq[TVec4[int64]]
+    of Vec2U32: vec2u32: seq[TVec2[uint32]]
+    of Vec2U64: vec2u64: seq[TVec2[uint64]]
+    of Vec3U32: vec3u32: seq[TVec3[uint32]]
+    of Vec3U64: vec3u64: seq[TVec3[uint64]]
+    of Vec4U32: vec4u32: seq[TVec4[uint32]]
+    of Vec4U64: vec4u64: seq[TVec4[uint64]]
+    of Vec2F32: vec2f32: seq[TVec2[float32]]
+    of Vec2F64: vec2f64: seq[TVec2[float64]]
+    of Vec3F32: vec3f32: seq[TVec3[float32]]
+    of Vec3F64: vec3f64: seq[TVec3[float64]]
+    of Vec4F32: vec4f32: seq[TVec4[float32]]
+    of Vec4F64: vec4f64: seq[TVec4[float64]]
+    of Mat2F32: mat2f32: seq[TMat2[float32]]
+    of Mat2F64: mat2f64: seq[TMat2[float64]]
+    of Mat23F32: mat23f32: seq[TMat23[float32]]
+    of Mat23F64: mat23f64: seq[TMat23[float64]]
+    of Mat32F32: mat32f32: seq[TMat32[float32]]
+    of Mat32F64: mat32f64: seq[TMat32[float64]]
+    of Mat3F32: mat3f32: seq[TMat3[float32]]
+    of Mat3F64: mat3f64: seq[TMat3[float64]]
+    of Mat34F32: mat34f32: seq[TMat34[float32]]
+    of Mat34F64: mat34f64: seq[TMat34[float64]]
+    of Mat43F32: mat43f32: seq[TMat43[float32]]
+    of Mat43F64: mat43f64: seq[TMat43[float64]]
+    of Mat4F32: mat4f32: seq[TMat4[float32]]
+    of Mat4F64: mat4f64: seq[TMat4[float64]]
   MemoryLocation* = enum
     VRAM, VRAMVisible, RAM # VRAM is fastest, VRAMVisible allows updating memory directly, may be slower
   ShaderAttribute* = object
@@ -166,7 +209,7 @@
     of Mat43F64: 92
     of Mat4F32: 64
     of Mat4F64: 128
-  
+
 func size*(attribute: ShaderAttribute, perDescriptor=false): uint32 =
   if perDescriptor: attribute.thetype.size div attribute.thetype.numberOfVertexInputAttributeDescriptors
   else:      attribute.thetype.size
@@ -175,6 +218,12 @@
   for attribute in thetype:
     result += attribute.size
 
+func size*(value: DataValue): uint32 =
+  value.thetype.size
+
+func size*(value: DataList): uint32 =
+  value.thetype.size * value.len
+
 func getDataType*[T: GPUType|int|uint|float](): DataType =
   when T is float32: Float32
   elif T is float64: Float64
@@ -290,7 +339,57 @@
   elif T is TMat4[float32]: value.mat4f32
   elif T is TMat4[float64]: value.mat4f64
 
-proc getRawData*(value: var DataValue): (pointer, uint64) =
+func get*[T: GPUType|int|uint|float](value: DataList): seq[T] =
+  when T is float32: value.float32
+  elif T is float64: value.float64
+  elif T is int8: value.int8
+  elif T is int16: value.int16
+  elif T is int32: value.int32
+  elif T is int64: value.int64
+  elif T is uint8: value.uint8
+  elif T is uint16: value.uint16
+  elif T is uint32: value.uint32
+  elif T is uint64: value.uint64
+  elif T is int and sizeof(int) == sizeof(int32): value.int32
+  elif T is int and sizeof(int) == sizeof(int64): value.int64
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64
+  elif T is float and sizeof(float) == sizeof(float32): value.float32
+  elif T is float and sizeof(float) == sizeof(float64): value.float64
+  elif T is TVec2[int32]: value.vec2i32
+  elif T is TVec2[int64]: value.vec2i64
+  elif T is TVec3[int32]: value.vec3i32
+  elif T is TVec3[int64]: value.vec3i64
+  elif T is TVec4[int32]: value.vec4i32
+  elif T is TVec4[int64]: value.vec4i64
+  elif T is TVec2[uint32]: value.vec2u32
+  elif T is TVec2[uint64]: value.vec2u64
+  elif T is TVec3[uint32]: value.vec3u32
+  elif T is TVec3[uint64]: value.vec3u64
+  elif T is TVec4[uint32]: value.vec4u32
+  elif T is TVec4[uint64]: value.vec4u64
+  elif T is TVec2[float32]: value.vec2f32
+  elif T is TVec2[float64]: value.vec2f64
+  elif T is TVec3[float32]: value.vec3f32
+  elif T is TVec3[float64]: value.vec3f64
+  elif T is TVec4[float32]: value.vec4f32
+  elif T is TVec4[float64]: value.vec4f64
+  elif T is TMat2[float32]: value.mat2f32
+  elif T is TMat2[float64]: value.mat2f64
+  elif T is TMat23[float32]: value.mat23f
+  elif T is TMat23[float64]: value.mat23f64
+  elif T is TMat32[float32]: value.mat32f32
+  elif T is TMat32[float64]: value.mat32f64
+  elif T is TMat3[float32]: value.mat3f32
+  elif T is TMat3[float64]: value.mat3f64
+  elif T is TMat34[float32]: value.mat34f32
+  elif T is TMat34[float64]: value.mat34f64
+  elif T is TMat43[float32]: value.mat43f32
+  elif T is TMat43[float64]: value.mat43f64
+  elif T is TMat4[float32]: value.mat4f32
+  elif T is TMat4[float64]: value.mat4f64
+
+func getRawData*(value: var DataValue): (pointer, uint32) =
   result[1] = value.thetype.size
   case value.thetype
     of Float32: result[0] = addr value.float32
@@ -336,6 +435,52 @@
     of Mat4F32: result[0] = addr value.mat4f32
     of Mat4F64: result[0] = addr value.mat4f64
 
+func getRawData*(value: var DataList): (pointer, uint32) =
+  result[1] = value.thetype.size * value.len
+  case value.thetype
+    of Float32: result[0] = addr value.float32[0]
+    of Float64: result[0] = addr value.float64[0]
+    of Int8: result[0] = addr value.int8[0]
+    of Int16: result[0] = addr value.int16[0]
+    of Int32: result[0] = addr value.int32[0]
+    of Int64: result[0] = addr value.int64[0]
+    of UInt8: result[0] = addr value.uint8[0]
+    of UInt16: result[0] = addr value.uint16[0]
+    of UInt32: result[0] = addr value.uint32[0]
+    of UInt64: result[0] = addr value.uint64[0]
+    of Vec2I32: result[0] = addr value.vec2i32[0]
+    of Vec2I64: result[0] = addr value.vec2i64[0]
+    of Vec3I32: result[0] = addr value.vec3i32[0]
+    of Vec3I64: result[0] = addr value.vec3i64[0]
+    of Vec4I32: result[0] = addr value.vec4i32[0]
+    of Vec4I64: result[0] = addr value.vec4i64[0]
+    of Vec2U32: result[0] = addr value.vec2u32[0]
+    of Vec2U64: result[0] = addr value.vec2u64[0]
+    of Vec3U32: result[0] = addr value.vec3u32[0]
+    of Vec3U64: result[0] = addr value.vec3u64[0]
+    of Vec4U32: result[0] = addr value.vec4u32[0]
+    of Vec4U64: result[0] = addr value.vec4u64[0]
+    of Vec2F32: result[0] = addr value.vec2f32[0]
+    of Vec2F64: result[0] = addr value.vec2f64[0]
+    of Vec3F32: result[0] = addr value.vec3f32[0]
+    of Vec3F64: result[0] = addr value.vec3f64[0]
+    of Vec4F32: result[0] = addr value.vec4f32[0]
+    of Vec4F64: result[0] = addr value.vec4f64[0]
+    of Mat2F32: result[0] = addr value.mat2f32[0]
+    of Mat2F64: result[0] = addr value.mat2f64[0]
+    of Mat23F32: result[0] = addr value.mat23f32[0]
+    of Mat23F64: result[0] = addr value.mat23f64[0]
+    of Mat32F32: result[0] = addr value.mat32f32[0]
+    of Mat32F64: result[0] = addr value.mat32f64[0]
+    of Mat3F32: result[0] = addr value.mat3f32[0]
+    of Mat3F64: result[0] = addr value.mat3f64[0]
+    of Mat34F32: result[0] = addr value.mat34f32[0]
+    of Mat34F64: result[0] = addr value.mat34f64[0]
+    of Mat43F32: result[0] = addr value.mat43f32[0]
+    of Mat43F64: result[0] = addr value.mat43f64[0]
+    of Mat4F32: result[0] = addr value.mat4f32[0]
+    of Mat4F64: result[0] = addr value.mat4f64[0]
+
 func setValue*[T: GPUType|int|uint|float](value: var DataValue, data: T) =
   when T is float32: value.float32 = data
   elif T is float64: value.float64 = data
@@ -386,6 +531,107 @@
   elif T is TMat4[float32]: value.mat4f32 = data
   elif T is TMat4[float64]: value.mat4f64 = data
 
+func setValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
+  value.len = uint32(data.len)
+  when T is float32: value.float32 = data
+  elif T is float64: value.float64 = data
+  elif T is int8: value.int8 = data
+  elif T is int16: value.int16 = data
+  elif T is int32: value.int32 = data
+  elif T is int64: value.int64 = data
+  elif T is uint8: value.uint8 = data
+  elif T is uint16: value.uint16 = data
+  elif T is uint32: value.uint32 = data
+  elif T is uint64: value.uint64 = data
+  elif T is int and sizeof(int) == sizeof(int32): value.int32 = data
+  elif T is int and sizeof(int) == sizeof(int64): value.int64 = data
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32 = data
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64 = data
+  elif T is float and sizeof(float) == sizeof(float32): value.float32 = data
+  elif T is float and sizeof(float) == sizeof(float64): value.float64 = data
+  elif T is TVec2[int32]: value.vec2i32 = data
+  elif T is TVec2[int64]: value.vec2i64 = data
+  elif T is TVec3[int32]: value.vec3i32 = data
+  elif T is TVec3[int64]: value.vec3i64 = data
+  elif T is TVec4[int32]: value.vec4i32 = data
+  elif T is TVec4[int64]: value.vec4i64 = data
+  elif T is TVec2[uint32]: value.vec2u32 = data
+  elif T is TVec2[uint64]: value.vec2u64 = data
+  elif T is TVec3[uint32]: value.vec3u32 = data
+  elif T is TVec3[uint64]: value.vec3u64 = data
+  elif T is TVec4[uint32]: value.vec4u32 = data
+  elif T is TVec4[uint64]: value.vec4u64 = data
+  elif T is TVec2[float32]: value.vec2f32 = data
+  elif T is TVec2[float64]: value.vec2f64 = data
+  elif T is TVec3[float32]: value.vec3f32 = data
+  elif T is TVec3[float64]: value.vec3f64 = data
+  elif T is TVec4[float32]: value.vec4f32 = data
+  elif T is TVec4[float64]: value.vec4f64 = data
+  elif T is TMat2[float32]: value.mat2f32 = data
+  elif T is TMat2[float64]: value.mat2f64 = data
+  elif T is TMat23[float32]: value.mat23f32 = data
+  elif T is TMat23[float64]: value.mat23f64 = data
+  elif T is TMat32[float32]: value.mat32f32 = data
+  elif T is TMat32[float64]: value.mat32f64 = data
+  elif T is TMat3[float32]: value.mat3f32 = data
+  elif T is TMat3[float64]: value.mat3f64 = data
+  elif T is TMat34[float32]: value.mat34f32 = data
+  elif T is TMat34[float64]: value.mat34f64 = data
+  elif T is TMat43[float32]: value.mat43f32 = data
+  elif T is TMat43[float64]: value.mat43f64 = data
+  elif T is TMat4[float32]: value.mat4f32 = data
+  elif T is TMat4[float64]: value.mat4f64 = data
+func setValue*[T: GPUType|int|uint|float](value: var DataList, i: uint32, data: T) =
+  assert i < value.len
+  when T is float32: value.float32[i] = data
+  elif T is float64: value.float64[i] = data
+  elif T is int8: value.int8[i] = data
+  elif T is int16: value.int16[i] = data
+  elif T is int32: value.int32[i] = data
+  elif T is int64: value.int64[i] = data
+  elif T is uint8: value.uint8[i] = data
+  elif T is uint16: value.uint16[i] = data
+  elif T is uint32: value.uint32[i] = data
+  elif T is uint64: value.uint64[i] = data
+  elif T is int and sizeof(int) == sizeof(int32): value.int32[i] = data
+  elif T is int and sizeof(int) == sizeof(int64): value.int64[i] = data
+  elif T is uint and sizeof(uint) == sizeof(uint32): value.uint32[i] = data
+  elif T is uint and sizeof(uint) == sizeof(uint64): value.uint64[i] = data
+  elif T is float and sizeof(float) == sizeof(float32): value.float32[i] = data
+  elif T is float and sizeof(float) == sizeof(float64): value.float64[i] = data
+  elif T is TVec2[int32]: value.vec2i32[i] = data
+  elif T is TVec2[int64]: value.vec2i64[i] = data
+  elif T is TVec3[int32]: value.vec3i32[i] = data
+  elif T is TVec3[int64]: value.vec3i64[i] = data
+  elif T is TVec4[int32]: value.vec4i32[i] = data
+  elif T is TVec4[int64]: value.vec4i64[i] = data
+  elif T is TVec2[uint32]: value.vec2u32[i] = data
+  elif T is TVec2[uint64]: value.vec2u64[i] = data
+  elif T is TVec3[uint32]: value.vec3u32[i] = data
+  elif T is TVec3[uint64]: value.vec3u64[i] = data
+  elif T is TVec4[uint32]: value.vec4u32[i] = data
+  elif T is TVec4[uint64]: value.vec4u64[i] = data
+  elif T is TVec2[float32]: value.vec2f32[i] = data
+  elif T is TVec2[float64]: value.vec2f64[i] = data
+  elif T is TVec3[float32]: value.vec3f32[i] = data
+  elif T is TVec3[float64]: value.vec3f64[i] = data
+  elif T is TVec4[float32]: value.vec4f32[i] = data
+  elif T is TVec4[float64]: value.vec4f64[i] = data
+  elif T is TMat2[float32]: value.mat2f32[i] = data
+  elif T is TMat2[float64]: value.mat2f64[i] = data
+  elif T is TMat23[float32]: value.mat23f32[i] = data
+  elif T is TMat23[float64]: value.mat23f64[i] = data
+  elif T is TMat32[float32]: value.mat32f32[i] = data
+  elif T is TMat32[float64]: value.mat32f64[i] = data
+  elif T is TMat3[float32]: value.mat3f32[i] = data
+  elif T is TMat3[float64]: value.mat3f64[i] = data
+  elif T is TMat34[float32]: value.mat34f32[i] = data
+  elif T is TMat34[float64]: value.mat34f64[i] = data
+  elif T is TMat43[float32]: value.mat43f32[i] = data
+  elif T is TMat43[float64]: value.mat43f64[i] = data
+  elif T is TMat4[float32]: value.mat4f32[i] = data
+  elif T is TMat4[float64]: value.mat4f64[i] = data
+
 const TYPEMAP = {
     Float32: VK_FORMAT_R32_SFLOAT,
     Float64: VK_FORMAT_R64_SFLOAT,
--- a/src/semicongine/mesh.nim	Sun Apr 09 01:04:54 2023 +0700
+++ b/src/semicongine/mesh.nim	Mon Apr 10 00:53:16 2023 +0700
@@ -4,36 +4,28 @@
 import std/strformat
 import std/sequtils
 
-import ./vulkan/utils
 import ./vulkan/api
 import ./gpu_data
 import ./entity
 import ./math
 
 type
-  SurfaceDataType = enum
-    Position, Color, Normal, Tangent, BiTangent, TextureCoordinate
   MeshIndexType* = enum
     None
     Tiny # up to 2^8 vertices # TODO: need to check and enable support for this
     Small # up to 2^16 vertices
     Big # up to 2^32 vertices
-  MeshData = object
-    case thetype*: SurfaceDataType
-      of Position: position: seq[Vec3f]
-      of Color: color: seq[Vec3f]
-      of Normal: normal: seq[Vec3f]
-      of Tangent: tangent: seq[Vec3f]
-      of BiTangent: bitangent: seq[Vec3f]
-      of TextureCoordinate: texturecoord: seq[Vec2f]
   Mesh* = ref object of Component
     vertexCount*: uint32
-    data: Table[ShaderAttribute, MeshData]
+    indicesCount*: uint32
+    instanceCount*: uint32
+    vertexdata: Table[string, DataList]
+    instancedata: Table[string, DataList]
     case indexType*: MeshIndexType
       of None: discard
-      of Tiny: tinyIndices*: seq[array[3, uint8]]
-      of Small: smallIndices*: seq[array[3, uint16]]
-      of Big: bigIndices*: seq[array[3, uint32]]
+      of Tiny: tinyIndices: seq[array[3, uint8]]
+      of Small: smallIndices: seq[array[3, uint16]]
+      of Big: bigIndices: seq[array[3, uint32]]
 
 converter toVulkan*(indexType: MeshIndexType): VkIndexType =
   case indexType:
@@ -42,139 +34,96 @@
     of Small: VK_INDEX_TYPE_UINT16
     of Big: VK_INDEX_TYPE_UINT32
 
-func indicesCount*(mesh: Mesh): uint32 =
-  case mesh.indexType:
-    of None: 0
-    of Tiny: mesh.tinyIndices.len * 3
-    of Small: mesh.smallIndices.len * 3
-    of Big: mesh.bigIndices.len * 3
-
 method `$`*(mesh: Mesh): string =
   &"Mesh ({mesh.vertexCount})"
 
-func newMesh*(positions: openArray[Vec3f], colors: openArray[Vec3f]=[]): Mesh =
-  assert colors.len == 0 or colors.len == positions.len
-  result = new Mesh
-  result.vertexCount = uint32(positions.len)
-  result.indexType = None
-  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
-  if colors.len > 0:
-    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
 
-
-func newMesh*(positions: openArray[Vec3f], colors: openArray[Vec3f]=[], indices: openArray[array[3, uint32|int32|int]], autoResize=true): auto =
+func newMesh*(
+  positions: openArray[Vec3f],
+  indices: openArray[array[3, uint32|int32|uint16|int16|int]],
+  colors: openArray[Vec3f]=[],
+  instances=1'u32,
+  autoResize=true
+): auto =
   assert colors.len == 0 or colors.len == positions.len
 
   result = new Mesh
   result.vertexCount = uint32(positions.len)
-  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
+  result.indicesCount = uint32(indices.len * 3)
+  result.instanceCount = instances
+  result.vertexdata["position"] = DataList(thetype: Vec3F32)
+  setValues(result.vertexdata["position"], positions.toSeq)
   if colors.len > 0:
-    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
+    result.vertexdata["color"] = DataList(thetype: Vec3F32)
+    setValues(result.vertexdata["color"], colors.toSeq)
 
   for i in indices:
     assert uint32(i[0]) < result.vertexCount
     assert uint32(i[1]) < result.vertexCount
     assert uint32(i[2]) < result.vertexCount
 
-  if autoResize and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support
-    result.indexType = Tiny
-    for i, tri in enumerate(indices):
-      result.tinyIndices.add [uint8(tri[0]), uint8(tri[1]), uint8(tri[2])]
-  elif autoResize and uint32(positions.len) < uint32(high(uint16)):
-    result.indexType = Small
-    for i, tri in enumerate(indices):
-      result.smallIndices.add [uint16(tri[0]), uint16(tri[1]), uint16(tri[2])]
+  if indices.len == 0:
+      result.indexType = None
   else:
-    result.indexType = Big
-    for i, tri in enumerate(indices):
-      result.bigIndices.add [uint32(tri[0]), uint32(tri[1]), uint32(tri[2])]
-
-func newMesh*(positions: openArray[Vec3f], colors: openArray[Vec3f]=[], indices: openArray[array[3, uint16|int16]]): auto =
-  assert colors.len == 0 or colors.len == positions.len
-
-  result = new Mesh
-  result.vertexCount = uint32(positions.len)
-  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
-  if colors.len > 0:
-    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
-
-  for i in indices:
-    assert i[0] < result.vertexCount
-    assert i[1] < result.vertexCount
-    assert i[2] < result.vertexCount
-  result.indexType = Small
-  for i, tri in enumerate(indices):
-    result.smallIndices.add [uint16(tri[0]), uint16(tri[1]), uint16(tri[2])]
+    if autoResize and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support
+      result.indexType = Tiny
+      for i, tri in enumerate(indices):
+        result.tinyIndices.add [uint8(tri[0]), uint8(tri[1]), uint8(tri[2])]
+    elif autoResize and uint32(positions.len) < uint32(high(uint16)):
+      result.indexType = Small
+      for i, tri in enumerate(indices):
+        result.smallIndices.add [uint16(tri[0]), uint16(tri[1]), uint16(tri[2])]
+    else:
+      result.indexType = Big
+      for i, tri in enumerate(indices):
+        result.bigIndices.add [uint32(tri[0]), uint32(tri[1]), uint32(tri[2])]
 
-func newMesh*(positions: openArray[Vec3f], colors: openArray[Vec3f]=[], indices: openArray[array[3, uint8|int8]]): auto =
-  assert colors.len == 0 or colors.len == positions.len
-  assert false # TODO: check feature support
-
-  result = new Mesh
-  result.vertexCount = uint32(positions.len)
-  result.data[attr[Vec3f]("position")] = MeshData(thetype: Position, position: positions.toSeq)
-  if colors.len > 0:
-    result.data[attr[Vec3f]("color")] = MeshData(thetype: Color, color: colors.toSeq)
-
-  for i in indices:
-    assert i[0] < result.vertexCount
-    assert i[1] < result.vertexCount
-    assert i[2] < result.vertexCount
-  result.indexType = Tiny
-  for i, tri in enumerate(indices):
-    result.smallIndices.add [uint8(tri[0]), uint8(tri[1]), uint8(tri[2])]
+func newMesh*(
+  positions: openArray[Vec3f],
+  colors: openArray[Vec3f]=[],
+  instances=1'u32,
+): auto =
+  newMesh(positions, newSeq[array[3, int]](), colors, instances)
 
+func vertexDataSize*(mesh: Mesh): uint32 =
+  for d in mesh.vertexdata.values:
+    result += d.size
 
-func meshDataSize*(meshdata: MeshData): uint64 =
-  case meshdata.thetype:
-    of Position: meshdata.position.size
-    of Color: meshdata.color.size
-    of Normal: meshdata.normal.size
-    of Tangent: meshdata.tangent.size
-    of BiTangent: meshdata.bitangent.size
-    of TextureCoordinate: meshdata.texturecoord.size
-
-func attributeSize*(mesh: Mesh, attribute: ShaderAttribute): uint64 =
-  mesh.data[attribute].meshDataSize
-
-func vertexDataSize*(mesh: Mesh): uint64 =
-  for d in mesh.data.values:
-    result += d.meshDataSize
-
-func indexDataSize*(mesh: Mesh): uint64 =
+func indexDataSize*(mesh: Mesh): uint32 =
   case mesh.indexType
     of None: 0
     of Tiny: mesh.tinyIndices.len * sizeof(get(genericParams(typeof(mesh.tinyIndices)), 0))
     of Small: mesh.smallIndices.len * sizeof(get(genericParams(typeof(mesh.smallIndices)), 0))
     of Big: mesh.bigIndices.len * sizeof(get(genericParams(typeof(mesh.bigIndices)), 0))
 
-proc rawData[T: seq](value: var T): (pointer, uint64) =
-  (pointer(addr(value[0])), uint64(sizeof(get(genericParams(typeof(value)), 0)) * value.len))
+func instanceDataSize*(mesh: Mesh): uint32 =
+  for d in mesh.instancedata.values:
+    result += d.size
 
-proc getRawData(data: var MeshData): (pointer, uint64) =
-  case data.thetype:
-    of Position: rawData(data.position)
-    of Color: rawData(data.color)
-    of Normal: rawData(data.normal)
-    of Tangent: rawData(data.tangent)
-    of BiTangent: rawData(data.bitangent)
-    of TextureCoordinate: rawData(data.texturecoord)
+func rawData[T: seq](value: var T): (pointer, uint32) =
+  (pointer(addr(value[0])), uint32(sizeof(get(genericParams(typeof(value)), 0)) * value.len))
 
-proc getRawIndexData*(mesh: Mesh): (pointer, uint64) =
+func getRawIndexData*(mesh: Mesh): (pointer, uint32) =
   case mesh.indexType:
     of None: raise newException(Exception, "Trying to get index data for non-indexed mesh")
     of Tiny: rawData(mesh.tinyIndices)
     of Small: rawData(mesh.smallIndices)
     of Big: rawData(mesh.bigIndices)
 
-proc hasDataFor*(mesh: Mesh, attribute: ShaderAttribute): bool =
-  assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
-  attribute in mesh.data
+func hasVertexDataFor*(mesh: Mesh, attribute: string): bool =
+  attribute in mesh.vertexdata
+
+func hasInstanceDataFor*(mesh: Mesh, attribute: string): bool =
+  attribute in mesh.instancedata
+
+func getRawVertexData*(mesh: Mesh, attribute: string): (pointer, uint32) =
+  mesh.vertexdata[attribute].getRawData()
 
-proc getRawData*(mesh: Mesh, attribute: ShaderAttribute): (pointer, uint64) =
-  assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
-  mesh.data[attribute].getRawData()
+func getRawInstanceData*(mesh: Mesh, attribute: string): (pointer, uint32) =
+  mesh.instancedata[attribute].getRawData()
 
-proc getData*(mesh: Mesh, attribute: ShaderAttribute): MeshData =
-  assert attribute.perInstance == false, "Mesh data cannot handle per-instance attributes"
-  mesh.data[attribute]
+proc setInstanceData*[T: GPUType|int|uint|float](mesh: var Mesh, attribute: string, data: seq[T]) =
+  assert uint32(data.len) == mesh.instanceCount
+  assert not (attribute in mesh.instancedata)
+  mesh.instancedata[attribute] = DataList(thetype: getDataType[T]())
+  setValues(mesh.instancedata[attribute], data)
--- a/src/semicongine/scene.nim	Sun Apr 09 01:04:54 2023 +0700
+++ b/src/semicongine/scene.nim	Mon Apr 10 00:53:16 2023 +0700
@@ -46,7 +46,7 @@
   value.setValue(data)
   ShaderGlobal(name: name, value: value)
 
-proc getBuffers*(scene: Scene, pipeline: VkPipeline): seq[Buffer] =
+func getBuffers*(scene: Scene, pipeline: VkPipeline): seq[Buffer] =
   var counted: seq[VkBuffer]
   for drawable in scene.drawables[pipeline]:
     if not (drawable.buffer.vk in counted):
@@ -80,7 +80,7 @@
     allIndexedMeshes: seq[Mesh]
   for mesh in allComponentsOfType[Mesh](scene.root):
     for inputAttr in pipeline.inputs.vertexInputs:
-      assert mesh.hasDataFor(inputAttr), &"{mesh} missing data for {inputAttr}"
+      assert mesh.hasVertexDataFor(inputAttr.name), &"{mesh} missing data for {inputAttr}"
     case mesh.indexType:
       of None: nonIndexedMeshes.add mesh
       of Tiny: tinyIndexedMeshes.add mesh
@@ -120,6 +120,7 @@
         mappable=location in [VRAMVisible, RAM],
       )
 
+    # TODO: gather instance data/buffers
     # non-indexed mesh drawable
     if nonIndexedMeshes.len > 0:
       var vertexCount = 0'u32
@@ -127,11 +128,16 @@
         vertexCount += mesh.vertexCount
       # remark: we merge all meshes into a single drawcall... smart?#
       # I think bad for instancing...
-      var nonIndexedDrawable = Drawable(elementCount: vertexCount, buffer: buffer, indexed: false, instanceCount: 1)
+      var nonIndexedDrawable = Drawable(
+        elementCount: vertexCount,
+        buffer: buffer,
+        indexed: false,
+        instanceCount: 1
+      )
       for inputAttr in attributes:
         nonIndexedDrawable.offsets.add bufferOffset
         for mesh in nonIndexedMeshes:
-          var (pdata, size) = mesh.getRawData(inputAttr)
+          var (pdata, size) = mesh.getRawVertexData(inputAttr.name)
           buffer.setData(pdata, size, bufferOffset)
           bufferOffset += size
       scene.drawables[pipeline.vk].add nonIndexedDrawable
@@ -152,7 +158,7 @@
       indexOffset += size
       for inputAttr in attributes:
         drawable.offsets.add bufferOffset
-        var (pdata, size) = mesh.getRawData(inputAttr)
+        var (pdata, size) = mesh.getRawVertexData(inputAttr.name)
         buffer.setData(pdata, size, bufferOffset)
         bufferOffset += size
       scene.drawables[pipeline.vk].add drawable
@@ -162,5 +168,5 @@
     for pipeline in subpass.pipelines:
       scene.setupDrawables(pipeline)
 
-proc getDrawables*(scene: Scene, pipeline: Pipeline): seq[Drawable] =
+func getDrawables*(scene: Scene, pipeline: Pipeline): seq[Drawable] =
   scene.drawables.getOrDefault(pipeline.vk, @[])
--- a/tests/test_vulkan_wrapper.nim	Sun Apr 09 01:04:54 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Mon Apr 10 00:53:16 2023 +0700
@@ -57,7 +57,7 @@
     vertexInput = @[
       attr[Vec3f]("position"),
       attr[Vec3f]("color"),
-      attr[Mat4]("transform", perInstance=true)
+      attr[Vec3f]("translate", perInstance=true)
     ]
     vertexOutput = @[attr[Vec3f]("outcolor")]
     uniforms = @[attr[float32]("time")]
@@ -87,6 +87,7 @@
 
   # INIT SCENE
   var time = initShaderGlobal("time", 0.0'f32)
+  #[
   var thescene = Scene(
     name: "main",
     root: newEntity("root",
@@ -123,6 +124,19 @@
       )),
     )
   )
+  ]#
+  var mymesh = newMesh(
+    positions=[newVec3f(0.0, -0.5), newVec3f(0.5, 0.5), newVec3f(-0.5, 0.5)],
+    colors=[newVec3f(1.0, 0.0, 0.0), newVec3f(0.0, 1.0, 0.0), newVec3f(0.0, 0.0, 1.0)],
+  )
+  setInstanceData[Vec3f](mymesh, "translate", @[newVec3f(0.3, 0.3)])
+  var thescene = Scene(
+    name: "main",
+    root: newEntity("root",
+      newEntity("stuff", time),
+      newEntity("triangle", mymesh),
+    )
+  )
   thescene.setupDrawables(renderPass)
   swapchain.setupUniforms(thescene)