changeset 407:ffc265916415

did: improve/refactor some of the material API
author Sam <sam@basx.dev>
date Wed, 03 Jan 2024 11:19:55 +0700
parents 7d926cc81620
children 848a6845a588
files semicongine/core/dynamic_arrays.nim semicongine/material.nim semicongine/renderer.nim semicongine/resources/mesh.nim semicongine/text.nim tests/test_audio.nim tests/test_materials.nim
diffstat 7 files changed, 46 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/semicongine/core/dynamic_arrays.nim	Mon Jan 01 21:41:40 2024 +0700
+++ b/semicongine/core/dynamic_arrays.nim	Wed Jan 03 11:19:55 2024 +0700
@@ -1,4 +1,5 @@
 import std/hashes
+import std/strformat
 
 import ./gpu_types
 import ./vector
@@ -252,7 +253,7 @@
   elif T is Texture: value.texture[] = data
   else: {. error: "Virtual datatype has no values" .}
 
-proc initDataList*(theType: DataType, len=1): DataList =
+proc initDataList*(theType: DataType, len=0): DataList =
   result = DataList(theType: theType)
   case result.theType
     of Float32: result.float32 = new seq[float32]
@@ -515,7 +516,7 @@
   else: {. error: "Virtual datatype has no values" .}
 
 proc appendValues*(value: var DataList, data: DataList) =
-  assert value.theType == data.theType
+  assert value.theType == data.theType, &"Expected datalist of type {value.theType} but got {data.theType}"
   value.len += data.len
   case value.theType:
   of Float32: value.float32[].add data.float32[]
@@ -563,6 +564,7 @@
   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
--- a/semicongine/material.nim	Mon Jan 01 21:41:40 2024 +0700
+++ b/semicongine/material.nim	Wed Jan 03 11:19:55 2024 +0700
@@ -35,9 +35,15 @@
 proc `==`*(a, b: MaterialData): bool =
   return a.name == b.name
 
-proc get*(material: MaterialData, attributeName: string): DataList =
+proc get*[T](material: MaterialData, attributeName: string): seq[T] =
+  getValues[T](material.attributes[attributeName])[]
+
+proc getDataList*(material: MaterialData, attributeName: string): DataList =
   material.attributes[attributeName]
 
+proc getSingle*[T](material: MaterialData, attributeName: string): T =
+  getValues[T](material.attributes[attributeName])[][0]
+
 let EMPTY_MATERIAL* = MaterialType(
   name: "empty material",
   vertexAttributes: {"position": Vec3F32}.toTable,
@@ -91,29 +97,29 @@
   return &"""Material '{material.name}' | Attributes: {attributes.join(", ")}"""
 
 proc initMaterialData*(
-  materialType: MaterialType,
+  theType: MaterialType,
   name: string,
   attributes: Table[string, DataList],
 ): MaterialData =
   var theName = name
   if theName == "":
-    theName = &"material instance of '{materialType}'"
-  for matName, theType in materialType.attributes.pairs:
-    assert attributes.contains(matName), &"missing material attribute '{matName}' for {materialType}"
+    theName = &"material instance of '{theType}'"
+  for matName, theType in theType.attributes.pairs:
+    assert attributes.contains(matName), &"missing material attribute '{matName}' for {theType}"
     assert attributes[matName].theType == theType
   MaterialData(
-    theType: materialType,
+    theType: theType,
     name: theName,
     attributes: attributes,
   )
 
 proc initMaterialData*(
-  materialType: MaterialType,
+  theType: MaterialType,
   name: string = "",
   attributes: openArray[(string, DataList)] = @[],
 ): MaterialData =
   var theName = name
   if theName == "":
-    theName = &"material instance of '{materialType}'"
-  initMaterialData(materialType=materialType, name=theName, attributes=attributes.toTable)
+    theName = &"material instance of '{theType}'"
+  initMaterialData(theType=theType, name=theName, attributes=attributes.toTable)
 
--- a/semicongine/renderer.nim	Mon Jan 01 21:41:40 2024 +0700
+++ b/semicongine/renderer.nim	Wed Jan 03 11:19:55 2024 +0700
@@ -271,13 +271,11 @@
             for material in scene.getMaterials(materialType):
               if material.hasMatchingAttribute(texture):
                 foundTexture = true
-                assert value.len == 1, &"Mesh material attribute '{materialAttribName}' has texture-array, but only single textures are allowed"
-                let textureValue = getValues[Texture](value)[][0]
-                if not uploadedTextures.contains(textureValue):
-                  uploadedTextures[textureValue] = renderer.device.uploadTexture(textureValue)
-                scenedata.textures[shaderPipeline.vk][texture.name].add uploadedTextures[textureValue]
-
-                break
+                let value = get[Texture](material, texture.name)
+                assert value.len == 1, &"Mesh material attribute '{texture.name}' has texture-array, but only single textures are allowed"
+                if not uploadedTextures.contains(value[0]):
+                  uploadedTextures[value[0]] = renderer.device.uploadTexture(value[0])
+                scenedata.textures[shaderPipeline.vk][texture.name].add uploadedTextures[value[0]]
             assert foundTexture, &"No texture found in shaderGlobals or materials for '{texture.name}'"
           let nTextures = scenedata.textures[shaderPipeline.vk][texture.name].len
           assert (texture.arrayCount == 0 and nTextures == 1) or texture.arrayCount == nTextures, &"Shader assigned to render '{materialType}' expected {texture.arrayCount} textures for '{texture.name}' but got {nTextures}"
@@ -348,7 +346,6 @@
 
   let dirty = scene.dirtyShaderGlobals
   if not forceAll and dirty.len == 0:
-    echo "Nothing dirty"
     return
 
   if forceAll:
@@ -372,31 +369,24 @@
         var offset = 0
         # loop over all uniforms of the shader-shaderPipeline
         for uniform in shaderPipeline.uniforms:
-          echo "UNIFORM: ", uniform
-          if dirty.contains(uniform.name) or forceAll: # only update if necessary
-            var value: DataList
+          if dirty.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
               value = scene.shaderGlobals[uniform.name]
             else:
               var foundValue = false
               for material in renderer.scenedata[scene].materials[materialType]:
-                for name, materialValue in material.attributes.pairs:
-                  if uniform.name == name:
-                    if not foundValue:
-                      foundValue = true
-                      value = initDataList(materialValue.theType, 0)
-                    else:
-                      assert value.theType == materialValue.theType, &"Material for uniform '{uniform.name}' was found multiple times with different types: {value.theType} and {materialValue.theType}"
-                    value.appendValues(materialValue)
-                    break
+                if material.hasMatchingAttribute(uniform):
+                  value.appendValues(material.getDataList(uniform.name))
+                  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 only provide {value.len})"
+            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}"
             debug &"  update uniform {uniform.name} with value: {value}"
             # TODO: technically we would only need to update the uniform buffer of the current
-            # frameInFlight, but we don't track for which frame the shaderglobals are no longer dirty
+            # 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)
--- a/semicongine/resources/mesh.nim	Mon Jan 01 21:41:40 2024 +0700
+++ b/semicongine/resources/mesh.nim	Wed Jan 03 11:19:55 2024 +0700
@@ -224,7 +224,7 @@
     else:
       setValue(attributes["emissiveColor"], @[newVec3f(1'f32, 1'f32, 1'f32)])
 
-  result = initMaterialData(materialType=defaultMaterial, name=materialNode["name"].getStr(), attributes=attributes)
+  result = initMaterialData(theType=defaultMaterial, name=materialNode["name"].getStr(), attributes=attributes)
 
 proc loadMesh(meshname: string, root: JsonNode, primitiveNode: JsonNode, defaultMaterial: MaterialType, mainBuffer: seq[uint8]): Mesh =
   if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4:
--- a/semicongine/text.nim	Mon Jan 01 21:41:40 2024 +0700
+++ b/semicongine/text.nim	Wed Jan 03 11:19:55 2024 +0700
@@ -114,10 +114,10 @@
   inc instanceCounter
   result.mesh[].renameAttribute("position", POSITION_ATTRIB)
   result.mesh[].renameAttribute("uv", UV_ATTRIB)
-  result.mesh.material = MaterialData(
-    theType: TEXT_MATERIAL_TYPE,
-    name: font.name & " text",
-    attributes: {"fontAtlas": initDataList(@[font.fontAtlas])}.toTable,
+  result.mesh.material = initMaterialData(
+    theType=TEXT_MATERIAL_TYPE,
+    name=font.name & " text",
+    attributes={"fontAtlas": initDataList(@[font.fontAtlas])},
   )
 
   result.updateMesh()
--- a/tests/test_audio.nim	Mon Jan 01 21:41:40 2024 +0700
+++ b/tests/test_audio.nim	Wed Jan 03 11:19:55 2024 +0700
@@ -73,9 +73,9 @@
 
 when isMainModule:
   startMixerThread()
-  # test1()
+  test1()
+  mixer[].stop()
+  test2()
+  mixer[].stop()
+  # test3()
   # mixer[].stop()
-  # test2()
-  # mixer[].stop()
-  test3()
-  mixer[].stop()
--- a/tests/test_materials.nim	Mon Jan 01 21:41:40 2024 +0700
+++ b/tests/test_materials.nim	Wed Jan 03 11:19:55 2024 +0700
@@ -22,13 +22,13 @@
     }.toTable,
     attributes: {"tex1": TextureType, "tex2": TextureType}.toTable
   )
-  material = MaterialData(
-    theType: doubleTextureMaterial,
-    name: "swiss-thai",
-    attributes: {
+  material = initMaterialData(
+    theType=doubleTextureMaterial,
+    name="swiss-thai",
+    attributes={
       "tex1": initDataList(@[Texture(image: thai, sampler: sampler)]),
       "tex2": initDataList(@[Texture(image: swiss, sampler: sampler)]),
-    }.toTable
+    }
   )
 
 proc main() =