# HG changeset patch # User sam # Date 1722088074 -25200 # Node ID b0f4c8ccd49a3adc57a6e338c2b39dee1ac7d0a4 # Parent c4f98eb4bb0541e64723c589956a5434ce30b4bf did: stuff to test gltf importer diff -r c4f98eb4bb05 -r b0f4c8ccd49a semiconginev2/core/matrix.nim --- a/semiconginev2/core/matrix.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/semiconginev2/core/matrix.nim Sat Jul 27 20:47:54 2024 +0700 @@ -23,6 +23,7 @@ TMat4*[T: SomeNumber] = object data*: array[16, T] TMat* = TMat2|TMat3|TMat4|TMat23|TMat32|TMat34|TMat43 + TSquareMat = TMat2|TMat3|TMat4 Mat2* = TMat2[float32] Mat23* = TMat23[float32] Mat32* = TMat32[float32] @@ -312,6 +313,15 @@ createAllMultiplicationOperators() +proc `+=`*[T1: TSquareMat, T2: TSquareMat|SomeNumber](a: var T1, b: T2) = + a = a + b + +proc `-=`*[T1: TSquareMat, T2: TSquareMat|SomeNumber](a: var T1, b: T2) = + a = a + b + +proc `*=`*[T1: TSquareMat, T2: TSquareMat|SomeNumber](a: var T1, b: T2) = + a = a * b + func `*`*(mat: Mat4, vec: Vec3f): Vec3f = (mat * vec.ToVec4(1)).ToVec3 diff -r c4f98eb4bb05 -r b0f4c8ccd49a semiconginev2/core/vector.nim --- a/semiconginev2/core/vector.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/semiconginev2/core/vector.nim Sat Jul 27 20:47:54 2024 +0700 @@ -40,9 +40,9 @@ func ConstX[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(1), T(0), T(0)]) func ConstY[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(1), T(0)]) func ConstZ[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(0), T(1)]) -func ConstR[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(1), T(0), T(0)]) -func ConstG[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(1), T(0)]) -func ConstB[T: SomeNumber](): auto {.compiletime.} = TVec3[T]([T(0), T(0), T(1)]) +func ConstR[T: SomeNumber](): auto {.compiletime.} = TVec4[T]([T(1), T(0), T(0), T(1)]) +func ConstG[T: SomeNumber](): auto {.compiletime.} = TVec4[T]([T(0), T(1), T(0), T(1)]) +func ConstB[T: SomeNumber](): auto {.compiletime.} = TVec4[T]([T(0), T(0), T(1), T(1)]) func NewVec2f*(x = 0'f32, y = 0'f32): auto = Vec2f([x, y]) @@ -87,6 +87,9 @@ const X* = ConstX[float32]() const Y* = ConstY[float32]() const Z* = ConstZ[float32]() +const R* = ConstR[float32]() +const G* = ConstG[float32]() +const B* = ConstB[float32]() const One1* = ConstOne1[float32]() const One2* = ConstOne2[float32]() const One3* = ConstOne3[float32]() diff -r c4f98eb4bb05 -r b0f4c8ccd49a semiconginev2/gltf.nim --- a/semiconginev2/gltf.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/semiconginev2/gltf.nim Sat Jul 27 20:47:54 2024 +0700 @@ -74,7 +74,8 @@ 6: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN, ] -proc getBufferViewData(bufferView: JsonNode, mainBuffer: seq[uint8], baseBufferOffset = 0): seq[uint8] = +proc getBufferViewData(bufferView: JsonNode, mainBuffer: seq[uint8], + baseBufferOffset = 0): seq[uint8] = assert bufferView["buffer"].getInt() == 0, "Currently no external buffers supported" result = newSeq[uint8](bufferView["byteLength"].getInt()) @@ -93,12 +94,14 @@ 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 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 + assert componentTypeId(elementType(default(T))) == componentType, name(T) & + " != " & $componentType else: assert componentTypeId(T) == componentType, name(T) & " != " & $componentType @@ -121,7 +124,8 @@ 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 accessorOffset = if accessor.hasKey("byteOffset"): accessor[ + "byteOffset"].getInt() else: 0 let length = bufferView["byteLength"].getInt() let bufferOffset = bufferView["byteOffset"].getInt() + accessorOffset var dstPointer = result.ToCPointer() @@ -130,12 +134,14 @@ 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) -proc loadTexture(root: JsonNode, textureNode: JsonNode, mainBuffer: seq[uint8]): Image[BGRA] = +proc loadTexture(root: JsonNode, textureNode: JsonNode, mainBuffer: seq[ + uint8]): Image[BGRA] = let imageIndex = textureNode["source"].getInt() @@ -144,22 +150,26 @@ let imageType = root["images"][imageIndex]["mimeType"].getStr() assert imageType == "image/png", "glTF loader currently only supports PNG" - let bufferView = root["bufferViews"][root["images"][imageIndex]["bufferView"].getInt()] + let bufferView = root["bufferViews"][root["images"][imageIndex][ + "bufferView"].getInt()] result = LoadImageData[BGRA](getBufferViewData(bufferView, mainBuffer)) if textureNode.hasKey("sampler"): let sampler = root["samplers"][textureNode["sampler"].getInt()] if sampler.hasKey("magFilter"): - result.magInterpolation = SAMPLER_FILTER_MODE_MAP[sampler["magFilter"].getInt()] + result.magInterpolation = SAMPLER_FILTER_MODE_MAP[sampler[ + "magFilter"].getInt()] if sampler.hasKey("minFilter"): - result.minInterpolation = SAMPLER_FILTER_MODE_MAP[sampler["minFilter"].getInt()] + result.minInterpolation = SAMPLER_FILTER_MODE_MAP[sampler[ + "minFilter"].getInt()] if sampler.hasKey("wrapS"): result.wrapU = SAMPLER_WRAP_MODE_MAP[sampler["wrapS"].getInt()] if sampler.hasKey("wrapT"): result.wrapV = SAMPLER_WRAP_MODE_MAP[sampler["wrapT"].getInt()] proc getVec4f(node: JsonNode): Vec4f = - NewVec4f(node[0].getFloat(), node[1].getFloat(), node[2].getFloat(), node[3].getFloat()) + NewVec4f(node[0].getFloat(), node[1].getFloat(), node[2].getFloat(), node[ + 3].getFloat()) proc loadMaterial[TMaterial]( root: JsonNode, @@ -204,14 +214,17 @@ 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()) + 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) + resultValue.data = getAccessorData[elementType(resultValue.data)]( + root, root["accessors"][accessor], mainBuffer) else: var i = 0 for mappedIndexName in mappedName: @@ -219,8 +232,10 @@ 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) + let accessor = primitive["attributes"][ + gltfAttributeIndexed].getInt() + resultValue.data = getAccessorData[elementType(resultValue.data)]( + root, root["accessors"][accessor], mainBuffer) inc i proc loadNode(node: JsonNode): GltfNode = @@ -282,30 +297,35 @@ chunkLength = stream.readUint32() assert stream.readUint32() == BINARY_CHUNK data.binaryBufferData.setLen(chunkLength) - assert stream.readData(addr data.binaryBufferData[0], int(chunkLength)) == int(chunkLength) + assert stream.readData(addr data.binaryBufferData[0], int(chunkLength)) == + int(chunkLength) # check that the refered buffer is the same as the binary chunk # 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 if "materials" in data.structuredContent: for materialnode in items(data.structuredContent["materials"]): - result.materials.add loadMaterial[TMaterial](data.structuredContent, materialnode, materialAttributesMapping, data.binaryBufferData) + result.materials.add loadMaterial[TMaterial](data.structuredContent, + materialnode, materialAttributesMapping, data.binaryBufferData) if "textures" in data.structuredContent: for texturenode in items(data.structuredContent["textures"]): - result.textures.add loadTexture(data.structuredContent, texturenode, data.binaryBufferData) + result.textures.add loadTexture(data.structuredContent, texturenode, + data.binaryBufferData) if "meshes" in data.structuredContent: for mesh in items(data.structuredContent["meshes"]): var primitives: seq[(TMesh, VkPrimitiveTopology)] for primitive in items(mesh["primitives"]): - primitives.add loadPrimitive[TMesh](data.structuredContent, primitive, meshAttributesMapping, data.binaryBufferData) + primitives.add loadPrimitive[TMesh](data.structuredContent, primitive, + meshAttributesMapping, data.binaryBufferData) result.meshes.add primitives if "nodes" in data.structuredContent: diff -r c4f98eb4bb05 -r b0f4c8ccd49a semiconginev2/input.nim --- a/semiconginev2/input.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/semiconginev2/input.nim Sat Jul 27 20:47:54 2024 +0700 @@ -11,6 +11,7 @@ mouseWheel: float32 windowWasResized: bool = true windowIsMinimized: bool = false + lockMouse: bool = false # warning, shit is not thread safe var input: Input @@ -25,6 +26,9 @@ input.mouseMove = NewVec2f() input.windowWasResized = false + if input.lockMouse: + SetMousePosition(vulkan.window, x=int(vulkan.swapchain.width div 2), y=int(vulkan.swapchain.height div 2)) + var killed = false for event in vulkan.window.PendingEvents(): case event.eventType: @@ -72,10 +76,12 @@ proc MousePositionNormalized*(size: (int, int)): Vec2f = result.x = (input.mousePosition.x / float32(size[0])) * 2.0 - 1.0 result.y = (input.mousePosition.y / float32(size[1])) * 2.0 - 1.0 -proc MouseMove*(): auto = input.mouseMove -proc MouseWheel*(): auto = input.mouseWheel +proc MouseMove*(): Vec2f = input.mouseMove +proc MouseWheel*(): float32 = input.mouseWheel proc WindowWasResized*(): auto = input.windowWasResized proc WindowIsMinimized*(): auto = input.windowIsMinimized +proc LockMouse*(value: bool) = input.lockMouse = value + # actions as a slight abstraction over raw input diff -r c4f98eb4bb05 -r b0f4c8ccd49a semiconginev2/rendering/platform/linux.nim --- a/semiconginev2/rendering/platform/linux.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/semiconginev2/rendering/platform/linux.nim Sat Jul 27 20:47:54 2024 +0700 @@ -204,6 +204,16 @@ if onscreen != 0: result = some(Vec2f([float32(winX), float32(winY)])) +proc SetMousePosition*(window: NativeWindow, x, y: int) = + checkXlibResult XWarpPointer( + window.display, + default(x11.Window), + window.window, + 0, 0, 0, 0, + x.cint, + y.cint, + ) + checkXlibResult XSync(window.display, false.XBool) proc CreateNativeSurface(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( diff -r c4f98eb4bb05 -r b0f4c8ccd49a semiconginev2/rendering/platform/windows.nim --- a/semiconginev2/rendering/platform/windows.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/semiconginev2/rendering/platform/windows.nim Sat Jul 27 20:47:54 2024 +0700 @@ -193,6 +193,8 @@ return some(Vec2f([float32(p.x), float32(p.y)])) return none(Vec2f) +proc SetMousePosition*(window: NativeWindow, x, y: int) = + CheckWin32Result SetCursorPos(x, y) proc CreateNativeSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = assert instance.Valid diff -r c4f98eb4bb05 -r b0f4c8ccd49a semiconginev2/rendering/renderer.nim --- a/semiconginev2/rendering/renderer.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/semiconginev2/rendering/renderer.nim Sat Jul 27 20:47:54 2024 +0700 @@ -533,51 +533,97 @@ for image in value.mitems: renderdata.createVulkanImage(image) -proc HasGPUValueField[T](name: static string): bool {.compileTime.} = - for fieldname, value in default(T).fieldPairs(): - when typeof(value) is GPUValue and fieldname == name: return true - return false +type EMPTY = object # only used for static assertions + +proc assertAllDescriptorsBound(A, B, C, D, TShader: typedesc) = + var foundDescriptorSets = false + for attrName, attrValue in default(TShader).fieldPairs(): + when hasCustomPragma(attrValue, DescriptorSets): + assert not foundDescriptorSets, "Only one shader attribute is allowed to have the pragma 'DescriptorSets'" + when not (A is EMPTY): assert typeof(attrValue[0]) is A + when not (B is EMPTY): assert typeof(attrValue[1]) is B + when not (C is EMPTY): assert typeof(attrValue[2]) is C + when not (D is EMPTY): assert typeof(attrValue[3]) is D + +var hasBoundDescriptorSets {.compileTime.} = false # okay, I am not sure if this is clean, unproblematic or sane. Just trying to get some comptime-validation +var hasDescriptorSets {.compileTime} = false -template WithGPUValueField(obj: object, name: static string, fieldvalue, body: untyped): untyped = - # HasGPUValueField MUST be used to check if this is supported - for fieldname, value in obj.fieldPairs(): - when fieldname == name: - block: - let `fieldvalue` {.inject.} = value - body - -template WithBind*[A, B, C, D](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C], DescriptorSet[D]), pipeline: Pipeline, body: untyped): untyped = +template WithBind*[A, B, C, D, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C], DescriptorSet[D]), pipeline: Pipeline[TShader], body: untyped): untyped = + static: assertAllDescriptorsBound(A, B, C, D, TShader) + block: + var descriptorSets: seq[VkDescriptorSet] + for dSet in sets.fields: + assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet" + descriptorSets.add dSet.vk[currentFiF()] + svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout) + static: + assert not hasBoundDescriptorSets, "Cannot call WithBind nested" + hasBoundDescriptorSets = true + body + static: + hasBoundDescriptorSets = false +template WithBind*[A, B, C, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C]), pipeline: Pipeline[TShader], body: untyped): untyped = + static: assertAllDescriptorsBound(A, B, C, EMPTY, TShader) block: var descriptorSets: seq[VkDescriptorSet] for dSet in sets.fields: assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet" descriptorSets.add dSet.vk[currentFiF()] svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout) + static: + assert not hasBoundDescriptorSets, "Cannot call WithBind nested" + hasBoundDescriptorSets = true body -template WithBind*[A, B, C](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B], DescriptorSet[C]), pipeline: Pipeline, body: untyped): untyped = + static: + hasBoundDescriptorSets = false +template WithBind*[A, B, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B]), pipeline: Pipeline[TShader], body: untyped): untyped = + static: assertAllDescriptorsBound(A, B, EMPTY, EMPTY, TShader) block: var descriptorSets: seq[VkDescriptorSet] for dSet in sets.fields: assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet" descriptorSets.add dSet.vk[currentFiF()] svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout) + static: + assert not hasBoundDescriptorSets, "Cannot call WithBind nested" + hasBoundDescriptorSets = true body -template WithBind*[A, B](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], DescriptorSet[B]), pipeline: Pipeline, body: untyped): untyped = + static: + hasBoundDescriptorSets = false +template WithBind*[A, TShader](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], ), pipeline: Pipeline[TShader], body: untyped): untyped = + static: assertAllDescriptorsBound(A, EMPTY, EMPTY, EMPTY, TShader) block: var descriptorSets: seq[VkDescriptorSet] for dSet in sets.fields: assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet" descriptorSets.add dSet.vk[currentFiF()] svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout) + static: + assert not hasBoundDescriptorSets, "Cannot call WithBind nested" + hasBoundDescriptorSets = true body -template WithBind*[A](commandBuffer: VkCommandBuffer, sets: (DescriptorSet[A], ), pipeline: Pipeline, body: untyped): untyped = - block: - var descriptorSets: seq[VkDescriptorSet] - for dSet in sets.fields: - assert dSet.vk[currentFiF()].Valid, "DescriptorSet not initialized, maybe forgot to call InitDescriptorSet" - descriptorSets.add dSet.vk[currentFiF()] - svkCmdBindDescriptorSets(commandBuffer, descriptorSets, pipeline.layout) - body + static: + hasBoundDescriptorSets = false + +proc assertCanRenderMesh(TShader, TMesh, TInstance: typedesc) = + for attrName, attrValue in default(TShader).fieldPairs: + if hasCustomPragma(attrValue, VertexAttribute): + var foundAttr = false + for meshAttrName, meshAttrValue in default(TMesh).fieldPairs: + if 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 + assert foundAttr, "Attribute '" & attrName & "' is not provided in mesh type '" & name(TMesh) & "'" + if hasCustomPragma(attrValue, InstanceAttribute): + var foundAttr = false + for instAttrName, instAttrValue in default(TInstance).fieldPairs: + if attrName == instAttrName: + assert typeof(instAttrValue) is GPUArray, "Instance attribute '" & attrName & "' must be a GPUArray" + assert foundAttr == false, "Attribute '" & attrName & "' is defined in Mesh and Instance, can only be one" + assert typeof(attrValue) is elementType(instAttrValue.data), "Type of shader attribute and mesh attribute '" & attrName & "' is not the same" + foundAttr = true + assert foundAttr, "Attribute '" & attrName & "' is not provided in instance type '" & name(TInstance) & "'" proc Render*[TShader, TMesh, TInstance]( commandBuffer: VkCommandBuffer, @@ -586,6 +632,15 @@ instances: TInstance, ) = + static: assertCanRenderMesh(TShader, TMesh, TInstance) + static: + hasDescriptorSets = false + for attrName, attrValue in default(TShader).fieldPairs(): + if attrValue.hasCustomPragma(DescriptorSets): + hasDescriptorSets = true + if hasDescriptorSets: + assert hasBoundDescriptorSets, "Shader uses descriptor sets, but none are bound" + var vertexBuffers: seq[VkBuffer] var vertexBuffersOffsets: seq[uint64] var elementCount = 0'u32 @@ -661,8 +716,6 @@ firstInstance = 0 ) -type EMPTY = object - proc Render*[TShader, TMesh]( commandBuffer: VkCommandBuffer, pipeline: Pipeline[TShader], diff -r c4f98eb4bb05 -r b0f4c8ccd49a tests/test_gltf.nim --- a/tests/test_gltf.nim Fri Jul 26 23:39:24 2024 +0700 +++ b/tests/test_gltf.nim Sat Jul 27 20:47:54 2024 +0700 @@ -14,8 +14,7 @@ ObjectData = object transform: Mat4 Camera = object - view: Mat4 - perspective: Mat4 + viewPerspective: Mat4 Material = object color: Vec4f = NewVec4f(1, 1, 1, 1) colorTexture: int32 = -1 @@ -33,7 +32,8 @@ Shader = object objectData {.PushConstantAttribute.}: ObjectData position {.VertexAttribute.}: Vec3f - uv {.VertexAttribute.}: Vec2f + color {.VertexAttribute.}: Vec4f + # uv {.VertexAttribute.}: Vec2f fragmentColor {.Pass.}: Vec4f fragmentUv {.Pass.}: Vec2f outColor {.ShaderOutput.}: Vec4f @@ -41,21 +41,26 @@ # code vertexCode: string = """ void main() { - fragmentColor = vec4(1, 1, 1, 1); - fragmentUv = uv; - gl_Position = vec4(position, 1) * camera.perspective * camera.view; + fragmentColor = color; + // fragmentUv = uv; + gl_Position = vec4(position, 1) * camera.viewPerspective; }""" fragmentCode: string = """void main() { outColor = fragmentColor;}""" Mesh = object position: GPUArray[Vec3f, VertexBuffer] + color: GPUArray[Vec4f, VertexBuffer] uv: GPUArray[Vec2f, VertexBuffer] + DebugMesh = object + position: GPUArray[Vec3f, VertexBuffer] + color: GPUArray[Vec4f, VertexBuffer] var gltfData = LoadMeshes[Mesh, Material]( "town.glb", MeshAttributeNames( POSITION: "position", - TEXCOORD: @["uv"], - ), + COLOR: @["color"], + TEXCOORD: @["uv"], + ), MaterialAttributeNames( baseColorFactor: "color", baseColorTexture: "colorTexture", @@ -71,31 +76,86 @@ var descriptors = asDescriptorSet( MainDescriptors( camera: asGPUValue(Camera( - view: Unit4, - perspective: Unit4, - ), UniformBufferMapped) - ) + viewPerspective: Unit4, + ), UniformBufferMapped) + ) ) 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) - var pipeline = CreatePipeline[Shader](renderPass = vulkan.swapchain.renderPass) + 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) 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 = Unit4) = let nodeTransform = gltfData.nodes[nodeId].transform * transform if gltfData.nodes[nodeId].mesh >= 0: for primitive in gltfData.meshes[gltfData.nodes[nodeId].mesh]: - RenderWithPushConstant(commandbuffer = commandbuffer, pipeline = pipeline, mesh = primitive[0], pushConstant = ObjectData(transform: nodeTransform)) + RenderWithPushConstant( + commandbuffer = commandbuffer, + pipeline = pipeline, + mesh = primitive[0], + pushConstant = ObjectData(transform: nodeTransform) + ) for childNode in gltfData.nodes[nodeId].children: drawNode(commandbuffer = commandbuffer, pipeline = pipeline, nodeId = childNode, transform = nodeTransform) + var camPos: Vec3f + var camYaw: float32 + var camPitch: float32 + + discard UpdateInputs() # clear inputs, otherwise MouseMove will have stuff var start = getMonoTime() - while ((getMonoTime() - start).inMilliseconds().int / 1000) < time: + var lastT = getMonoTime() + while ((getMonoTime() - start).inMilliseconds().int / 1000) < time and UpdateInputs(): + let dt = ((getMonoTime() - lastT).inNanoseconds().int / 1_000_000_000).float32 + lastT = getMonoTime() + + camYaw -= MouseMove().x / 1000 + camPitch -= MouseMove().y / 1000 + var + forward = 0'f32 + sideward = 0'f32 + if KeyIsDown(W): forward += 2 + if KeyIsDown(S): forward -= 2 + 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 + camPos += camDir * forward * dt + camPos += camDirSide * sideward * dt + + descriptors.data.camera.data.viewPerspective = ( + Perspective(PI/3, aspect = GetAspectRatio(), zNear = 0.001, zFar = 100) * + Rotate(-camPitch, X) * Rotate(-camYaw, Y) * Translate(-camPos) + ) + + UpdateGPUBuffer(descriptors.data.camera) WithNextFrame(framebuffer, commandbuffer): @@ -105,17 +165,22 @@ 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) # cleanup checkVkResult vkDeviceWaitIdle(vulkan.device) DestroyPipeline(pipeline) + DestroyPipeline(debugpipeline) DestroyRenderData(renderdata) when isMainModule: - var time = 5'f32 + var time = 1000'f32 InitVulkan() var renderpass = CreateDirectPresentationRenderPass(depthBuffer = true, samples = VK_SAMPLE_COUNT_4_BIT) SetupSwapchain(renderpass = renderpass) + LockMouse(true) # tests a simple triangle with minimalistic shader and vertex format test_gltf(time)