changeset 870:b975eab2b694

did: improve dynamic array, mesh and material APIs a ton, changes in material attributes are now detected and will trigger uniform-updates
author Sam <sam@basx.dev>
date Sun, 07 Jan 2024 00:56:44 +0700
parents 65afec4cb6c6
children 1f1e959a5fa3
files semicongine/core/dynamic_arrays.nim semicongine/material.nim semicongine/mesh.nim semicongine/renderer.nim semicongine/resources/mesh.nim semicongine/scene.nim
diffstat 6 files changed, 187 insertions(+), 207 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/core/dynamic_arrays.nim	Thu Jan 04 21:13:11 2024 +0700
+++ b/semicongine/core/dynamic_arrays.nim	Sun Jan 07 00:56:44 2024 +0700
@@ -1,4 +1,5 @@
 import std/hashes
+import std/tables
 import std/strformat
 
 import ./gpu_types
@@ -200,7 +201,7 @@
     of TextureType: discard
 
 
-proc setValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
+proc setValues[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
   value.setLen(data.len)
   when T is float32: value.float32[] = data
   elif T is float64: value.float64[] = data
@@ -253,6 +254,59 @@
   elif T is Texture: value.texture[] = data
   else: {. error: "Virtual datatype has no values" .}
 
+proc setValue[T: GPUType|int|uint|float](value: var DataList, i: int, 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
+  elif T is Texture: value.texture[i] = data
+  else: {. error: "Virtual datatype has no values" .}
+
 proc initDataList*(theType: DataType, len=0): DataList =
   result = DataList(theType: theType)
   case result.theType
@@ -418,54 +472,65 @@
 template `[]`*(list: DataList, i: int, t: typedesc): untyped =
   getValue[t](list, i)
 
-func getRawData*(value: var DataList): (pointer, int) =
+# since we use this often with tables, add this for an easy assignment
+template `[]=`*[T](table: var Table[string, DataList], key: string, values: seq[T]) =
+  if key in table:
+    table[key].setValues(values)
+  else:
+    table[key] = initDataList(values)
+
+template `[]=`*[T](list: var DataList, values: seq[T]) =
+  list.setValues(values)
+template `[]=`*[T](list: var DataList, i: int, value: T) =
+  list.setValue(i, value)
+
+func getPointer*(value: var DataList): pointer =
   if value.len == 0:
-    return (nil, 0)
-  result[1] = value.theType.size * value.len
+    result = nil
   case value.theType
-    of Float32: result[0] = value.float32[].toCPointer
-    of Float64: result[0] = value.float64[].toCPointer
-    of Int8: result[0] = value.int8[].toCPointer
-    of Int16: result[0] = value.int16[].toCPointer
-    of Int32: result[0] = value.int32[].toCPointer
-    of Int64: result[0] = value.int64[].toCPointer
-    of UInt8: result[0] = value.uint8[].toCPointer
-    of UInt16: result[0] = value.uint16[].toCPointer
-    of UInt32: result[0] = value.uint32[].toCPointer
-    of UInt64: result[0] = value.uint64[].toCPointer
-    of Vec2I32: result[0] = value.vec2i32[].toCPointer
-    of Vec2I64: result[0] = value.vec2i64[].toCPointer
-    of Vec3I32: result[0] = value.vec3i32[].toCPointer
-    of Vec3I64: result[0] = value.vec3i64[].toCPointer
-    of Vec4I32: result[0] = value.vec4i32[].toCPointer
-    of Vec4I64: result[0] = value.vec4i64[].toCPointer
-    of Vec2U32: result[0] = value.vec2u32[].toCPointer
-    of Vec2U64: result[0] = value.vec2u64[].toCPointer
-    of Vec3U32: result[0] = value.vec3u32[].toCPointer
-    of Vec3U64: result[0] = value.vec3u64[].toCPointer
-    of Vec4U32: result[0] = value.vec4u32[].toCPointer
-    of Vec4U64: result[0] = value.vec4u64[].toCPointer
-    of Vec2F32: result[0] = value.vec2f32[].toCPointer
-    of Vec2F64: result[0] = value.vec2f64[].toCPointer
-    of Vec3F32: result[0] = value.vec3f32[].toCPointer
-    of Vec3F64: result[0] = value.vec3f64[].toCPointer
-    of Vec4F32: result[0] = value.vec4f32[].toCPointer
-    of Vec4F64: result[0] = value.vec4f64[].toCPointer
-    of Mat2F32: result[0] = value.mat2f32[].toCPointer
-    of Mat2F64: result[0] = value.mat2f64[].toCPointer
-    of Mat23F32: result[0] = value.mat23f32[].toCPointer
-    of Mat23F64: result[0] = value.mat23f64[].toCPointer
-    of Mat32F32: result[0] = value.mat32f32[].toCPointer
-    of Mat32F64: result[0] = value.mat32f64[].toCPointer
-    of Mat3F32: result[0] = value.mat3f32[].toCPointer
-    of Mat3F64: result[0] = value.mat3f64[].toCPointer
-    of Mat34F32: result[0] = value.mat34f32[].toCPointer
-    of Mat34F64: result[0] = value.mat34f64[].toCPointer
-    of Mat43F32: result[0] = value.mat43f32[].toCPointer
-    of Mat43F64: result[0] = value.mat43f64[].toCPointer
-    of Mat4F32: result[0] = value.mat4f32[].toCPointer
-    of Mat4F64: result[0] = value.mat4f64[].toCPointer
-    of TextureType: result[0] = nil
+    of Float32: result = value.float32[].toCPointer
+    of Float64: result = value.float64[].toCPointer
+    of Int8: result = value.int8[].toCPointer
+    of Int16: result = value.int16[].toCPointer
+    of Int32: result = value.int32[].toCPointer
+    of Int64: result = value.int64[].toCPointer
+    of UInt8: result = value.uint8[].toCPointer
+    of UInt16: result = value.uint16[].toCPointer
+    of UInt32: result = value.uint32[].toCPointer
+    of UInt64: result = value.uint64[].toCPointer
+    of Vec2I32: result = value.vec2i32[].toCPointer
+    of Vec2I64: result = value.vec2i64[].toCPointer
+    of Vec3I32: result = value.vec3i32[].toCPointer
+    of Vec3I64: result = value.vec3i64[].toCPointer
+    of Vec4I32: result = value.vec4i32[].toCPointer
+    of Vec4I64: result = value.vec4i64[].toCPointer
+    of Vec2U32: result = value.vec2u32[].toCPointer
+    of Vec2U64: result = value.vec2u64[].toCPointer
+    of Vec3U32: result = value.vec3u32[].toCPointer
+    of Vec3U64: result = value.vec3u64[].toCPointer
+    of Vec4U32: result = value.vec4u32[].toCPointer
+    of Vec4U64: result = value.vec4u64[].toCPointer
+    of Vec2F32: result = value.vec2f32[].toCPointer
+    of Vec2F64: result = value.vec2f64[].toCPointer
+    of Vec3F32: result = value.vec3f32[].toCPointer
+    of Vec3F64: result = value.vec3f64[].toCPointer
+    of Vec4F32: result = value.vec4f32[].toCPointer
+    of Vec4F64: result = value.vec4f64[].toCPointer
+    of Mat2F32: result = value.mat2f32[].toCPointer
+    of Mat2F64: result = value.mat2f64[].toCPointer
+    of Mat23F32: result = value.mat23f32[].toCPointer
+    of Mat23F64: result = value.mat23f64[].toCPointer
+    of Mat32F32: result = value.mat32f32[].toCPointer
+    of Mat32F64: result = value.mat32f64[].toCPointer
+    of Mat3F32: result = value.mat3f32[].toCPointer
+    of Mat3F64: result = value.mat3f64[].toCPointer
+    of Mat34F32: result = value.mat34f32[].toCPointer
+    of Mat34F64: result = value.mat34f64[].toCPointer
+    of Mat43F32: result = value.mat43f32[].toCPointer
+    of Mat43F64: result = value.mat43f64[].toCPointer
+    of Mat4F32: result = value.mat4f32[].toCPointer
+    of Mat4F64: result = value.mat4f64[].toCPointer
+    of TextureType: nil
 
 proc appendValues*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
   value.len += data.len
@@ -568,112 +633,6 @@
   of Mat4F64: value.mat4f64[].add data.mat4f64[]
   of TextureType: value.texture[].add data.texture[]
 
-proc setValue*[T: GPUType|int|uint|float](value: var DataList, data: seq[T]) =
-  value.len = 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
-  elif T is Texture: value.texture[] = data
-  else: {. error: "Virtual datatype has no values" .}
-
-proc setValue*[T: GPUType|int|uint|float](value: var DataList, i: int, 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
-  elif T is Texture: value.texture[i] = data
-  else: {. error: "Virtual datatype has no values" .}
-
 proc appendFrom*(a: var DataList, i: int, b: DataList, j: int) =
   assert  a.theType == b.theType
   case a.theType
--- a/semicongine/material.nim	Thu Jan 04 21:13:11 2024 +0700
+++ b/semicongine/material.nim	Sun Jan 07 00:56:44 2024 +0700
@@ -12,15 +12,16 @@
     vertexAttributes*: Table[string, DataType]
     instanceAttributes*: Table[string, DataType]
     attributes*: Table[string, DataType]
-  MaterialData* = object
+  MaterialData* = ref object
     theType*: MaterialType
     name*: string
     attributes: Table[string, DataList]
+    dirtyAttributes: seq[string]
 
-func hasMatchingAttribute*(materialType: MaterialType, attr: ShaderAttribute): bool =
+proc hasMatchingAttribute*(materialType: MaterialType, attr: ShaderAttribute): bool =
   return materialType.attributes.contains(attr.name) and materialType.attributes[attr.name] == attr.theType
 
-func hasMatchingAttribute*(material: MaterialData, attr: ShaderAttribute): bool =
+proc hasMatchingAttribute*(material: MaterialData, attr: ShaderAttribute): bool =
   return material.attributes.contains(attr.name) and material.attributes[attr.name].theType == attr.theType
 
 proc hash*(materialType: MaterialType): Hash =
@@ -37,13 +38,30 @@
 
 template `[]`*(material: MaterialData, attributeName: string): DataList =
   material.attributes[attributeName]
-
 template `[]`*(material: MaterialData, attributeName: string, t: typedesc): ref seq[t] =
   material.attributes[attributeName][t]
-
 template `[]`*(material: MaterialData, attributeName: string, i: int, t: typedesc): untyped =
   material.attributes[attributeName][i, t]
 
+template `[]=`*(material: var MaterialData, attribute: string, newList: DataList) =
+  material.attributes[attribute] = newList
+  if not material.dirtyAttributes.contains(attribute):
+    material.dirtyAttributes.add attribute
+template `[]=`*[T](material: var MaterialData, attribute: string, newList: seq[T]) =
+  material.attributes[attribute][] = newList
+  if not material.dirtyAttributes.contains(attribute):
+    material.dirtyAttributes.add attribute
+template `[]=`*[T](material: var MaterialData, attribute: string, i: int, newValue: T) =
+  material.attributes[attribute][i] = newValue
+  if not material.dirtyAttributes.contains(attribute):
+    material.dirtyAttributes.add attribute
+
+func dirtyAttributes*(material: MaterialData): seq[string] =
+  material.dirtyAttributes
+
+proc clearDirtyAttributes*(material: var MaterialData) =
+  material.dirtyAttributes.reset
+
 let EMPTY_MATERIAL* = MaterialType(
   name: "empty material",
   vertexAttributes: {"position": Vec3F32}.toTable,
--- a/semicongine/mesh.nim	Thu Jan 04 21:13:11 2024 +0700
+++ b/semicongine/mesh.nim	Sun Jan 07 00:56:44 2024 +0700
@@ -96,7 +96,8 @@
   assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute)
   mesh.vertexData[attribute] = initDataList(thetype=getDataType[T]())
   mesh.vertexData[attribute].setLen(mesh.vertexCount)
-  mesh.vertexData[attribute].setValues(value)
+  mesh.vertexData[attribute] = value
+  # `=`(mesh.vertexData[attribute], value)
 proc initVertexAttribute*[T](mesh: var MeshObject, attribute: string, value: T) =
   initVertexAttribute(mesh, attribute, newSeqWith(mesh.vertexCount, value))
 proc initVertexAttribute*[T](mesh: var MeshObject, attribute: string) =
@@ -114,7 +115,7 @@
   assert not mesh.vertexData.contains(attribute) and not mesh.instanceData.contains(attribute)
   mesh.instanceData[attribute] = initDataList(thetype=getDataType[T]())
   mesh.instanceData[attribute].setLen(mesh.instanceCount)
-  mesh.instanceData[attribute].setValues(value)
+  mesh.instanceData[attribute] = value
 proc initInstanceAttribute*[T](mesh: var MeshObject, attribute: string, value: T) =
   initInstanceAttribute(mesh, attribute, newSeqWith(mesh.instanceCount, value))
 proc initInstanceAttribute*[T](mesh: var MeshObject, attribute: string) =
@@ -237,11 +238,11 @@
     of Small: rawData(mesh.smallIndices)
     of Big: rawData(mesh.bigIndices)
 
-func getRawData*(mesh: var MeshObject, attribute: string): (pointer, int) =
+func getPointer*(mesh: var MeshObject, attribute: string): pointer =
   if mesh.vertexData.contains(attribute):
-    mesh.vertexData[attribute].getRawData()
+    mesh.vertexData[attribute].getPointer()
   elif mesh.instanceData.contains(attribute):
-    mesh.instanceData[attribute].getRawData()
+    mesh.instanceData[attribute].getPointer()
   else:
     raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}")
 
@@ -263,12 +264,17 @@
 
 template `[]`*(mesh: MeshObject, attribute: string, t: typedesc): ref seq[t] =
   getAttribute[t](mesh, attribute)
-template `[]`*(mesh: Mesh, attribute: string, t: typedesc): ref seq[t] =
-  getAttribute[t](mesh[], attribute)
 template `[]`*(mesh: MeshObject, attribute: string, i: int, t: typedesc): untyped =
   getAttribute[t](mesh, attribute, i)
+template `[]=`*[T](mesh: MeshObject, attribute: string, value: seq[T]) =
+  getAttribute[t](mesh, attribute)
+template `[]=`*[T](mesh: MeshObject, attribute: string, i: int, value: T) =
+  getAttribute[t](mesh, attribute, i)
+
+template `[]`*(mesh: Mesh, attribute: string, t: typedesc): ref seq[t] =
+  mesh[][attribute, t]
 template `[]`*(mesh: Mesh, attribute: string, i: int, t: typedesc): untyped =
-  getAttribute[t](mesh[], attribute, i)
+  mesh[][attribute, i, t]
 
 proc updateAttributeData[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, data: DataList) =
   if mesh.vertexData.contains(attribute):
@@ -287,10 +293,10 @@
 proc updateAttributeData[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, data: seq[T]) =
   if mesh.vertexData.contains(attribute):
     assert data.len == mesh.vertexCount
-    setValues(mesh.vertexData[attribute], data)
+    mesh.vertexData[attribute] = data
   elif mesh.instanceData.contains(attribute):
     assert data.len == mesh.instanceCount
-    setValues(mesh.instanceData[attribute], data)
+    mesh.instanceData[attribute] = data
   else:
     raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}")
   if not mesh.dirtyAttributes.contains(attribute):
@@ -299,10 +305,10 @@
 proc updateAttributeData[T: GPUType|int|uint|float](mesh: var MeshObject, attribute: string, i: int, value: T) =
   if mesh.vertexData.contains(attribute):
     assert i < mesh.vertexData[attribute].len
-    setValue(mesh.vertexData[attribute], i, value)
+    mesh.vertexData[attribute][i] = value
   elif mesh.instanceData.contains(attribute):
     assert i < mesh.instanceData[attribute].len
-    setValue(mesh.instanceData[attribute], i, value)
+    mesh.instanceData[attribute][i] = value
   else:
     raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}")
   if not mesh.dirtyAttributes.contains(attribute):
@@ -372,19 +378,18 @@
 proc transform*[T: GPUType](mesh: var MeshObject, attribute: string, transform: Mat4) =
   if mesh.vertexData.contains(attribute):
     for i in 0 ..< mesh.vertexData[attribute].len:
-      setValue(mesh.vertexData[attribute], i, transform * mesh.vertexData[attribute][i, T])
+      mesh.vertexData[attribute][i] = transform * mesh.vertexData[attribute][i, T]
   elif mesh.instanceData.contains(attribute):
     for i in 0 ..< mesh.instanceData[attribute].len:
-      setValue(mesh.instanceData[attribute], i, transform * mesh.vertexData[attribute][i, T])
+      mesh.instanceData[attribute][i] = transform * mesh.vertexData[attribute][i, T]
   else:
     raise newException(Exception, &"Attribute {attribute} is not defined for mesh {mesh}")
 
 proc applyTransformToVertices*(mesh: var MeshObject, positionAttribute=DEFAULT_POSITION_ATTRIBUTE) =
   for i in 0 ..< mesh.vertexData[positionAttribute].len:
-    setValue(mesh.vertexData[positionAttribute], i, mesh.transform * mesh.vertexData[positionAttribute][i, Vec3f])
+    mesh.vertexData[positionAttribute][i] = mesh.transform * mesh.vertexData[positionAttribute][i, Vec3f]
   mesh.transform = Unit4
 
-
 func getCollisionPoints*(mesh: MeshObject, positionAttribute=DEFAULT_POSITION_ATTRIBUTE): seq[Vec3f] =
   for p in mesh[positionAttribute, Vec3f][]:
     result.add mesh.transform * p
--- a/semicongine/renderer.nim	Thu Jan 04 21:13:11 2024 +0700
+++ b/semicongine/renderer.nim	Sun Jan 07 00:56:44 2024 +0700
@@ -213,8 +213,7 @@
     for attribute in inputs:
       scenedata.vertexBufferOffsets[(mesh, attribute.name)] = perLocationOffsets[attribute.memoryPerformanceHint]
       if mesh[].attributes.contains(attribute.name):
-        let size = mesh[].getRawData(attribute.name)[1]
-        perLocationOffsets[attribute.memoryPerformanceHint] += size
+        perLocationOffsets[attribute.memoryPerformanceHint] += mesh[].attributeSize(attribute.name)
         if perLocationOffsets[attribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT != 0:
           perLocationOffsets[attribute.memoryPerformanceHint] += VERTEX_ATTRIB_ALIGNMENT - (perLocationOffsets[attribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT)
 
@@ -322,9 +321,12 @@
   if not (attribute in renderer.scenedata[scene].attributeLocation):
     return
 
-  let (pdata, size) = mesh[].getRawData(attribute)
   let memoryPerformanceHint = renderer.scenedata[scene].attributeLocation[attribute]
-  renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].setData(pdata, size, renderer.scenedata[scene].vertexBufferOffsets[(mesh, attribute)])
+  renderer.scenedata[scene].vertexBuffers[memoryPerformanceHint].setData(
+    mesh[].getPointer(attribute),
+    mesh[].attributeSize(attribute),
+    renderer.scenedata[scene].vertexBufferOffsets[(mesh, attribute)]
+  )
 
 proc updateMeshData*(renderer: var Renderer, scene: var Scene, forceAll=false) =
   assert scene in renderer.scenedata
@@ -340,13 +342,10 @@
 
 proc updateUniformData*(renderer: var Renderer, scene: var Scene, forceAll=false) =
   assert scene in renderer.scenedata
-  # TODO: maybe check for dirty materials too, but atm we copy materials into the
-  # renderers scenedata, so they are immutable after initialization, would 
-  # need to allow updates of materials too in order to make sense
 
   let dirty = scene.dirtyShaderGlobals
-  if not forceAll and dirty.len == 0:
-    return
+  # if not forceAll and dirty.len == 0:
+    # return
 
   if forceAll:
     debug "Update uniforms because 'forceAll' was given"
@@ -361,6 +360,10 @@
         renderer.scenedata[scene].uniformBuffers.hasKey(shaderPipeline.vk) and
         renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk].len != 0
       ):
+        var dirtyMaterialAttribs: seq[string]
+        for material in renderer.scenedata[scene].materials[materialType].mitems:
+          dirtyMaterialAttribs.add material.dirtyAttributes
+          material.clearDirtyAttributes()
         assert renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk][renderer.swapchain.currentInFlight].vk.valid
         if forceAll:
           for buffer in renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk]:
@@ -369,7 +372,7 @@
         var offset = 0
         # loop over all uniforms of the shader-shaderPipeline
         for uniform in shaderPipeline.uniforms:
-          if dirty.contains(uniform.name) or forceAll: # only update uniforms if necessary
+          if dirty.contains(uniform.name) or dirtyMaterialAttribs.contains(uniform.name) or forceAll: # only update uniforms if necessary
             var value = initDataList(uniform.theType)
             if scene.shaderGlobals.hasKey(uniform.name):
               assert scene.shaderGlobals[uniform.name].thetype == uniform.thetype
@@ -382,14 +385,13 @@
                   foundValue = true
               assert foundValue, &"Uniform '{uniform.name}' not found in scene shaderGlobals or materials"
             assert (uniform.arrayCount == 0 and value.len == 1) or value.len == uniform.arrayCount, &"Uniform '{uniform.name}' found has wrong length (shader declares {uniform.arrayCount} but shaderGlobals and materials provide {value.len})"
-            let (pdata, size) = value.getRawData()
-            assert size == uniform.size, "During uniform update: gathered value has size {size} but uniform expects size {uniform.size}"
+            assert value.size == uniform.size, "During uniform update: gathered value has size {value.size} but uniform expects size {uniform.size}"
             debug &"  update uniform {uniform.name} with value: {value}"
             # TODO: technically we would only need to update the uniform buffer of the current
             # frameInFlight (I think), but we don't track for which frame the shaderglobals are no longer dirty
             # therefore we have to update the uniform values in all buffers, of all inFlightframes (usually 2)
             for buffer in renderer.scenedata[scene].uniformBuffers[shaderPipeline.vk]:
-              buffer.setData(pdata, size, offset)
+              buffer.setData(value.getPointer(), value.size, offset)
           offset += uniform.size
   scene.clearDirtyShaderGlobals()
 
--- a/semicongine/resources/mesh.nim	Thu Jan 04 21:13:11 2024 +0700
+++ b/semicongine/resources/mesh.nim	Sun Jan 07 00:56:44 2024 +0700
@@ -116,7 +116,7 @@
   let accessorOffset = if accessor.hasKey("byteOffset"): accessor["byteOffset"].getInt() else: 0
   let length = bufferView["byteLength"].getInt()
   let bufferOffset = bufferView["byteOffset"].getInt() + accessorOffset
-  var dstPointer = result.getRawData()[0]
+  var dstPointer = result.getPointer()
 
   if bufferView.hasKey("byteStride"):
     warn "Congratulations, you try to test a feature (loading buffer data with stride attributes) that we have no idea where it is used and how it can be tested (need a coresponding *.glb file)."
@@ -170,23 +170,23 @@
   if defaultMaterial.attributes.contains("color"):
     attributes["color"] = initDataList(thetype=Vec4F32)
     if pbr.hasKey(GLTF_MATERIAL_MAPPING["color"]):
-      setValue(attributes["color"], @[newVec4f(
+      attributes["color"] = @[newVec4f(
         pbr[GLTF_MATERIAL_MAPPING["color"]][0].getFloat(),
         pbr[GLTF_MATERIAL_MAPPING["color"]][1].getFloat(),
         pbr[GLTF_MATERIAL_MAPPING["color"]][2].getFloat(),
         pbr[GLTF_MATERIAL_MAPPING["color"]][3].getFloat(),
-      )])
+      )]
     else:
-      setValue(attributes["color"], @[newVec4f(1, 1, 1, 1)])
+      attributes["color"] = @[newVec4f(1, 1, 1, 1)]
 
     # pbr material values
     for factor in ["metallic", "roughness"]:
       if defaultMaterial.attributes.contains(factor):
         attributes[factor] = initDataList(thetype=Float32)
         if pbr.hasKey(GLTF_MATERIAL_MAPPING[factor]):
-          setValue(attributes[factor], @[float32(pbr[GLTF_MATERIAL_MAPPING[factor]].getFloat())])
+          attributes[factor] = @[float32(pbr[GLTF_MATERIAL_MAPPING[factor]].getFloat())]
         else:
-          setValue(attributes[factor], @[0.5'f32])
+          attributes[factor] = @[0.5'f32]
 
   # pbr material textures
   for texture in ["baseTexture", "metallicRoughnessTexture"]:
@@ -194,11 +194,9 @@
       attributes[texture] = initDataList(thetype=TextureType)
       # attributes[texture & "Index"] = initDataList(thetype=UInt8)
       if pbr.hasKey(GLTF_MATERIAL_MAPPING[texture]):
-        setValue(attributes[texture], @[loadTexture(root, pbr[GLTF_MATERIAL_MAPPING[texture]]["index"].getInt(), mainBuffer)])
-        # setValue(attributes[texture & "Index"], @[pbr[GLTF_MATERIAL_MAPPING[texture]].getOrDefault("texCoord").getInt(0).uint8])
+        attributes[texture] = @[loadTexture(root, pbr[GLTF_MATERIAL_MAPPING[texture]]["index"].getInt(), mainBuffer)]
       else:
-        setValue(attributes[texture], @[EMPTY_TEXTURE])
-        # setValue(attributes[texture & "Index"], @[0'u8])
+        attributes[texture] = @[EMPTY_TEXTURE]
 
   # generic material textures
   for texture in ["normalTexture", "occlusionTexture", "emissiveTexture"]:
@@ -206,23 +204,21 @@
       attributes[texture] = initDataList(thetype=TextureType)
       # attributes[texture & "Index"] = initDataList(thetype=UInt8)
       if materialNode.hasKey(GLTF_MATERIAL_MAPPING[texture]):
-        setValue(attributes[texture], @[loadTexture(root, materialNode[texture]["index"].getInt(), mainBuffer)])
-        # setValue(attributes[texture & "Index"], @[materialNode[texture].getOrDefault("texCoord").getInt(0).uint8])
+        attributes[texture] = @[loadTexture(root, materialNode[texture]["index"].getInt(), mainBuffer)]
       else:
-        setValue(attributes[texture], @[EMPTY_TEXTURE])
-        # setValue(attributes[texture & "Index"], @[0'u8])
+        attributes[texture] = @[EMPTY_TEXTURE]
 
   # emissiv color
   if defaultMaterial.attributes.contains("emissiveColor"):
     attributes["emissiveColor"] = initDataList(thetype=Vec3F32)
     if materialNode.hasKey(GLTF_MATERIAL_MAPPING["emissiveColor"]):
-      setValue(attributes["emissiveColor"], @[newVec3f(
+      attributes["emissiveColor"] = @[newVec3f(
         materialNode[GLTF_MATERIAL_MAPPING["emissiveColor"]][0].getFloat(),
         materialNode[GLTF_MATERIAL_MAPPING["emissiveColor"]][1].getFloat(),
         materialNode[GLTF_MATERIAL_MAPPING["emissiveColor"]][2].getFloat(),
-      )])
+      )]
     else:
-      setValue(attributes["emissiveColor"], @[newVec3f(1'f32, 1'f32, 1'f32)])
+      attributes["emissiveColor"] = @[newVec3f(1'f32, 1'f32, 1'f32)]
 
   result = initMaterialData(theType=defaultMaterial, name=materialNode["name"].getStr(), attributes=attributes)
 
--- a/semicongine/scene.nim	Thu Jan 04 21:13:11 2024 +0700
+++ b/semicongine/scene.nim	Sun Jan 07 00:56:44 2024 +0700
@@ -46,7 +46,7 @@
 proc addShaderGlobal*[T](scene: var Scene, name: string, data: T) =
   assert not scene.loaded, &"Scene {scene.name} has already been loaded, cannot add shader values"
   scene.shaderGlobals[name] = initDataList(thetype=getDataType[T]())
-  setValues(scene.shaderGlobals[name], @[data])
+  scene.shaderGlobals[name] = @[data]
   scene.dirtyShaderGlobals.add name
 
 proc addShaderGlobalArray*[T](scene: var Scene, name: string, data: openArray[T]) =
@@ -61,12 +61,12 @@
   scene.shaderGlobals[name][T]
 
 proc setShaderGlobal*[T](scene: var Scene, name: string, value: T) =
-  setValues[T](scene.shaderGlobals[name], @[value])
+  scene.shaderGlobals[name] = @[value]
   if not scene.dirtyShaderGlobals.contains(name):
     scene.dirtyShaderGlobals.add name
 
 proc setShaderGlobalArray*[T](scene: var Scene, name: string, value: seq[T]) =
-  setValues[T](scene.shaderGlobals[name], value)
+  scene.shaderGlobals[name] = value
   if not scene.dirtyShaderGlobals.contains(name):
     scene.dirtyShaderGlobals.add name