changeset 1249:d83726af7abb

did: first triangles getting loaded from gltf
author sam <sam@basx.dev>
date Thu, 25 Jul 2024 22:41:24 +0700
parents 317bb5a73606
children 9ceb509af5ea
files semiconginev2/gltf.nim tests/test_gltf.nim
diffstat 2 files changed, 77 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/semiconginev2/gltf.nim	Thu Jul 25 20:23:54 2024 +0700
+++ b/semiconginev2/gltf.nim	Thu Jul 25 22:41:24 2024 +0700
@@ -141,7 +141,34 @@
     raise newException(Exception, "Unsupported feature: byteStride in buffer view")
   copyMem(dstPointer, addr mainBuffer[bufferOffset], result.len)
 
+proc componentTypeId(t: typedesc): int =
+  if t is int8: return 5120
+  elif t is uint8: return 5121
+  elif t is int16: return 5122
+  elif t is uint16: return 5123
+  elif t is uint32: return 5125
+  elif t is float32: return 5126
+
 proc getAccessorData[T](root: JsonNode, accessor: JsonNode, mainBuffer: seq[uint8]): seq[T] =
+  let componentType = accessor["componentType"].getInt()
+  let itemType = accessor["type"].getStr()
+
+  when T is TVec or T is TMat:
+    assert componentTypeId(elementType(default(T))) == componentType, name(T) & " != " & $componentType
+  else:
+    assert componentTypeId(T) == componentType, name(T) & " != " & $componentType
+
+  when T is TVec:
+    when len(default(T)) == 2: assert itemType == "VEC2"
+    elif len(default(T)) == 3: assert itemType == "VEC3"
+    elif len(default(T)) == 4: assert itemType == "VEC4"
+  elif T is TMat:
+    when T is Mat2: assert itemType == "MAT2"
+    elif T is Mat3: assert itemType == "MAT3"
+    elif T is Mat4: assert itemType == "MAT4"
+  else:
+    assert itemType == "SCALAR"
+
   result.setLen(accessor["count"].getInt())
 
   let bufferView = root["bufferViews"][accessor["bufferView"].getInt()]
@@ -158,9 +185,9 @@
   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)."
     # we don't support stride, have to convert stuff here... does this even work?
-    for i in 0 ..< int(result.len):
-      copyMem(dstPointer, addr mainBuffer[bufferOffset + i * bufferView["byteStride"].getInt()], result.len * sizeof(T))
-      dstPointer = cast[typeof(dstPointer)](cast[uint](dstPointer) + (result.len * sizeof(T)).uint)
+    for i in 0 ..< result.len:
+      copyMem(dstPointer, addr mainBuffer[bufferOffset + i * bufferView["byteStride"].getInt()], sizeof(T))
+      dstPointer = cast[typeof(dstPointer)](cast[uint](dstPointer) + sizeof(T).uint)
   else:
     copyMem(dstPointer, addr mainBuffer[bufferOffset], length)
 
@@ -193,8 +220,8 @@
 proc loadMaterial[TMaterial](
   root: JsonNode,
   materialNode: JsonNode,
+  mapping: static MaterialAttributeNames,
   mainBuffer: seq[uint8],
-  mapping: static MaterialAttributeNames
 ): TMaterial =
   result = TMaterial()
 
@@ -214,16 +241,43 @@
           else:
             {.error: "Unsupported gltf material attribute".}
 
-proc loadPrimitive[TMesh](root: JsonNode, primitive: JsonNode, mapping: static MeshAttributeNames, mainBuffer: seq[uint8]): (TMesh, VkPrimitiveTopology) =
+proc loadPrimitive[TMesh](
+  root: JsonNode,
+  primitive: JsonNode,
+  mapping: static MeshAttributeNames,
+  mainBuffer: seq[uint8]
+): (TMesh, VkPrimitiveTopology) =
   result[0] = TMesh()
   result[1] = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
   if primitive.hasKey("mode"):
     result[1] = PRIMITIVE_MODE_MAP[primitive["mode"].getInt()]
 
-  for name, value in fieldPairs(result[0]):
+  for resultFieldName, resultValue in fieldPairs(result[0]):
     for gltfAttribute, mappedName in fieldPairs(mapping):
-      when gltfAttribute != "" and name == mappedName:
-        assert value is GPUData, "Attribute " & name & " must be of type GPUData"
+      when typeof(mappedName) is string:
+        when gltfAttribute != "" and resultFieldName == mappedName:
+          assert resultValue is GPUData, "Attribute " & resultFieldName & " must be of type GPUData"
+          when gltfAttribute == "indices":
+            if primitive.hasKey(gltfAttribute):
+              let accessor = primitive[gltfAttribute].getInt()
+              resultValue.data = getAccessorData[elementType(resultValue.data)](root, root["accessors"][accessor], mainBuffer)
+          elif gltfAttribute == "material":
+            if primitive.hasKey(gltfAttribute):
+              resultValue.data = typeof(resultValue.data)(primitive[gltfAttribute].getInt())
+          else:
+            if primitive["attributes"].hasKey(gltfAttribute):
+              let accessor = primitive["attributes"][gltfAttribute].getInt()
+              resultValue.data = getAccessorData[elementType(resultValue.data)](root, root["accessors"][accessor], mainBuffer)
+      else:
+        var i = 0
+        for mappedIndexName in mappedName:
+          if gltfAttribute != "" and resultFieldName == mappedIndexName:
+            assert resultValue is GPUData, "Attribute " & resultFieldName & " must be of type GPUData"
+            let gltfAttributeIndexed = gltfAttribute & "_" & $i
+            if primitive["attributes"].hasKey(gltfAttributeIndexed):
+              let accessor = primitive["attributes"][gltfAttributeIndexed].getInt()
+              resultValue.data = getAccessorData[elementType(resultValue.data)](root, root["accessors"][accessor], mainBuffer)
+          inc i
         #[
         when gltfAttribute == "indices":
           if primitive.hasKey(gltfAttribute):
@@ -436,7 +490,7 @@
 
   if "materials" in data.structuredContent:
     for materialnode in items(data.structuredContent["materials"]):
-      result.materials.add loadMaterial[TMaterial](data.structuredContent, materialnode, data.binaryBufferData, materialAttributesMapping)
+      result.materials.add loadMaterial[TMaterial](data.structuredContent, materialnode, materialAttributesMapping, data.binaryBufferData)
 
   if "textures" in data.structuredContent:
     for texturenode in items(data.structuredContent["textures"]):
@@ -461,7 +515,9 @@
   for m in result.meshes:
     echo "  Primitives:"
     for p in m:
-      echo "    ", p[1], ": ", p[0]
+      for field, value in fieldPairs(p[0]):
+        if typeof(value) is GPUData:
+          echo "    ", field, ": ", value.data.len
 
 proc LoadMeshes*[TMesh, TMaterial](
   path: string,
--- a/tests/test_gltf.nim	Thu Jul 25 20:23:54 2024 +0700
+++ b/tests/test_gltf.nim	Thu Jul 25 22:41:24 2024 +0700
@@ -26,7 +26,6 @@
       material: GPUValue[Material, UniformBuffer]
     Shader = object
       position {.VertexAttribute.}: Vec3f
-      color {.VertexAttribute.}: Vec4f
       uv {.VertexAttribute.}: Vec2f
       fragmentColor {.Pass.}: Vec4f
       fragmentUv {.Pass.}: Vec2f
@@ -35,23 +34,21 @@
       # code
       vertexCode: string = """
 void main() {
-  fragmentColor = color;
+  fragmentColor = vec4(1, 1, 1, 1);
   fragmentUv = uv;
   gl_Position = vec4(position, 1);
 }"""
       fragmentCode: string = """void main() { outColor = fragmentColor;}"""
     Mesh = object
       position: GPUArray[Vec3f, VertexBuffer]
-      color: GPUArray[Vec4f, VertexBuffer]
       uv: GPUArray[Vec2f, VertexBuffer]
 
-  let gltfMesh = LoadMeshes[Mesh, Material](
+  var gltfMesh = LoadMeshes[Mesh, Material](
     "town.glb",
     MeshAttributeNames(
       POSITION: "position",
-      COLOR: @["color"],
-      TEXCOORD: @["uv"],
-    ),
+    TEXCOORD: @["uv"],
+  ),
     MaterialAttributeNames(
       baseColorFactor: "color",
       baseColorTexture: "colorTexture",
@@ -64,8 +61,9 @@
       emissiveFactor: "emissive",
     )
   )
-  var mesh = gltfMesh.meshes[0][0]
-  renderdata.AssignBuffers(mesh)
+  for mesh in mitems(gltfMesh.meshes):
+    for primitive in mitems(mesh):
+      renderdata.AssignBuffers(primitive[0])
   renderdata.FlushAllMemory()
 
   var pipeline = CreatePipeline[Shader](renderPass = vulkan.swapchain.renderPass)
@@ -79,14 +77,16 @@
 
         WithPipeline(commandbuffer, pipeline):
 
-          Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh)
+          for mesh in gltfMesh.meshes:
+            for primitive in mesh:
+              Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = primitive[0])
 
   # cleanup
   checkVkResult vkDeviceWaitIdle(vulkan.device)
   DestroyPipeline(pipeline)
   DestroyRenderData(renderdata)
 when isMainModule:
-  var time = 1'f32
+  var time = 5'f32
   InitVulkan()
 
   var renderpass = CreateDirectPresentationRenderPass(depthBuffer = true, samples = VK_SAMPLE_COUNT_4_BIT)