changeset 1250:9ceb509af5ea

add: loading of most kinds of data from gltf
author sam <sam@basx.dev>
date Thu, 25 Jul 2024 23:15:05 +0700
parents d83726af7abb
children 3f98ad20a9d3
files semiconginev2/gltf.nim
diffstat 1 files changed, 51 insertions(+), 251 deletions(-) [+]
line wrap: on
line diff
--- a/semiconginev2/gltf.nim	Thu Jul 25 22:41:24 2024 +0700
+++ b/semiconginev2/gltf.nim	Thu Jul 25 23:15:05 2024 +0700
@@ -1,7 +1,11 @@
 type
-  GLTFMesh*[TMesh, TMaterial] = object
+  GltfNode* = object
+    children: seq[int]
+    mesh: int
+    transform: Mat4
+  GltfMesh*[TMesh, TMaterial] = object
     scenes*: seq[seq[int]] # each scene has a seq of node indices
-    nodes*: seq[seq[int]]  # each node has a seq of mesh indices
+    nodes*: seq[GltfNode]  # each node has a seq of mesh indices
     meshes*: seq[seq[(TMesh, VkPrimitiveTopology)]]
     materials*: seq[TMaterial]
     textures*: seq[Image[BGRA]]
@@ -43,32 +47,10 @@
     indices*: string
     material*: string
 
-#[
-static:
-  let TypeIds = {
-    int8: 5120,
-    uint8: 5121,
-    int16: 5122,
-    uint16: 5123,
-    uint32: 5125,
-    float32: 5126,
-  }.toTable
-]#
-
 const
   HEADER_MAGIC = 0x46546C67
   JSON_CHUNK = 0x4E4F534A
   BINARY_CHUNK = 0x004E4942
-  #[
-  ACCESSOR_TYPE_MAP = {
-    5120: Int8,
-    5121: UInt8,
-    5122: Int16,
-    5123: UInt16,
-    5125: UInt32,
-    5126: Float32,
-  }.toTable
-  ]#
   SAMPLER_FILTER_MODE_MAP = {
     9728: VK_FILTER_NEAREST,
     9729: VK_FILTER_LINEAR,
@@ -92,44 +74,6 @@
     6: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
   ]
 
-#[
-proc getGPUType(accessor: JsonNode, attribute: string): DataType =
-  # TODO: no full support for all datatypes that glTF may provide
-  # semicongine/core/gpu_data should maybe generated with macros to allow for all combinations
-  let componentType = ACCESSOR_TYPE_MAP[accessor["componentType"].getInt()]
-  let theType = accessor["type"].getStr()
-  case theType
-  of "SCALAR":
-    return componentType
-  of "VEC2":
-    case componentType
-    of UInt32: return Vec2U32
-    of Float32: return Vec2F32
-    else: raise newException(Exception, &"Unsupported data type for attribute '{attribute}': {componentType} {theType}")
-  of "VEC3":
-    case componentType
-    of UInt32: return Vec3U32
-    of Float32: return Vec3F32
-    else: raise newException(Exception, &"Unsupported data type for attribute '{attribute}': {componentType} {theType}")
-  of "VEC4":
-    case componentType
-    of UInt32: return Vec4U32
-    of Float32: return Vec4F32
-    else: raise newException(Exception, &"Unsupported data type for attribute '{attribute}': {componentType} {theType}")
-  of "MAT2":
-    case componentType
-    of Float32: return Vec4F32
-    else: raise newException(Exception, &"Unsupported data type for attribute '{attribute}': {componentType} {theType}")
-  of "MAT3":
-    case componentType
-    of Float32: return Vec4F32
-    else: raise newException(Exception, &"Unsupported data type for attribute '{attribute}': {componentType} {theType}")
-  of "MAT4":
-    case componentType
-    of Float32: return Vec4F32
-    else: raise newException(Exception, &"Unsupported data type for attribute '{attribute}': {componentType} {theType}")
-]#
-
 proc getBufferViewData(bufferView: JsonNode, mainBuffer: seq[uint8], baseBufferOffset = 0): seq[uint8] =
   assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported"
 
@@ -278,188 +222,48 @@
               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):
-            let accessor = primitive[gltfAttribute].getInt()
-            value.data = getAccessorData[elementType(value.data)](root, root["accessors"][accessor], mainBuffer)
-        elif gltfAttribute == "material":
-          if primitive.hasKey(gltfAttribute):
-            value.data = typeof(value.data)(primitive[gltfAttribute].getInt())
-        else:
-          if primitive["attributes"].hasKey(gltfAttribute):
-            let accessor = primitive["attributes"][gltfAttribute].getInt()
-            value.data = getAccessorData[elementType(value.data)](root, root["accessors"][accessor], mainBuffer)
-        ]#
 
-  #[
-  var indexType = None
-  let indexed = primitive.hasKey("indices")
-  if indexed:
-    var indexCount = root["accessors"][primitive["indices"].getInt()]["count"].getInt()
-    if indexCount < int(high(uint16)):
-      indexType = Small
-    else:
-      indexType = Big
-
-  for attribute, accessor in primitive["attributes"].pairs:
-    let data = root.getAccessorData(root["accessors"][accessor.getInt()], mainBuffer)
-    if result.vertexCount == 0:
-      result.vertexCount = data.len
-    assert data.len == result.vertexCount
-    result[].InitVertexAttribute(attribute.toLowerAscii, data)
-
-  if primitive.hasKey("material"):
-    let materialId = primitive["material"].getInt()
-    result[].material = materials[materialId]
-  else:
-    result[].material = EMPTY_MATERIAL.InitMaterialData()
-
-  if primitive.hasKey("indices"):
-    assert result[].indexType != None
-    let data = root.getAccessorData(root["accessors"][primitive["indices"].getInt()], mainBuffer)
-    var tri: seq[int]
-    case data.thetype
-      of UInt16:
-        for entry in data[uint16][]:
-          tri.add int(entry)
-          if tri.len == 3:
-            # FYI gltf uses counter-clockwise indexing
-            result[].AppendIndicesData(tri[0], tri[1], tri[2])
-            tri.setLen(0)
-      of UInt32:
-        for entry in data[uint32][]:
-          tri.add int(entry)
-          if tri.len == 3:
-            # FYI gltf uses counter-clockwise indexing
-            result[].AppendIndicesData(tri[0], tri[1], tri[2])
-            tri.setLen(0)
-      else:
-        raise newException(Exception, &"Unsupported index data type: {data.thetype}")
-  ]#
-
-
-#[
-
-proc loadPrimitive(meshname: string, root: JsonNode, primitiveNode: JsonNode, materials: seq[MaterialData], mainBuffer: seq[uint8]): Mesh =
-  if primitiveNode.hasKey("mode") and primitiveNode["mode"].getInt() != 4:
-    raise newException(Exception, "Currently only TRIANGLE mode is supported for geometry mode")
-
-  var indexType = None
-  let indexed = primitiveNode.hasKey("indices")
-  if indexed:
-    # TODO: Tiny indices
-    var indexCount = root["accessors"][primitiveNode["indices"].getInt()]["count"].getInt()
-    if indexCount < int(high(uint16)):
-      indexType = Small
-    else:
-      indexType = Big
-
-  result = Mesh(
-    instanceTransforms: @[Unit4F32],
-    indexType: indexType,
-    name: meshname,
-    vertexCount: 0,
-  )
+proc loadNode(node: JsonNode): GltfNode =
+  result.transform = Unit4
+  if "mesh" in node:
+    result.mesh = node["mesh"].getInt()
+  if "children" in node:
+    for child in items(node["children"]):
+      result.children.add child.getInt()
+  if "matrix" in node:
+    for i in 0 ..< node["matrix"].len:
+      result.transform[i] = node["matrix"][i].getFloat()
 
-  for attribute, accessor in primitiveNode["attributes"].pairs:
-    let data = root.getAccessorData(root["accessors"][accessor.getInt()], mainBuffer)
-    if result.vertexCount == 0:
-      result.vertexCount = data.len
-    assert data.len == result.vertexCount
-    result[].InitVertexAttribute(attribute.toLowerAscii, data)
-
-  if primitiveNode.hasKey("material"):
-    let materialId = primitiveNode["material"].getInt()
-    result[].material = materials[materialId]
-  else:
-    result[].material = EMPTY_MATERIAL.InitMaterialData()
-
-  if primitiveNode.hasKey("indices"):
-    assert result[].indexType != None
-    let data = root.getAccessorData(root["accessors"][primitiveNode["indices"].getInt()], mainBuffer)
-    var tri: seq[int]
-    case data.thetype
-      of UInt16:
-        for entry in data[uint16][]:
-          tri.add int(entry)
-          if tri.len == 3:
-            # FYI gltf uses counter-clockwise indexing
-            result[].AppendIndicesData(tri[0], tri[1], tri[2])
-            tri.setLen(0)
-      of UInt32:
-        for entry in data[uint32][]:
-          tri.add int(entry)
-          if tri.len == 3:
-            # FYI gltf uses counter-clockwise indexing
-            result[].AppendIndicesData(tri[0], tri[1], tri[2])
-            tri.setLen(0)
-      else:
-        raise newException(Exception, &"Unsupported index data type: {data.thetype}")
-  # TODO: getting from gltf to vulkan system is still messed up somehow, see other TODO
-  Transform[Vec3f](result[], "position", Scale(1, -1, 1))
-
-
-proc loadNode(root: JsonNode, node: JsonNode, materials: seq[MaterialData], mainBuffer: var seq[uint8]): MeshTree =
-  result = MeshTree()
-  # mesh
-  if node.hasKey("mesh"):
-    let mesh = root["meshes"][node["mesh"].getInt()]
-    for primitive in mesh["primitives"]:
-      result.children.add MeshTree(mesh: loadPrimitive(mesh["name"].getStr(), root, primitive, materials, mainBuffer))
+  var (t, r, s) = (Unit4, Unit4, Unit4)
+  if "translation" in node:
+    t = Translate(
+      float32(node["translation"][0].getFloat()),
+      float32(node["translation"][1].getFloat()),
+      float32(node["translation"][2].getFloat())
+    )
+  if "rotation" in node:
+    t = Rotate(
+      float32(node["rotation"][3].getFloat()),
+      NewVec3f(
+        float32(node["rotation"][0].getFloat()),
+        float32(node["rotation"][1].getFloat()),
+        float32(node["rotation"][2].getFloat())
+      )
+    )
+  if "scale" in node:
+    t = Scale(
+      float32(node["scale"][0].getFloat()),
+      float32(node["scale"][1].getFloat()),
+      float32(node["scale"][2].getFloat())
+    )
 
-  # transformation
-  if node.hasKey("matrix"):
-    var mat: Mat4
-    for i in 0 ..< node["matrix"].len:
-      mat[i] = node["matrix"][i].getFloat()
-    result.transform = mat
-  else:
-    var (t, r, s) = (Unit4F32, Unit4F32, Unit4F32)
-    if node.hasKey("translation"):
-      t = Translate(
-        float32(node["translation"][0].getFloat()),
-        float32(node["translation"][1].getFloat()),
-        float32(node["translation"][2].getFloat())
-      )
-    if node.hasKey("rotation"):
-      t = Rotate(
-        float32(node["rotation"][3].getFloat()),
-        NewVec3f(
-          float32(node["rotation"][0].getFloat()),
-          float32(node["rotation"][1].getFloat()),
-          float32(node["rotation"][2].getFloat())
-        )
-      )
-    if node.hasKey("scale"):
-      t = Scale(
-        float32(node["scale"][0].getFloat()),
-        float32(node["scale"][1].getFloat()),
-        float32(node["scale"][2].getFloat())
-      )
-    result.transform = t * r * s
-  result.transform = Scale(1, -1, 1) * result.transform
-
-  # children
-  if node.hasKey("children"):
-    for childNode in node["children"]:
-      result.children.add loadNode(root, root["nodes"][childNode.getInt()], materials, mainBuffer)
-
-proc loadScene(root: JsonNode, scenenode: JsonNode, materials: seq[MaterialData], mainBuffer: var seq[uint8]): MeshTree =
-  result = MeshTree()
-  for nodeId in scenenode["nodes"]:
-    result.children.add loadNode(root, root["nodes"][nodeId.getInt()], materials, mainBuffer)
-  # TODO: getting from gltf to vulkan system is still messed up somehow (i.e. not consistent for different files), see other TODO
-  # result.transform = Scale(1, -1, 1)
-  result.updateTransforms()
-
-  ]#
+  result.transform = t * r * s * result.transform
 
 proc ReadglTF*[TMesh, TMaterial](
   stream: Stream,
   meshAttributesMapping: static MeshAttributeNames,
   materialAttributesMapping: static MaterialAttributeNames,
-): GLTFMesh[TMesh, TMaterial] =
+): GltfMesh[TMesh, TMaterial] =
   var
     header: glTFHeader
     data: glTFData
@@ -503,28 +307,24 @@
         primitives.add loadPrimitive[TMesh](data.structuredContent, primitive, meshAttributesMapping, data.binaryBufferData)
       result.meshes.add primitives
 
-  echo "Textures:"
-  for t in result.textures:
-    echo "  ", t
-
-  echo "Materials:"
-  for m in result.materials:
-    echo "  ", m
+  if "nodes" in data.structuredContent:
+    for node in items(data.structuredContent["nodes"]):
+      result.nodes.add loadNode(node)
 
-  echo "Meshes:"
-  for m in result.meshes:
-    echo "  Primitives:"
-    for p in m:
-      for field, value in fieldPairs(p[0]):
-        if typeof(value) is GPUData:
-          echo "    ", field, ": ", value.data.len
+  if "scenes" in data.structuredContent:
+    for scene in items(data.structuredContent["scenes"]):
+      if "nodes" in scene:
+        var nodes: seq[int]
+        for nodeId in items(scene["nodes"]):
+          nodes.add nodeId.getInt()
+        result.scenes.add nodes
 
 proc LoadMeshes*[TMesh, TMaterial](
   path: string,
   meshAttributesMapping: static MeshAttributeNames,
   materialAttributesMapping: static MaterialAttributeNames,
   package = DEFAULT_PACKAGE
-): GLTFMesh[TMesh, TMaterial] =
+): GltfMesh[TMesh, TMaterial] =
   ReadglTF[TMesh, TMaterial](
     stream = loadResource_intern(path, package = package),
     meshAttributesMapping = meshAttributesMapping,