changeset 316:b145a05c2459

add: changing rendering system, not finished yet, also upgrading to Nim 2
author Sam <sam@basx.dev>
date Mon, 07 Aug 2023 00:23:00 +0700
parents 4921ec86dcb4
children 9efc258df208
files config.nims examples/E01_hello_triangle.nim examples/E02_squares.nim examples/E03_hello_cube.nim examples/E04_input.nim examples/E10_pong.nim src/semicongine/audio.nim src/semicongine/core/buildconfig.nim src/semicongine/engine.nim src/semicongine/mesh.nim src/semicongine/renderer.nim src/semicongine/resources/mesh.nim src/semicongine/scene.nim src/semicongine/vulkan/descriptor.nim src/semicongine/vulkan/shader.nim tests/test_audio.nim tests/test_font.nim tests/test_materials.nim tests/test_mesh.nim tests/test_vulkan_wrapper.nim
diffstat 20 files changed, 192 insertions(+), 158 deletions(-) [+]
line wrap: on
line diff
--- a/config.nims	Sun Jul 23 19:59:47 2023 +0700
+++ b/config.nims	Mon Aug 07 00:23:00 2023 +0700
@@ -37,7 +37,7 @@
   rmDir(outdir)
   mkDir(outdir)
   let resourcedir = joinPath(projectDir(), RESOURCEROOT)
-  if existsDir(resourcedir):
+  if dirExists(resourcedir):
     let outdir_resources = joinPath(outdir, RESOURCEROOT)
     if BUNDLETYPE == "dir":
       cpDir(resourcedir, outdir_resources)
@@ -66,6 +66,11 @@
     if file.endsWith(".nim"):
       exec(&"nim build -d:release {file}")
 
+task test_all, "Run all test programs":
+  for file in listFiles("tests"):
+    if file.endsWith(".nim"):
+      exec(&"nim build --run {file}")
+
 task clean, "remove all build files":
   exec(&"rm -rf {BUILDBASE}")
 
--- a/examples/E01_hello_triangle.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/examples/E01_hello_triangle.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -2,26 +2,21 @@
 
 
 const
-  vertexInput = @[
+  inputs = @[
     attr[Vec3f]("position"),
     attr[Vec4f]("color"),
   ]
-  vertexOutput = @[attr[Vec4f]("outcolor")]
-  fragOutput = @[attr[Vec4f]("color")]
-  vertexCode = compileGlslShader(
-    stage=VK_SHADER_STAGE_VERTEX_BIT,
-    inputs=vertexInput,
-    outputs=vertexOutput,
-    main="""
+  intermediate = @[attr[Vec4f]("outcolor")]
+  outputs = @[attr[Vec4f]("color")]
+  (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
+    inputs=inputs,
+    intermediate=intermediate,
+    outputs=outputs,
+    vertexCode="""
     gl_Position = vec4(position, 1.0);
     outcolor = color;
-    """
-  )
-  fragmentCode = compileGlslShader(
-    stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-    inputs=vertexOutput,
-    outputs=fragOutput,
-    main="color = outcolor;"
+    """,
+    fragmentCode="color = outcolor;",
   )
 
 var
@@ -36,7 +31,7 @@
   renderPass = myengine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode)
 
 myengine.setRenderer(renderPass)
-myengine.addScene(triangle, vertexInput, @[], transformAttribute="")
+myengine.addScene(triangle, inputs, @[], transformAttribute="")
 
 while myengine.updateInputs() == Running and not myengine.keyWasPressed(Escape):
   myengine.renderScene(triangle)
--- a/examples/E02_squares.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/examples/E02_squares.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -42,34 +42,28 @@
 
 
   const
-    vertexInput = @[
+    inputs = @[
       attr[Vec3f]("position"),
       attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
       attr[uint32]("index"),
     ]
-    vertexOutput = @[attr[Vec4f]("outcolor")]
+    intermediate = @[attr[Vec4f]("outcolor")]
     uniforms = @[attr[float32]("time")]
-    fragOutput = @[attr[Vec4f]("color")]
-    vertexCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_VERTEX_BIT,
-      inputs=vertexInput,
+    outputs = @[attr[Vec4f]("color")]
+    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
+      inputs=inputs,
+      intermediate=intermediate,
+      outputs=outputs,
       uniforms=uniforms,
-      outputs=vertexOutput,
-      main="""
+      vertexCode="""
 float pos_weight = index / 100.0; // add some gamma correction?
 float t = sin(Uniforms.time * 0.5) * 0.5 + 0.5;
 float v = min(1, max(0, pow(pos_weight - t, 2)));
 v = pow(1 - v, 3000);
 outcolor = vec4(color.r, color.g, v * 0.5, 1);
 gl_Position = vec4(position, 1.0);
-"""
-    )
-    fragmentCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-      inputs=vertexOutput,
-      uniforms=uniforms,
-      outputs=fragOutput,
-      main="color = outcolor;"
+""",
+      fragmentCode="color = outcolor;",
     )
   var squaremesh = newMesh(
     positions=vertices,
@@ -82,7 +76,7 @@
   myengine.setRenderer(myengine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode))
 
   var scene = newScene("scene", newEntity("scene", [], newEntity("squares", {"mesh": Component(squaremesh)})))
-  myengine.addScene(scene, vertexInput, @[], transformAttribute="")
+  myengine.addScene(scene, inputs, @[], transformAttribute="")
   scene.addShaderGlobal("time", 0.0'f32)
   while myengine.updateInputs() == Running and not myengine.keyWasPressed(Escape):
     setShaderGlobal(scene, "time", getShaderGlobal[float32](scene, "time") + 0.0005'f)
--- a/examples/E03_hello_cube.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/examples/E03_hello_cube.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -52,37 +52,31 @@
   var myengine = initEngine("Hello cube")
 
   const
-    vertexInput = @[
+    inputs = @[
       attr[Vec3f]("position"),
       attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
     ]
-    vertexOutput = @[attr[Vec4f]("outcolor")]
+    intermediate = @[attr[Vec4f]("outcolor")]
     uniforms = @[
       attr[Mat4]("projection"),
       attr[Mat4]("view"),
       attr[Mat4]("model"),
     ]
     fragOutput = @[attr[Vec4f]("color")]
-    vertexCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_VERTEX_BIT,
-      inputs=vertexInput,
+    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
+      inputs=inputs,
+      intermediate=intermediate,
+      outputs=fragOutput,
       uniforms=uniforms,
-      outputs=vertexOutput,
-      main="""outcolor = color; gl_Position = (Uniforms.projection * Uniforms.view * Uniforms.model) * vec4(position, 1);"""
-    )
-    fragmentCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-      inputs=vertexOutput,
-      uniforms=uniforms,
-      outputs=fragOutput,
-      main="color = outcolor;"
+      vertexCode="""outcolor = color; gl_Position = (Uniforms.projection * Uniforms.view * Uniforms.model) * vec4(position, 1);""",
+      fragmentCode="color = outcolor;",
     )
   myengine.setRenderer(myengine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode))
   var cube = newScene("scene", newEntity("cube", {"mesh": Component(newMesh(positions=cube_pos, indices=tris, colors=cube_color))}))
   cube.addShaderGlobal("projection", Unit4f32)
   cube.addShaderGlobal("view", Unit4f32)
   cube.addShaderGlobal("model", Unit4f32)
-  myengine.addScene(cube, vertexInput, @[], transformAttribute="")
+  myengine.addScene(cube, inputs, @[], transformAttribute="")
 
   var t: float32 = cpuTime()
   while myengine.updateInputs() == Running and not myengine.keyWasPressed(Escape):
--- a/examples/E04_input.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/examples/E04_input.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -147,32 +147,26 @@
 
   # shaders
   const
-    vertexInput = @[
+    inputs = @[
       attr[Vec3f]("position"),
       attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
       attr[Mat4]("transform", memoryPerformanceHint=PreferFastWrite, perInstance=true),
     ]
-    vertexOutput = @[attr[Vec4f]("outcolor")]
+    intermediate = @[attr[Vec4f]("outcolor")]
     uniforms = @[attr[Mat4]("projection")]
-    fragOutput = @[attr[Vec4f]("color")]
-    vertexCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_VERTEX_BIT,
-      inputs=vertexInput,
+    outputs = @[attr[Vec4f]("color")]
+    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
+      inputs=inputs,
+      intermediate=intermediate,
+      outputs=outputs,
       uniforms=uniforms,
-      outputs=vertexOutput,
-      main="""outcolor = color; gl_Position = vec4(position, 1) * (transform * Uniforms.projection);"""
-    )
-    fragmentCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-      inputs=vertexOutput,
-      uniforms=uniforms,
-      outputs=fragOutput,
-      main="color = outcolor;"
+      vertexCode="""outcolor = color; gl_Position = vec4(position, 1) * (transform * Uniforms.projection);""",
+      fragmentCode="color = outcolor;",
     )
 
   # set up rendering
   myengine.setRenderer(myengine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode, clearColor=newVec4f(0, 0, 0.5)))
-  myengine.addScene(scene, vertexInput, @[], transformAttribute="transform")
+  myengine.addScene(scene, inputs, @[], transformAttribute="transform")
   scene.addShaderGlobal("projection", Unit4f32)
 
   # mainloop
--- a/examples/E10_pong.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/examples/E10_pong.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -31,32 +31,26 @@
   level.root.add ball
 
   const
-    vertexInput = @[
+    inputs = @[
       attr[Vec3f]("position"),
       attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
       attr[Mat4]("transform", memoryPerformanceHint=PreferFastWrite, perInstance=true),
     ]
-    vertexOutput = @[attr[Vec4f]("outcolor")]
+    intermediate = @[attr[Vec4f]("outcolor")]
     uniforms = @[attr[Mat4]("projection")]
-    fragOutput = @[attr[Vec4f]("color")]
-    vertexCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_VERTEX_BIT,
-      inputs=vertexInput,
+    outputs = @[attr[Vec4f]("color")]
+    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
+      inputs=inputs,
+      intermediate=intermediate,
+      outputs=outputs,
       uniforms=uniforms,
-      outputs=vertexOutput,
-      main="""outcolor = color; gl_Position = vec4(position, 1) * (transform * Uniforms.projection);"""
-    )
-    fragmentCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-      inputs=vertexOutput,
-      uniforms=uniforms,
-      outputs=fragOutput,
-      main="color = outcolor;"
+      vertexCode="""outcolor = color; gl_Position = vec4(position, 1) * (transform * Uniforms.projection);""",
+      fragmentCode="color = outcolor;",
     )
 
   # set up rendering
   myengine.setRenderer(myengine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode, clearColor=backgroundColor))
-  myengine.addScene(level, vertexInput, @[], transformAttribute="transform")
+  myengine.addScene(level, inputs, @[], transformAttribute="transform")
   level.addShaderGlobal("projection", Unit4f32)
 
   var
--- a/src/semicongine/audio.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/audio.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -15,7 +15,6 @@
 
 const NBUFFERS = 4
 const BUFFERSAMPLECOUNT = 2048
-const SOUND_SCALE = 4 # SOUND_SCALE is logarithm-scale
 
 type
   Playback = object
--- a/src/semicongine/core/buildconfig.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/core/buildconfig.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -1,6 +1,5 @@
 import std/parsecfg
 import std/streams
-import std/compilesettings
 import std/strutils
 import std/logging
 import std/os
@@ -15,7 +14,6 @@
 
 # checks required build options:
 static:
-  assert querySetting(gc) == "orc", ENGINENAME & " requires --mm=orc"
   assert compileOption("threads"), ENGINENAME & " requires --threads=on"
 
   if defined(release):
--- a/src/semicongine/engine.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/engine.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -1,4 +1,5 @@
 import std/sequtils
+import std/tables
 import std/options
 import std/logging
 import std/os
@@ -10,6 +11,7 @@
 import ./vulkan/device
 import ./vulkan/physicaldevice
 import ./vulkan/renderpass
+import ./vulkan/shader
 
 import ./scene
 import ./renderer
@@ -108,6 +110,17 @@
 
 proc addScene*(engine: var Engine, scene: Scene, vertexInput: seq[ShaderAttribute], samplers: seq[ShaderAttribute], transformAttribute="transform", materialIndexAttribute="materialIndex") =
   assert transformAttribute == "" or transformAttribute in map(vertexInput, proc(a: ShaderAttribute): string = a.name)
+  assert materialIndexAttribute == "" or materialIndexAttribute in map(vertexInput, proc(a: ShaderAttribute): string = a.name)
+  assert engine.renderer.isSome
+  engine.renderer.get.setupDrawableBuffers(scene, vertexInput, samplers, transformAttribute=transformAttribute, materialIndexAttribute=materialIndexAttribute)
+
+proc addScene*(engine: var Engine, scene: Scene, materialShaders: Table[string, ShaderConfiguration], transformAttribute="transform", materialIndexAttribute="materialIndex") =
+  if transformAttribute != "":
+    for shader in materialShaders.values:
+      assert transformAttribute in map(shader.inputs, proc(a: ShaderAttribute): string = a.name)
+  if materialIndexAttribute != "":
+    for shader in materialShaders.values:
+      assert materialIndexAttribute in map(shader.inputs, proc(a: ShaderAttribute): string = a.name)
   assert engine.renderer.isSome
   engine.renderer.get.setupDrawableBuffers(scene, vertexInput, samplers, transformAttribute=transformAttribute, materialIndexAttribute=materialIndexAttribute)
 
--- a/src/semicongine/mesh.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/mesh.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -8,7 +8,6 @@
 import ./core
 import ./scene
 import ./collision
-import ./material
 
 type
   MeshIndexType* = enum
@@ -132,10 +131,10 @@
 
 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))
+    of None: 0'u32
+    of Tiny: uint32(mesh.tinyIndices.len * sizeof(get(genericParams(typeof(mesh.tinyIndices)), 0)))
+    of Small: uint32(mesh.smallIndices.len * sizeof(get(genericParams(typeof(mesh.smallIndices)), 0)))
+    of Big: uint32(mesh.bigIndices.len * sizeof(get(genericParams(typeof(mesh.bigIndices)), 0)))
 
 func rawData[T: seq](value: var T): (pointer, uint32) =
   (pointer(addr(value[0])), uint32(sizeof(get(genericParams(typeof(value)), 0)) * value.len))
--- a/src/semicongine/renderer.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/renderer.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -92,6 +92,7 @@
         mesh.initData(inputAttr)
       assert mesh.dataType(inputAttr.name) == inputAttr.thetype, &"mesh attribute {inputAttr.name} has type {mesh.dataType(inputAttr.name)} but shader expects {inputAttr.thetype}"
       if scenedata.materialIndexAttribute != "" and inputAttr.name == scenedata.materialIndexAttribute:
+        assert mesh.materials.len > 0, "Missing material specification for mesh. Either set the 'materials' attribute or pass the argument 'materialIndexAttribute=\"\"' when calling 'addScene'"
         assert mesh.materials.len == getMeshData[uint16](mesh, scenedata.materialIndexAttribute)[].len
         for i, material in enumerate(mesh.materials):
           let matIndex = materialIndex(scene, material)
@@ -135,7 +136,7 @@
     inc bindingNumber
     # setup one buffer per attribute-location-type
     for mesh in allMeshes:
-      # align size to VERTEX_ATTRIB_ALIGNMENT bytes (the important thing is the correct alignment of the offsets, bu
+      # align size to VERTEX_ATTRIB_ALIGNMENT bytes (the important thing is the correct alignment of the offsets, but
       # we need to expand the buffer size as well, therefore considering alignment already here as well
       if perLocationSizes[attribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT != 0:
         perLocationSizes[attribute.memoryPerformanceHint] += VERTEX_ATTRIB_ALIGNMENT - (perLocationSizes[attribute.memoryPerformanceHint] mod VERTEX_ATTRIB_ALIGNMENT)
--- a/src/semicongine/resources/mesh.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/resources/mesh.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -103,7 +103,7 @@
     raise newException(Exception, "Unsupported feature: byteStride in buffer view")
   copyMem(dstPointer, addr mainBuffer[bufferOffset], result.len)
 
-proc getAccessorData(root: JsonNode, accessor: JsonNode, mainBuffer: var seq[uint8]): DataList =
+proc getAccessorData(root: JsonNode, accessor: JsonNode, mainBuffer: seq[uint8]): DataList =
   result = newDataList(thetype=accessor.getGPUType())
   result.initData(uint32(accessor["count"].getInt()))
 
@@ -127,7 +127,7 @@
   else:
     copyMem(dstPointer, addr mainBuffer[bufferOffset], length)
 
-proc addPrimitive(mesh: var Mesh, root: JsonNode, primitiveNode: JsonNode, mainBuffer: var seq[uint8]) =
+proc addPrimitive(mesh: var Mesh, root: JsonNode, primitiveNode: JsonNode, mainBuffer: seq[uint8]) =
   if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4:
     raise newException(Exception, "Currently only TRIANGLE mode is supported for geometry mode")
 
@@ -163,7 +163,8 @@
       else:
         raise newException(Exception, &"Unsupported index data type: {data.thetype}")
 
-proc loadMesh(root: JsonNode, meshNode: JsonNode, mainBuffer: var seq[uint8], materials: seq[Material]): Mesh =
+# TODO: use one mesh per primitive?? right now we are merging primitives... check addPrimitive below
+proc loadMesh(root: JsonNode, meshNode: JsonNode, mainBuffer: seq[uint8], materials: seq[string]): Mesh =
   result = Mesh(instanceCount: 1, instanceTransforms: newSeqWith(1, Unit4F32))
 
   # check if and how we use indexes
@@ -187,7 +188,6 @@
   # prepare mesh attributes
   for attribute, accessor in meshNode["primitives"][0]["attributes"].pairs:
     result.setMeshData(attribute.toLowerAscii, newDataList(thetype=root["accessors"][accessor.getInt()].getGPUType()))
-  result.setMeshData("material", newDataList(thetype=getDataType[uint8]()))
 
   # add all mesh data
   for primitive in meshNode["primitives"]:
@@ -195,7 +195,7 @@
 
   setInstanceData(result, "transform", newSeqWith(int(result.instanceCount), Unit4F32))
 
-proc loadNode(root: JsonNode, node: JsonNode, mainBuffer: var seq[uint8], materials: seq[Material]): Entity =
+proc loadNode(root: JsonNode, node: JsonNode, mainBuffer: var seq[uint8], materials: seq[string]): Entity =
   var name = "<Unknown>"
   if node.hasKey("name"):
     name = node["name"].getStr()
@@ -241,7 +241,7 @@
   if node.hasKey("mesh"):
     result["mesh"] = loadMesh(root, root["meshes"][node["mesh"].getInt()], mainBuffer, materials)
 
-proc loadScene(root: JsonNode, scenenode: JsonNode, mainBuffer: var seq[uint8], materials: seq[Material]): Scene =
+proc loadScene(root: JsonNode, scenenode: JsonNode, mainBuffer: var seq[uint8], materials: seq[string]): Scene =
   var rootEntity = newEntity("<root>")
   for nodeId in scenenode["nodes"]:
     var node = loadNode(root, root["nodes"][nodeId.getInt()], mainBuffer, materials)
@@ -369,10 +369,12 @@
 
   debug data.structuredContent.pretty
 
-  for scene in data.structuredContent["scenes"]:
-    var materials: seq[Material]
+  for scenedata in data.structuredContent["scenes"]:
+    var materials: seq[string]
+    var scene = data.structuredContent.loadScene(scenedata, data.binaryBufferData, materials)
     for i, materialNode in enumerate(data.structuredContent["materials"]):
-      materials.add loadMaterial(data.structuredContent, materialNode, data.binaryBufferData, i)
-    var scene = data.structuredContent.loadScene(scene, data.binaryBufferData, materials)
+      let material = loadMaterial(data.structuredContent, materialNode, data.binaryBufferData, i)
+      materials.add material.name
+      scene.addMaterial material
 
     result.add scene
--- a/src/semicongine/scene.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/scene.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -1,6 +1,5 @@
 import std/strformat
 import std/sequtils
-import std/algorithm
 import std/strutils
 import std/tables
 import std/hashes
@@ -17,13 +16,6 @@
     shaderGlobals*: Table[string, DataList]
     materials: OrderedTable[string, Material]
 
-  #[
-  Material* = object
-    name*: string
-    textures*: Table[string, Texture]
-    data*: Table[string, DataValue]
-  ]#
-
   Component* = ref object of RootObj
     entity*: Entity
 
@@ -173,7 +165,7 @@
   for (name, comp) in components:
     result[name] = comp
   if result.name == "":
-    result.name = &"Entity[{$(cast[ByteAddress](result))}]"
+    result.name = &"Entity[{$(cast[uint](result))}]"
   result.internal_transform = Unit4
 
 iterator allEntitiesOfType*[T: Entity](root: Entity): T =
--- a/src/semicongine/vulkan/descriptor.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/vulkan/descriptor.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -151,7 +151,6 @@
           descriptorCount: descriptor.count,
           pBufferInfo: addr bufferInfos[^1],
         )
-      echo bufferInfos
     elif descriptor.thetype == ImageSampler:
       var imgInfo: seq[VkDescriptorImageInfo]
       for img_i in 0 ..< descriptor.count:
@@ -173,5 +172,4 @@
           pImageInfo: imgInfos[^1].toCPointer,
         )
     inc i
-  echo descriptorSetWrites
   descriptorSet.layout.device.vk.vkUpdateDescriptorSets(uint32(descriptorSetWrites.len), descriptorSetWrites.toCPointer, 0, nil)
--- a/src/semicongine/vulkan/shader.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/src/semicongine/vulkan/shader.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -34,6 +34,15 @@
     uniforms*: seq[ShaderAttribute]
     samplers*: seq[ShaderAttribute]
     outputs*: seq[ShaderAttribute]
+  ShaderConfiguration* = object
+    vertexBinary: seq[uint32]
+    fragmentBinary: seq[uint32]
+    entrypoint: string
+    inputs*: seq[ShaderAttribute]
+    intermediates*: seq[ShaderAttribute]
+    outputs*: seq[ShaderAttribute]
+    uniforms*: seq[ShaderAttribute]
+    samplers*: seq[ShaderAttribute]
 
 
 proc compileGlslToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} =
@@ -97,8 +106,7 @@
   main: string
 ): ShaderCode {.compileTime.} =
 
-  var code = @[&"#version {version}", "#extension GL_EXT_scalar_block_layout : require", ""] &
-  # var code = @[&"#version {version}", "layout(row_major) uniform;", ""] &
+  let code = @[&"#version {version}", "#extension GL_EXT_scalar_block_layout : require", ""] &
     (if inputs.len > 0: inputs.glslInput() & @[""] else: @[]) &
     (if uniforms.len > 0: uniforms.glslUniforms(binding=0) & @[""] else: @[]) &
     (if samplers.len > 0: samplers.glslSamplers(basebinding=if uniforms.len > 0: 1 else: 0) & @[""] else: @[]) &
@@ -114,7 +122,28 @@
   result.stage = stage
   result.binary = compileGlslToSPIRV(stage, code.join("\n"), entrypoint)
 
-proc compileVertextShader*(
+proc compileGlslCode*(
+  stage: VkShaderStageFlagBits,
+  inputs: seq[ShaderAttribute]= @[],
+  uniforms: seq[ShaderAttribute]= @[],
+  samplers: seq[ShaderAttribute]= @[],
+  outputs: seq[ShaderAttribute]= @[],
+  version=DEFAULT_SHADER_VERSION ,
+  entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
+  main: string
+): seq[uint32] {.compileTime.} =
+
+  let code = @[&"#version {version}", "#extension GL_EXT_scalar_block_layout : require", ""] &
+    (if inputs.len > 0: inputs.glslInput() & @[""] else: @[]) &
+    (if uniforms.len > 0: uniforms.glslUniforms(binding=0) & @[""] else: @[]) &
+    (if samplers.len > 0: samplers.glslSamplers(basebinding=if uniforms.len > 0: 1 else: 0) & @[""] else: @[]) &
+    (if outputs.len > 0: outputs.glslOutput() & @[""] else: @[]) &
+    @[&"void {entrypoint}(){{"] &
+    main &
+    @[&"}}"]
+  compileGlslToSPIRV(stage, code.join("\n"), entrypoint)
+
+proc compileVertexShader*(
   inputs: seq[ShaderAttribute]= @[],
   uniforms: seq[ShaderAttribute]= @[],
   samplers: seq[ShaderAttribute]= @[],
@@ -166,7 +195,7 @@
   fragmentCode: string,
 ): (ShaderCode, ShaderCode) {.compileTime.} =
 
-  result[0] = compileVertextShader(
+  result[0] = compileVertexShader(
     inputs=inputs,
     outputs=intermediate,
     uniforms=uniforms,
@@ -185,6 +214,42 @@
     main=fragmentCode
   )
 
+proc createShaderConfiguration*(
+  inputs: seq[ShaderAttribute]= @[],
+  intermediates: seq[ShaderAttribute]= @[],
+  outputs: seq[ShaderAttribute]= @[],
+  uniforms: seq[ShaderAttribute]= @[],
+  samplers: seq[ShaderAttribute]= @[],
+  version=DEFAULT_SHADER_VERSION ,
+  entrypoint=DEFAULT_SHADER_ENTRYPOINT ,
+  vertexCode: string,
+  fragmentCode: string,
+): ShaderConfiguration {.compileTime.} =
+  ShaderConfiguration(
+    vertexBinary: compileGlslCode(
+      stage=VK_SHADER_STAGE_VERTEX_BIT,
+      inputs=inputs,
+      outputs=intermediates,
+      uniforms=uniforms,
+      samplers=samplers,
+      main=vertexCode,
+    ),
+    fragmentBinary: compileGlslCode(
+      stage=VK_SHADER_STAGE_FRAGMENT_BIT,
+      inputs=inputs,
+      outputs=intermediates,
+      uniforms=uniforms,
+      samplers=samplers,
+      main=fragmentCode,
+    ),
+    entrypoint: entrypoint,
+    inputs: inputs,
+    intermediates: intermediates,
+    outputs: outputs,
+    uniforms: uniforms,
+    samplers: samplers,
+  )
+
 
 proc createShaderModule*(
   device: Device,
--- a/tests/test_audio.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/tests/test_audio.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -76,5 +76,5 @@
   mixer[].stop()
   test2()
   mixer[].stop()
-  test3()
-  mixer[].stop()
+  # test3()
+  # mixer[].stop()
--- a/tests/test_font.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/tests/test_font.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -45,7 +45,7 @@
 
   var engine = initEngine("Test fonts")
   engine.setRenderer(engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode))
-  engine.addScene(scene, vertexInput, samplers)
+  engine.addScene(scene, vertexInput, samplers, materialIndexAttribute="")
   scene.addShaderGlobal("perspective", Unit4F32)
 
   while engine.updateInputs() == Running and not engine.keyIsDown(Escape):
--- a/tests/test_materials.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/tests/test_materials.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -5,6 +5,7 @@
 
 proc main() =
   var flag = rect()
+  flag.materials = @["material2"]
   var scene = newScene("main", root=newEntity("rect", {"mesh": Component(flag)}))
   let (RT, WT, PT) = (hexToColorAlpha("A51931").asPixel, hexToColorAlpha("F4F5F8").asPixel, hexToColorAlpha("2D2A4A").asPixel)
   let
@@ -24,12 +25,12 @@
   sampler.magnification = VK_FILTER_NEAREST
   sampler.minification = VK_FILTER_NEAREST
   scene.addMaterial(Material(name:"material1", textures: {
-    "swissflag": Texture(image: swiss, sampler: sampler),
-    "thaiflag": Texture(image: thai, sampler: sampler),
+    "tex1": Texture(image: swiss, sampler: sampler),
+    "tex2": Texture(image: thai, sampler: sampler),
   }.toTable))
   scene.addMaterial(Material(name:"material2", textures: {
-    "swissflag": Texture(image: thai, sampler: sampler),
-    "thaiflag": Texture(image: swiss, sampler: sampler),
+    "tex1": Texture(image: thai, sampler: sampler),
+    "tex2": Texture(image: swiss, sampler: sampler),
   }.toTable))
   scene.addShaderGlobalArray("test2", @[0'f32, 0'f32])
 
@@ -38,36 +39,33 @@
   const
     vertexInput = @[
       attr[Vec3f]("position", memoryPerformanceHint=PreferFastRead),
+      attr[uint16]("materialIndex", memoryPerformanceHint=PreferFastRead, perInstance=true),
       attr[Vec2f]("uv", memoryPerformanceHint=PreferFastRead),
     ]
-    vertexOutput = @[attr[Vec2f]("uvout")]
+    vertexOutput = @[attr[Vec2f]("uvout"), attr[uint16]("materialIndexOut", noInterpolation=true)]
     uniforms = @[attr[float32]("test2", arrayCount=2)]
     samplers = @[
-      attr[Sampler2DType]("swissflag", arrayCount=2),
-      attr[Sampler2DType]("thaiflag", arrayCount=2),
+      attr[Sampler2DType]("tex1", arrayCount=2),
+      attr[Sampler2DType]("tex2", arrayCount=2),
     ]
     fragOutput = @[attr[Vec4f]("color")]
-    vertexCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_VERTEX_BIT,
+    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
       inputs=vertexInput,
-      uniforms=uniforms,
+      intermediate=vertexOutput,
+      outputs=fragOutput,
       samplers=samplers,
-      outputs=vertexOutput,
-      main="""gl_Position = vec4(position.x, position.y + sin(Uniforms.test2[1]) / Uniforms.test2[1] * 0.5, position.z, 1.0); uvout = uv;"""
-    )
-    fragmentCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-      inputs=vertexOutput,
       uniforms=uniforms,
-      samplers=samplers,
-      outputs=fragOutput,
-      main="""
-float d = sin(Uniforms.test2[0]) * 0.5 + 0.5;
-color = texture(swissflag[1], uvout) * (1 - d) + texture(thaiflag[1], uvout) * d;
-"""
+      vertexCode="""
+      gl_Position = vec4(position.x, position.y + sin(Uniforms.test2[1]) / Uniforms.test2[1] * 0.5, position.z, 1.0);
+      uvout = uv;
+      materialIndexOut = materialIndex;""",
+      fragmentCode="""
+      float d = sin(Uniforms.test2[0]) * 0.5 + 0.5;
+      color = texture(tex1[materialIndexOut], uvout) * (1 - d) + texture(tex2[materialIndexOut], uvout) * d;
+      """,
     )
   engine.setRenderer(engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode))
-  engine.addScene(scene, vertexInput, samplers, transformAttribute="", materialIndexAttribute="")
+  engine.addScene(scene, vertexInput, samplers, transformAttribute="")
   var t = cpuTime()
   while engine.updateInputs() == Running and not engine.keyIsDown(Escape):
     var d = float32(cpuTime() - t)
--- a/tests/test_mesh.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/tests/test_mesh.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -5,7 +5,7 @@
   var ent2 = newEntity("hehe", [], ent1)
   var myScene = newScene("hi", ent2)
   myScene.root.transform = translate3d(0.2'f32, 0'f32, 0'f32)
-  myScene.root.children[0].transform = translate3d(0'f32, 0.2'f32, 0'f32)
+  myScene.root[0].transform = translate3d(0'f32, 0.2'f32, 0'f32)
   var scenes = [
     # loadScene("default_cube.glb", "1"),
     # loadScene("default_cube1.glb", "3"),
@@ -55,7 +55,7 @@
     )
   engine.setRenderer(engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode, clearColor=newVec4f(0, 0, 0, 1)))
   for scene in scenes.mitems:
-    engine.addScene(scene, vertexInput, samplers, transformAttribute="transform")
+    engine.addScene(scene, vertexInput, samplers, transformAttribute="transform", materialIndexAttribute="")
     scene.addShaderGlobal("projection", Unit4)
     scene.addShaderGlobal("view", Unit4)
   var
--- a/tests/test_vulkan_wrapper.nim	Sun Jul 23 19:59:47 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Mon Aug 07 00:23:00 2023 +0700
@@ -90,25 +90,18 @@
       attr[Vec4f]("color", memoryPerformanceHint=PreferFastWrite),
       attr[Vec3f]("translate", perInstance=true)
     ]
-    vertexOutput = @[attr[Vec4f]("outcolor")]
-    uniforms = @[attr[float32]("time")]
-    samplers = @[attr[Sampler2DType]("my_little_texture")]
+    intermediate = @[attr[Vec4f]("outcolor")]
     fragOutput = @[attr[Vec4f]("color")]
-    vertexCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_VERTEX_BIT,
+    samplers = @[attr[Sampler2DType]("my_little_texture")]
+    uniforms = @[attr[float32]("time")]
+    (vertexCode, fragmentCode) = compileVertexFragmentShaderSet(
       inputs=vertexInput,
-      uniforms=uniforms,
+      intermediate=intermediate,
+      outputs=fragOutput,
       samplers=samplers,
-      outputs=vertexOutput,
-      main="""gl_Position = vec4(position + translate, 1.0); outcolor = color;"""
-    )
-    fragmentCode = compileGlslShader(
-      stage=VK_SHADER_STAGE_FRAGMENT_BIT,
-      inputs=vertexOutput,
       uniforms=uniforms,
-      samplers=samplers,
-      outputs=fragOutput,
-      main="color = texture(my_little_texture, outcolor.xy) * 0.5 + outcolor * 0.5;"
+      vertexCode="""gl_Position = vec4(position + translate, 1.0); outcolor = color;""",
+      fragmentCode="color = texture(my_little_texture, outcolor.xy) * 0.5 + outcolor * 0.5;",
     )
   var renderPass = engine.gpuDevice.simpleForwardRenderPass(vertexCode, fragmentCode)
   engine.setRenderer(renderPass)