# HG changeset patch # User sam # Date 1721922084 -25200 # Node ID d83726af7abb9ffe55cbd65bf1498709f03b235e # Parent 317bb5a736066aec72c19685677d5c778800d0cc did: first triangles getting loaded from gltf diff -r 317bb5a73606 -r d83726af7abb semiconginev2/gltf.nim --- 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, diff -r 317bb5a73606 -r d83726af7abb tests/test_gltf.nim --- 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)