# HG changeset patch # User sam # Date 1722100654 -25200 # Node ID 2b5ca798f6d6fda6e9e3dd35807a983d47c2b3e2 # Parent b0f4c8ccd49a3adc57a6e338c2b39dee1ac7d0a4 did: make example town loadable and renderable, yay! diff -r b0f4c8ccd49a -r 2b5ca798f6d6 semiconginev2/gltf.nim --- a/semiconginev2/gltf.nim Sat Jul 27 20:47:54 2024 +0700 +++ b/semiconginev2/gltf.nim Sun Jul 28 00:17:34 2024 +0700 @@ -94,16 +94,25 @@ elif t is uint32: return 5125 elif t is float32: return 5126 -proc getAccessorData[T](root: JsonNode, accessor: JsonNode, mainBuffer: seq[ - uint8]): seq[T] = +proc componentTypeName(id: int): string = + if id == 5120: return int8.name + elif id == 5121: return uint8.name + elif id == 5122: return int16.name + elif id == 5123: return uint16.name + elif id == 5125: return uint32.name + elif id == 5126: return float32.name + +proc getAccessorData[T](root: JsonNode, accessor: JsonNode, mainBuffer: seq[uint8]): seq[T] = + if accessor.hasKey("sparse"): + raise newException(Exception, "Sparce accessors are currently not supported") + 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 + assert componentTypeId(elementType(default(T))) == componentType, "Requested type '" & name(elementType(default(T))) & $componentTypeId(elementType(default(T))) & "' but actual type is '" & componentTypeName(componentType) & "'" else: - assert componentTypeId(T) == componentType, name(T) & " != " & $componentType + assert componentTypeId(T) == componentType, "Requested type '" & name(T) & "' but actual type is '" & componentTypeName(componentType) & "'" when T is TVec: when len(default(T)) == 2: assert itemType == "VEC2" @@ -120,25 +129,18 @@ let bufferView = root["bufferViews"][accessor["bufferView"].getInt()] assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported" - - if accessor.hasKey("sparse"): - raise newException(Exception, "Sparce accessors are currently not supported") - - let accessorOffset = if accessor.hasKey("byteOffset"): accessor[ - "byteOffset"].getInt() else: 0 - let length = bufferView["byteLength"].getInt() - let bufferOffset = bufferView["byteOffset"].getInt() + accessorOffset + let accessorOffset = if accessor.hasKey("byteOffset"): accessor["byteOffset"].getInt() else: 0 + let bufferOffset = (if "byteOffset" in bufferView: bufferView["byteOffset"].getInt() else: 0) + accessorOffset var dstPointer = result.ToCPointer() 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 ..< result.len: - copyMem(dstPointer, addr mainBuffer[bufferOffset + i * bufferView[ - "byteStride"].getInt()], sizeof(T)) + 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) + copyMem(dstPointer, addr(mainBuffer[bufferOffset]), result.len * sizeof(T)) proc loadTexture(root: JsonNode, textureNode: JsonNode, mainBuffer: seq[ uint8]): Image[BGRA] = @@ -206,37 +208,36 @@ if primitive.hasKey("mode"): result[1] = PRIMITIVE_MODE_MAP[primitive["mode"].getInt()] + if primitive.hasKey("indices"): + assert mapping.indices != "", "Mesh requires indices" + for resultFieldName, resultValue in fieldPairs(result[0]): for gltfAttribute, mappedName in fieldPairs(mapping): - when typeof(mappedName) is string: - when gltfAttribute != "" and resultFieldName == mappedName: - assert resultValue is GPUData, "Attribute " & resultFieldName & " must be of type GPUData" + when typeof(mappedName) is seq: + when resultFieldName in mappedName: + 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 + elif typeof(mappedName) is string: + when resultFieldName == mappedName: + assert resultValue is GPUData or gltfAttribute == "material", "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) + 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()) + if primitive.hasKey(gltfAttribute): # assuming here that materials IDs are a normal field on the mesh, not GPUData + resultValue = typeof(resultValue)(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 + resultValue.data = getAccessorData[elementType(resultValue.data)](root, root["accessors"][accessor], mainBuffer) proc loadNode(node: JsonNode): GltfNode = result = GltfNode() @@ -304,8 +305,7 @@ # external binary buffers are not supported assert data.structuredContent["buffers"].len == 1 assert not data.structuredContent["buffers"][0].hasKey("uri") - let bufferLenDiff = int(chunkLength) - data.structuredContent["buffers"][0][ - "byteLength"].getInt() + let bufferLenDiff = int(chunkLength) - data.structuredContent["buffers"][0]["byteLength"].getInt() assert 0 <= bufferLenDiff and bufferLenDiff <= 3 # binary buffer may be aligned to 4 bytes debug "Loading mesh: ", data.structuredContent.pretty diff -r b0f4c8ccd49a -r 2b5ca798f6d6 semiconginev2/rendering/renderer.nim --- a/semiconginev2/rendering/renderer.nim Sat Jul 27 20:47:54 2024 +0700 +++ b/semiconginev2/rendering/renderer.nim Sun Jul 28 00:17:34 2024 +0700 @@ -607,10 +607,10 @@ proc assertCanRenderMesh(TShader, TMesh, TInstance: typedesc) = for attrName, attrValue in default(TShader).fieldPairs: - if hasCustomPragma(attrValue, VertexAttribute): + when hasCustomPragma(attrValue, VertexAttribute): var foundAttr = false for meshAttrName, meshAttrValue in default(TMesh).fieldPairs: - if attrName == meshAttrName: + when attrName == meshAttrName: assert typeof(meshAttrValue) is GPUArray, "Mesh attribute '" & attrName & "' must be a GPUArray" assert typeof(attrValue) is elementType(meshAttrValue.data), "Type of shader attribute and mesh attribute '" & attrName & "' is not the same" foundAttr = true diff -r b0f4c8ccd49a -r 2b5ca798f6d6 tests/test_gltf.nim --- a/tests/test_gltf.nim Sat Jul 27 20:47:54 2024 +0700 +++ b/tests/test_gltf.nim Sun Jul 28 00:17:34 2024 +0700 @@ -13,53 +13,58 @@ type ObjectData = object transform: Mat4 + materialId: int32 Camera = object viewPerspective: Mat4 Material = object color: Vec4f = NewVec4f(1, 1, 1, 1) - colorTexture: int32 = -1 + # colorTexture: int32 = -1 metallic: float32 = 0 roughness: float32 = 0 - metallicRoughnessTexture: int32 = -1 - - normalTexture: int32 = -1 - occlusionTexture: int32 = -1 + # metallicRoughnessTexture: int32 = -1 + # normalTexture: int32 = -1 + # occlusionTexture: int32 = -1 emissive: Vec4f = NewVec4f(0, 0, 0, 0) - emissiveTexture: int32 = -1 + # emissiveTexture: int32 = -1 MainDescriptors = object - material: GPUValue[Material, UniformBuffer] + materials: array[32, GPUValue[Material, UniformBuffer]] camera: GPUValue[Camera, UniformBufferMapped] Shader = object objectData {.PushConstantAttribute.}: ObjectData position {.VertexAttribute.}: Vec3f color {.VertexAttribute.}: Vec4f - # uv {.VertexAttribute.}: Vec2f + normal {.VertexAttribute.}: Vec3f fragmentColor {.Pass.}: Vec4f - fragmentUv {.Pass.}: Vec2f + fragmentNormal {.Pass.}: Vec3f outColor {.ShaderOutput.}: Vec4f descriptors {.DescriptorSets.}: (MainDescriptors, ) # code vertexCode: string = """ void main() { - fragmentColor = color; - // fragmentUv = uv; - gl_Position = vec4(position, 1) * camera.viewPerspective; + fragmentColor = color * materials[objectData.materialId].color; + fragmentNormal = normal; + gl_Position = vec4(position, 1) * (objectData.transform * camera.viewPerspective); }""" - fragmentCode: string = """void main() { outColor = fragmentColor;}""" + fragmentCode: string = """ +const vec3 lightDir = normalize(vec3(1, -1, 1)); +void main() { + outColor = vec4(fragmentColor.rgb * (1 - abs(dot(fragmentNormal, lightDir))), fragmentColor.a); +}""" Mesh = object position: GPUArray[Vec3f, VertexBuffer] color: GPUArray[Vec4f, VertexBuffer] - uv: GPUArray[Vec2f, VertexBuffer] - DebugMesh = object - position: GPUArray[Vec3f, VertexBuffer] - color: GPUArray[Vec4f, VertexBuffer] + normal: GPUArray[Vec3f, VertexBuffer] + indices: GPUArray[uint32, IndexBuffer] + material: int32 var gltfData = LoadMeshes[Mesh, Material]( "town.glb", MeshAttributeNames( POSITION: "position", COLOR: @["color"], - TEXCOORD: @["uv"], + NORMAL: "normal", + indices: "indices", + material: "material", ), MaterialAttributeNames( baseColorFactor: "color", @@ -80,34 +85,20 @@ ), UniformBufferMapped) ) ) + for i in 0 ..< gltfData.materials.len: + descriptors.data.materials[i] = asGPUValue(gltfData.materials[i], UniformBuffer) for mesh in mitems(gltfData.meshes): for primitive in mitems(mesh): primitive[0].color = asGPUArray(newSeqWith(primitive[0].position.data.len, NewVec4f(1, 1, 1, 1)), VertexBuffer) renderdata.AssignBuffers(primitive[0]) renderdata.AssignBuffers(descriptors) - let O = default(Vec3f) - let Gray = NewVec4f(0.5, 0.5, 0.5, 1) - var gridPos = @[O, X, O, Y, O, Z] - var gridColor = @[R, R, G, G, B, B] - for i in 0 ..< 10: - gridPos.add [NewVec3f(-5, -0.001, i.float32 - 5), NewVec3f(5, -0.001, i.float32 - 5)] - gridPos.add [NewVec3f(i.float32 - 5, -0.001, -5), NewVec3f(i.float32 - 5, -0.001, 5)] - gridColor.add [Gray, Gray, Gray, Gray] - var grid = DebugMesh( - position: asGPUArray(gridPos, VertexBuffer), - color: asGPUArray(gridColor, VertexBuffer), - ) - renderdata.AssignBuffers(grid) - - var pipeline = CreatePipeline[Shader](renderPass = vulkan.swapchain.renderPass, cullMode = []) - var debugpipeline = CreatePipeline[Shader](renderPass = vulkan.swapchain.renderPass, topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, lineWidth=10) + var pipeline = CreatePipeline[Shader](renderPass = vulkan.swapchain.renderPass) InitDescriptorSet(renderdata, pipeline.descriptorSetLayouts[0], descriptors) renderdata.FlushAllMemory() - proc drawNode(commandbuffer: VkCommandBuffer, pipeline: Pipeline, nodeId: int, - transform: Mat4 = Unit4) = + proc drawNode(commandbuffer: VkCommandBuffer, pipeline: Pipeline, nodeId: int, transform: Mat4) = let nodeTransform = gltfData.nodes[nodeId].transform * transform if gltfData.nodes[nodeId].mesh >= 0: for primitive in gltfData.meshes[gltfData.nodes[nodeId].mesh]: @@ -115,7 +106,7 @@ commandbuffer = commandbuffer, pipeline = pipeline, mesh = primitive[0], - pushConstant = ObjectData(transform: nodeTransform) + pushConstant = ObjectData(transform: nodeTransform, materialId: primitive[0].material) ) for childNode in gltfData.nodes[nodeId].children: drawNode(commandbuffer = commandbuffer, pipeline = pipeline, nodeId = childNode, transform = nodeTransform) @@ -142,16 +133,13 @@ if KeyIsDown(A): sideward -= 2 if KeyIsDown(D): sideward += 2 - let camDir = (Rotate(camPitch, X) * Rotate(camYaw, Y)) * Z - echo camDir - let camDirSide = (Rotate(camPitch, X) * Rotate(camYaw, Y)) * X - # echo camDir - # echo camDirSide + let camDir = (Rotate(camYaw, Y) * Rotate(camPitch, X)) * Z + let camDirSide = camDir.Cross(-Y).Normalized camPos += camDir * forward * dt camPos += camDirSide * sideward * dt descriptors.data.camera.data.viewPerspective = ( - Perspective(PI/3, aspect = GetAspectRatio(), zNear = 0.001, zFar = 100) * + Perspective(PI/3, aspect = GetAspectRatio(), zNear = 0.1, zFar = 1) * Rotate(-camPitch, X) * Rotate(-camYaw, Y) * Translate(-camPos) ) @@ -164,16 +152,18 @@ WithPipeline(commandbuffer, pipeline): WithBind(commandbuffer, (descriptors, ), pipeline): for nodeId in gltfData.scenes[0]: - drawNode(commandbuffer = commandbuffer, pipeline = pipeline, nodeId = nodeId) - WithBind(commandbuffer, (descriptors, ), pipeline): - WithPipeline(commandbuffer, debugpipeline): - Render(commandbuffer = commandbuffer, pipeline = debugpipeline, mesh = grid) + drawNode( + commandbuffer = commandbuffer, + pipeline = pipeline, + nodeId = nodeId, + transform = Rotate(PI / 2, Z) + ) # cleanup checkVkResult vkDeviceWaitIdle(vulkan.device) DestroyPipeline(pipeline) - DestroyPipeline(debugpipeline) DestroyRenderData(renderdata) + when isMainModule: var time = 1000'f32 InitVulkan()