Mercurial > games > semicongine
changeset 478:871ee602bf95
add: vertex basics, some refactoring
author | Sam <sam@basx.dev> |
---|---|
date | Sun, 01 Jan 2023 01:00:50 +0700 |
parents | f226c99b5043 |
children | 16842d15319a |
files | notes src/engine.nim src/math/matrix.nim src/math/vector.nim src/matrix.nim src/notes src/shader.nim src/vector.nim src/vertex.nim src/vulkan_helpers.nim |
diffstat | 10 files changed, 877 insertions(+), 640 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/notes Sun Jan 01 01:00:50 2023 +0700 @@ -0,0 +1,9 @@ +For implementation of font rendering: +https://developer.apple.com/fonts/TrueType-Reference-Manual/ + +ideas: +- mining-game with structure simulation, crashing mineshafts, etc. +- top-down 2d shooter (wild west?) with one room per scene, fixed camera +- Top-down 2d shooter with autoshoot (-> what is the challenge? position? cover? effects?) +- Clean up something +- Defend house (embassy?), against burglar, enemies, receive guests
--- a/src/engine.nim Fri Dec 30 15:56:17 2022 +0700 +++ b/src/engine.nim Sun Jan 01 01:00:50 2023 +0700 @@ -8,6 +8,9 @@ import ./vulkan_helpers import ./window import ./events +import ./math/vector +import ./shader +import ./vertex import ./glslang/glslang @@ -18,24 +21,56 @@ addHandler(logger) -var vertexShaderCode: string = """#version 450 +type + MyVertex = object + position: VertexAttribute[Vec2[float32]] + color: VertexAttribute[Vec3[float32]] + +const vertices = [ + (Vec2([ 0.0'f32, -0.5'f32]), Vec3([1.0'f32, 0.0'f32, 0.0'f32])), + (Vec2([ 0.5'f32, 0.5'f32]), Vec3([0.0'f32, 1.0'f32, 0.0'f32])), + (Vec2([-0.5'f32, 0.5'f32]), Vec3([0.0'f32, 0.0'f32, 1.0'f32])) +] + + +proc getBindingDescription(binding: int): auto = + VkVertexInputBindingDescription( + binding: uint32(binding), + stride: uint32(sizeof(vertices[0])), + inputRate: VK_VERTEX_INPUT_RATE_VERTEX, # VK_VERTEX_INPUT_RATE_INSTANCE for instances + ) + +proc getAttributeDescriptions(binding: int): auto = + [ + VkVertexInputAttributeDescription( + binding: 0'u32, + location: 0, + format: VK_FORMAT_R32G32_SFLOAT, + offset: 0, + ), + VkVertexInputAttributeDescription( + binding: 0'u32, + location: 1, + format: VK_FORMAT_R32G32B32_SFLOAT, + offset: uint32(sizeof(Vec2)), # use offsetOf? + ), + ] + +var vertexShaderCode = """ +#version 450 + +layout(location = 0) in vec2 inPosition; +layout(location = 1) in vec3 inColor; + layout(location = 0) out vec3 fragColor; -vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), - vec3(0.0, 1.0, 0.0), - vec3(0.0, 0.0, 1.0) -); -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); + void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); - fragColor = colors[gl_VertexIndex]; -}""" + gl_Position = vec4(inPosition, 0.0, 1.0); + fragColor = inColor; +} +""" -var fragmentShaderCode: string = """#version 450 +var fragmentShaderCode = """#version 450 layout(location = 0) out vec4 outColor; layout(location = 0) in vec3 fragColor; void main() { @@ -57,7 +92,7 @@ images: seq[VkImage] imageviews: seq[VkImageView] RenderPipeline = object - shaderStages*: seq[VkPipelineShaderStageCreateInfo] + shaders*: seq[ShaderProgram] layout*: VkPipelineLayout pipeline*: VkPipeline QueueFamily = object @@ -265,11 +300,9 @@ checkVkResult device.vkCreateRenderPass(addr(renderPassCreateInfo), nil, addr(result)) proc setupRenderPipeline(device: VkDevice, frameDimension: VkExtent2D, renderPass: VkRenderPass): RenderPipeline = - # (seq[VkPipelineShaderStageCreateInfo], VkViewport, VkRect2D, VkPipelineLayout, VkPipeline) = - # load shaders - result.shaderStages.add(device.createShaderStage(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode)) - result.shaderStages.add(device.createShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode)) + result.shaders.add(device.initShaderProgram(VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode)) + result.shaders.add(device.initShaderProgram(VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode)) var # define which parts can be dynamic (pipeline is fixed after setup) @@ -279,13 +312,16 @@ dynamicStateCount: uint32(dynamicStates.len), pDynamicStates: addr(dynamicStates[0]), ) + vertexbindings = generateInputVertexBinding[MyVertex]() + attributebindings = generateInputAttributeBinding[MyVertex]() + # define input data format vertexInputInfo = VkPipelineVertexInputStateCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - vertexBindingDescriptionCount: 0, - pVertexBindingDescriptions: nil, - vertexAttributeDescriptionCount: 0, - pVertexAttributeDescriptions: nil, + vertexBindingDescriptionCount: uint32(vertexbindings.len), + pVertexBindingDescriptions: addr(vertexbindings[0]), + vertexAttributeDescriptionCount: uint32(attributebindings.len), + pVertexAttributeDescriptions: addr(attributebindings[0]), ) inputAssembly = VkPipelineInputAssemblyStateCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, @@ -358,10 +394,13 @@ ) checkVkResult device.vkCreatePipelineLayout(addr(pipelineLayoutInfo), nil, addr(result.layout)) + var stages: seq[VkPipelineShaderStageCreateInfo] + for shader in result.shaders: + stages.add(shader.shader) var pipelineInfo = VkGraphicsPipelineCreateInfo( sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - stageCount: 2, - pStages: addr(result.shaderStages[0]), + stageCount: uint32(stages.len), + pStages: addr(stages[0]), pVertexInputState: addr(vertexInputInfo), pInputAssemblyState: addr(inputAssembly), pViewportState: addr(viewportState), @@ -633,8 +672,8 @@ engine.vulkan.device.device.vkDestroyPipelineLayout(engine.vulkan.pipeline.layout, nil) engine.vulkan.device.device.vkDestroyRenderPass(engine.vulkan.renderPass, nil) - for shaderStage in engine.vulkan.pipeline.shaderStages: - engine.vulkan.device.device.vkDestroyShaderModule(shaderStage.module, nil) + for shader in engine.vulkan.pipeline.shaders: + engine.vulkan.device.device.vkDestroyShaderModule(shader.shader.module, nil) engine.vulkan.instance.vkDestroySurfaceKHR(engine.vulkan.surface, nil) engine.vulkan.device.device.vkDestroyDevice(nil)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/math/matrix.nim Sun Jan 01 01:00:50 2023 +0700 @@ -0,0 +1,357 @@ +import std/math +import std/macros +import std/random +import std/strutils +import std/typetraits + +import ./vector + +type + # layout is row-first + # having an object instead of directly aliasing the array seems a bit ugly at + # first, but is necessary to be able to work correctly with distinguished + # types (i.e. Mat23 and Mat32 would be an alias for the same type array[6, T] + # which prevents the type system from identifying the correct type at times) + # + # Though, great news is that objects have zero overhead! + Mat22*[T: SomeNumber] = object + data: array[4, T] + Mat23*[T: SomeNumber] = object + data: array[6, T] + Mat32*[T: SomeNumber] = object + data: array[6, T] + Mat33*[T: SomeNumber] = object + data: array[9, T] + Mat34*[T: SomeNumber] = object + data: array[12, T] + Mat43*[T: SomeNumber] = object + data: array[12, T] + Mat44*[T: SomeNumber] = object + data: array[16, T] + MatMM* = Mat22|Mat33|Mat44 + MatMN* = Mat23|Mat32|Mat34|Mat43 + Mat* = MatMM|MatMN + IntegerMat = Mat22[SomeInteger]|Mat33[SomeInteger]|Mat44[SomeInteger]|Mat23[SomeInteger]|Mat32[SomeInteger]|Mat34[SomeInteger]|Mat43[SomeInteger] + FloatMat = Mat22[SomeFloat]|Mat33[SomeFloat]|Mat44[SomeFloat]|Mat23[SomeFloat]|Mat32[SomeFloat]|Mat34[SomeFloat]|Mat43[SomeFloat] + +func unit22[T: SomeNumber](): auto {.compiletime.} = Mat22[T](data:[ + T(1), T(0), + T(0), T(1), +]) +func unit33[T: SomeNumber](): auto {.compiletime.} = Mat33[T](data:[ + T(1), T(0), T(0), + T(0), T(1), T(0), + T(0), T(0), T(1), +]) +func unit44[T: SomeNumber](): auto {.compiletime.} = Mat44[T](data: [ + T(1), T(0), T(0), T(0), + T(0), T(1), T(0), T(0), + T(0), T(0), T(1), T(0), + T(0), T(0), T(0), T(1), +]) + +# generates constants: Unit +# Also for Y, Z, R, G, B +# not sure if this is necessary or even a good idea... +macro generateAllConsts() = + result = newStmtList() + for theType in ["int", "int8", "int16", "int32", "int64", "float", "float32", "float64"]: + var typename = theType[0 .. 0] + if theType[^2].isDigit: + typename = typename & theType[^2] + if theType[^1].isDigit: + typename = typename & theType[^1] + result.add(newConstStmt( + postfix(ident("Unit22" & typename), "*"), + newCall(nnkBracketExpr.newTree(ident("unit22"), ident(theType))) + )) + result.add(newConstStmt( + postfix(ident("Unit33" & typename), "*"), + newCall(nnkBracketExpr.newTree(ident("unit33"), ident(theType))) + )) + result.add(newConstStmt( + postfix(ident("Unit44" & typename), "*"), + newCall(nnkBracketExpr.newTree(ident("unit44"), ident(theType))) + )) + +generateAllConsts() + +const Unit22* = unit22[float]() +const Unit33* = unit33[float]() +const Unit44* = unit44[float]() + +template rowCount*(m: typedesc): int = + when m is Mat22: 2 + elif m is Mat23: 2 + elif m is Mat32: 3 + elif m is Mat33: 3 + elif m is Mat34: 3 + elif m is Mat43: 4 + elif m is Mat44: 4 +template columnCount*(m: typedesc): int = + when m is Mat22: 2 + elif m is Mat23: 3 + elif m is Mat32: 2 + elif m is Mat33: 3 + elif m is Mat34: 4 + elif m is Mat43: 3 + elif m is Mat44: 4 + + +func toString[T](value: T): string = + var + strvalues: seq[string] + maxwidth = 0 + + for n in value.data: + let strval = $n + strvalues.add(strval) + if strval.len > maxwidth: + maxwidth = strval.len + + for i in 0 ..< strvalues.len: + let filler = " ".repeat(maxwidth - strvalues[i].len) + if i mod T.columnCount == T.columnCount - 1: + result &= filler & strvalues[i] & "\n" + else: + if i mod T.columnCount == 0: + result &= " " + result &= filler & strvalues[i] & " " + result = $T & "\n" & result + +func `$`*(v: Mat22[SomeNumber]): string = toString[Mat22[SomeNumber]](v) +func `$`*(v: Mat23[SomeNumber]): string = toString[Mat23[SomeNumber]](v) +func `$`*(v: Mat32[SomeNumber]): string = toString[Mat32[SomeNumber]](v) +func `$`*(v: Mat33[SomeNumber]): string = toString[Mat33[SomeNumber]](v) +func `$`*(v: Mat34[SomeNumber]): string = toString[Mat34[SomeNumber]](v) +func `$`*(v: Mat43[SomeNumber]): string = toString[Mat43[SomeNumber]](v) +func `$`*(v: Mat44[SomeNumber]): string = toString[Mat44[SomeNumber]](v) + +func `[]`*[T: Mat](m: T, row, col: int): auto = m.data[col + row * T.columnCount] +proc `[]=`*[T: Mat, U](m: var T, row, col: int, value: U) = m.data[col + row * T.columnCount] = value + +func row*[T: Mat22](m: T, i: 0..1): auto = Vec2([m[i, 0], m[i, 1]]) +func row*[T: Mat32](m: T, i: 0..2): auto = Vec2([m[i, 0], m[i, 1]]) +func row*[T: Mat23](m: T, i: 0..1): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]]) +func row*[T: Mat33](m: T, i: 0..2): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]]) +func row*[T: Mat43](m: T, i: 0..3): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]]) +func row*[T: Mat34](m: T, i: 0..2): auto = Vec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]]) +func row*[T: Mat44](m: T, i: 0..3): auto = Vec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]]) + +func col*[T: Mat22](m: T, i: 0..1): auto = Vec2([m[0, i], m[1, i]]) +func col*[T: Mat23](m: T, i: 0..2): auto = Vec2([m[0, i], m[1, i]]) +func col*[T: Mat32](m: T, i: 0..1): auto = Vec3([m[0, i], m[1, i], m[2, i]]) +func col*[T: Mat33](m: T, i: 0..2): auto = Vec3([m[0, i], m[1, i], m[2, i]]) +func col*[T: Mat34](m: T, i: 0..3): auto = Vec3([m[0, i], m[1, i], m[2, i]]) +func col*[T: Mat43](m: T, i: 0..2): auto = Vec4([m[0, i], m[1, i], m[2, i], m[3, i]]) +func col*[T: Mat44](m: T, i: 0..3): auto = Vec4([m[0, i], m[1, i], m[2, i], m[3, i]]) + +proc createMatMatMultiplicationOperator(leftType: typedesc, rightType: typedesc, outType: typedesc): NimNode = + var data = nnkBracket.newTree() + for i in 0 ..< rowCount(leftType): + for j in 0 ..< rightType.columnCount: + data.add(newCall( + ident("sum"), + infix( + newCall(newDotExpr(ident("a"), ident("row")), newLit(i)), + "*", + newCall(newDotExpr(ident("b"), ident("col")), newLit(j)) + ) + )) + + return newProc( + postfix(nnkAccQuoted.newTree(ident("*")), "*"), + params=[ + ident("auto"), + newIdentDefs(ident("a"), ident(leftType.name)), + newIdentDefs(ident("b"), ident(rightType.name)) + ], + body=nnkObjConstr.newTree(ident(outType.name), nnkExprColonExpr.newTree(ident("data"), data)), + procType=nnkFuncDef, + ) + +proc createVecMatMultiplicationOperator(matType: typedesc, vecType: typedesc): NimNode = + var data = nnkBracket.newTree() + for i in 0 ..< matType.rowCount: + data.add(newCall( + ident("sum"), + infix( + ident("v"), + "*", + newCall(newDotExpr(ident("m"), ident("row")), newLit(i)) + ) + )) + + let resultVec = newCall( + nnkBracketExpr.newTree(ident(vecType.name), ident("T")), + data, + ) + let name = postfix(nnkAccQuoted.newTree(ident("*")), "*") + let genericParams = nnkGenericParams.newTree(nnkIdentDefs.newTree(ident("T"), ident("SomeNumber"), newEmptyNode())) + let formalParams = nnkFormalParams.newTree( + ident("auto"), + newIdentDefs(ident("m"), nnkBracketExpr.newTree(ident(matType.name), ident("T"))), + newIdentDefs(ident("v"), nnkBracketExpr.newTree(ident(vecType.name), ident("T"))), + ) + + return nnkFuncDef.newTree( + name, + newEmptyNode(), + genericParams, + formalParams, + newEmptyNode(), + newEmptyNode(), + resultVec + ) + +proc createVecMatMultiplicationOperator1(vecType: typedesc, matType: typedesc): NimNode = + var data = nnkBracket.newTree() + for i in 0 ..< matType.columnCount: + data.add(newCall( + ident("sum"), + infix( + ident("v"), + "*", + newCall(newDotExpr(ident("m"), ident("col")), newLit(i)) + ) + )) + let resultVec = nnkObjConstr.newTree( + nnkBracketExpr.newTree(ident(vecType.name), ident("float")), + nnkExprColonExpr.newTree(ident("data"), data) + ) + + return nnkFuncDef.newTree( + ident("test"), + newEmptyNode(), + newEmptyNode(), + newEmptyNode(), + newEmptyNode(), + newEmptyNode(), + resultVec, + ) + +proc createMatScalarOperator(matType: typedesc, op: string): NimNode = + result = newStmtList() + + var data = nnkBracket.newTree() + for i in 0 ..< matType.rowCount * matType.columnCount: + data.add(infix(nnkBracketExpr.newTree(newDotExpr(ident("a"), ident("data")), newLit(i)), op, ident("b"))) + result.add(newProc( + postfix(nnkAccQuoted.newTree(ident(op)), "*"), + params=[ + ident("auto"), + newIdentDefs(ident("a"), ident(matType.name)), + newIdentDefs(ident("b"), ident("SomeNumber")), + ], + body=nnkObjConstr.newTree(ident(matType.name), nnkExprColonExpr.newTree(ident("data"), data)), + procType=nnkFuncDef, + )) + result.add(newProc( + postfix(nnkAccQuoted.newTree(ident(op)), "*"), + params=[ + ident("auto"), + newIdentDefs(ident("b"), ident("SomeNumber")), + newIdentDefs(ident("a"), ident(matType.name)), + ], + body=nnkObjConstr.newTree(ident(matType.name), nnkExprColonExpr.newTree(ident("data"), data)), + procType=nnkFuncDef, + )) + if op == "-": + var data2 = nnkBracket.newTree() + for i in 0 ..< matType.rowCount * matType.columnCount: + data2.add(prefix(nnkBracketExpr.newTree(newDotExpr(ident("a"), ident("data")), newLit(i)), op)) + result.add(newProc( + postfix(nnkAccQuoted.newTree(ident(op)), "*"), + params=[ + ident("auto"), + newIdentDefs(ident("a"), ident(matType.name)), + ], + body=nnkObjConstr.newTree(ident(matType.name), nnkExprColonExpr.newTree(ident("data"), data2)), + procType=nnkFuncDef, + )) + +macro createAllMultiplicationOperators() = + result = newStmtList() + + for op in ["+", "-", "*", "/"]: + result.add(createMatScalarOperator(Mat22, op)) + result.add(createMatScalarOperator(Mat23, op)) + result.add(createMatScalarOperator(Mat32, op)) + result.add(createMatScalarOperator(Mat33, op)) + result.add(createMatScalarOperator(Mat34, op)) + result.add(createMatScalarOperator(Mat43, op)) + result.add(createMatScalarOperator(Mat44, op)) + + result.add(createMatMatMultiplicationOperator(Mat22, Mat22, Mat22)) + result.add(createMatMatMultiplicationOperator(Mat22, Mat23, Mat23)) + result.add(createMatMatMultiplicationOperator(Mat23, Mat32, Mat22)) + result.add(createMatMatMultiplicationOperator(Mat23, Mat33, Mat23)) + result.add(createMatMatMultiplicationOperator(Mat32, Mat22, Mat32)) + result.add(createMatMatMultiplicationOperator(Mat32, Mat23, Mat33)) + result.add(createMatMatMultiplicationOperator(Mat33, Mat32, Mat32)) + result.add(createMatMatMultiplicationOperator(Mat33, Mat33, Mat33)) + result.add(createMatMatMultiplicationOperator(Mat33, Mat34, Mat34)) + result.add(createMatMatMultiplicationOperator(Mat43, Mat33, Mat43)) + result.add(createMatMatMultiplicationOperator(Mat43, Mat34, Mat44)) + result.add(createMatMatMultiplicationOperator(Mat44, Mat43, Mat43)) + result.add(createMatMatMultiplicationOperator(Mat44, Mat44, Mat44)) + + result.add(createVecMatMultiplicationOperator(Mat22, Vec2)) + result.add(createVecMatMultiplicationOperator(Mat33, Vec3)) + result.add(createVecMatMultiplicationOperator(Mat44, Vec4)) + +createAllMultiplicationOperators() + + +func transposed*[T](m: Mat22[T]): Mat22[T] = Mat22[T](data: [ + m[0, 0], m[1, 0], + m[0, 1], m[1, 1], +]) +func transposed*[T](m: Mat23[T]): Mat32[T] = Mat32[T](data: [ + m[0, 0], m[1, 0], + m[0, 1], m[1, 1], + m[0, 2], m[1, 2], +]) +func transposed*[T](m: Mat32[T]): Mat23[T] = Mat23[T](data: [ + m[0, 0], m[1, 0], m[2, 0], + m[0, 1], m[1, 1], m[2, 1], +]) +func transposed*[T](m: Mat33[T]): Mat33[T] = Mat33[T](data: [ + m[0, 0], m[1, 0], m[2, 0], + m[0, 1], m[1, 1], m[2, 1], + m[0, 2], m[1, 2], m[2, 2], +]) +func transposed*[T](m: Mat43[T]): Mat34[T] = Mat34[T](data: [ + m[0, 0], m[1, 0], m[2, 0], m[3, 0], + m[0, 1], m[1, 1], m[2, 1], m[3, 1], + m[0, 2], m[1, 2], m[2, 2], m[3, 2], +]) +func transposed*[T](m: Mat34[T]): Mat43[T] = Mat43[T](data: [ + m[0, 0], m[1, 0], m[2, 0], + m[0, 1], m[1, 1], m[2, 1], + m[0, 2], m[1, 2], m[2, 2], + m[0, 3], m[1, 3], m[2, 3], +]) +func transposed*[T](m: Mat44[T]): Mat44[T] = Mat44[T](data: [ + m[0, 0], m[1, 0], m[2, 0], m[3, 0], + m[0, 1], m[1, 1], m[2, 1], m[3, 1], + m[0, 2], m[1, 2], m[2, 2], m[3, 2], + m[0, 3], m[1, 3], m[2, 3], m[3, 3], +]) + +# call e.g. Mat32[int]().randomized() to get a random matrix +template makeRandomInit(mattype: typedesc) = + proc randomized*[T: SomeInteger](m: mattype[T]): mattype[T] = + for i in 0 ..< result.data.len: + result.data[i] = rand(low(typeof(m.data[0])) .. high(typeof(m.data[0]))) + proc randomized*[T: SomeFloat](m: mattype[T]): mattype[T] = + for i in 0 ..< result.data.len: + result.data[i] = rand(1.0) + +makeRandomInit(Mat22) +makeRandomInit(Mat23) +makeRandomInit(Mat32) +makeRandomInit(Mat33) +makeRandomInit(Mat34) +makeRandomInit(Mat43) +makeRandomInit(Mat44)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/math/vector.nim Sun Jan 01 01:00:50 2023 +0700 @@ -0,0 +1,223 @@ +import std/random +import std/math +import std/strutils +import std/macros +import std/typetraits +import std/tables + + +type + Vec2*[T: SomeNumber] = array[2, T] + Vec3*[T: SomeNumber] = array[3, T] + Vec4*[T: SomeNumber] = array[4, T] + Vec* = Vec2|Vec3|Vec4 + +# define some often used constants +func ConstOne2[T: SomeNumber](): auto {.compiletime.} = Vec2[T]([T(1), T(1)]) +func ConstOne3[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(1), T(1)]) +func ConstOne4[T: SomeNumber](): auto {.compiletime.} = Vec4[T]([T(1), T(1), T(1), T(1)]) +func ConstX[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(0), T(0)]) +func ConstY[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(1), T(0)]) +func ConstZ[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(0), T(1)]) +func ConstR[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(0), T(0)]) +func ConstG[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(1), T(0)]) +func ConstB[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(0), T(1)]) + +# generates constants: Xf, Xf32, Xf64, Xi, Xi8, Xi16, Xi32, Xi64 +# Also for Y, Z, R, G, B and One +# not sure if this is necessary or even a good idea... +macro generateAllConsts() = + result = newStmtList() + for component in ["X", "Y", "Z", "R", "G", "B", "One2", "One3", "One4"]: + for theType in ["int", "int8", "int16", "int32", "int64", "float", "float32", "float64"]: + var typename = theType[0 .. 0] + if theType[^2].isDigit: + typename = typename & theType[^2] + if theType[^1].isDigit: + typename = typename & theType[^1] + result.add( + newConstStmt( + postfix(ident(component & typename), "*"), + newCall(nnkBracketExpr.newTree(ident("Const" & component), ident(theType))) + ) + ) + +generateAllConsts() + +const X* = ConstX[float]() +const Y* = ConstY[float]() +const Z* = ConstZ[float]() +const One2* = ConstOne2[float]() +const One3* = ConstOne3[float]() +const One4* = ConstOne4[float]() + +func newVec2*[T](x, y: T): auto = Vec2([x, y]) +func newVec3*[T](x, y, z: T): auto = Vec3([x, y, z]) +func newVec4*[T](x, y, z, w: T): auto = Vec4([x, y, z, w]) + +func to*[T](v: Vec2): auto = Vec2([T(v[0]), T(v[1])]) +func to*[T](v: Vec3): auto = Vec3([T(v[0]), T(v[1]), T(v[2])]) +func to*[T](v: Vec4): auto = Vec4([T(v[0]), T(v[1]), T(v[2]), T(v[3])]) + +func toString[T](value: T): string = + var items: seq[string] + for item in value: + items.add($item) + $T & "(" & join(items, " ") & ")" + +func `$`*(v: Vec2[SomeNumber]): string = toString[Vec2[SomeNumber]](v) +func `$`*(v: Vec3[SomeNumber]): string = toString[Vec3[SomeNumber]](v) +func `$`*(v: Vec4[SomeNumber]): string = toString[Vec4[SomeNumber]](v) + +func length*(vec: Vec2[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1]) +func length*(vec: Vec2[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1])) +func length*(vec: Vec3[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]) +func length*(vec: Vec3[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])) +func length*(vec: Vec4[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3]) +func length*(vec: Vec4[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3])) + +func normalized*[T](vec: Vec2[T]): auto = + let l = vec.length + when T is SomeFloat: + Vec2[T]([vec[0] / l, vec[1] / l]) + else: + Vec2[float]([float(vec[0]) / l, float(vec[1]) / l]) +func normalized*[T](vec: Vec3[T]): auto = + let l = vec.length + when T is SomeFloat: + Vec3[T]([vec[0] / l, vec[1] / l, vec[2] / l]) + else: + Vec3[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l]) +func normalized*[T](vec: Vec4[T]): auto = + let l = vec.length + when T is SomeFloat: + Vec4[T]([vec[0] / l, vec[1] / l, vec[2] / l, vec[3] / l]) + else: + Vec4[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l, float(vec[3]) / l]) + +# scalar operations +func `+`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] + b, a[1] + b]) +func `+`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] + b, a[1] + b, a[2] + b]) +func `+`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]) +func `-`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] - b, a[1] - b]) +func `-`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] - b, a[1] - b, a[2] - b]) +func `-`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b]) +func `*`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] * b, a[1] * b]) +func `*`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] * b, a[1] * b, a[2] * b]) +func `*`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]) +func `/`*[T: SomeInteger](a: Vec2[T], b: SomeInteger): auto = Vec2([a[0] div b, a[1] div b]) +func `/`*[T: SomeFloat](a: Vec2[T], b: SomeFloat): auto = Vec2([a[0] / b, a[1] / b]) +func `/`*[T: SomeInteger](a: Vec3[T], b: SomeInteger): auto = Vec3([a[0] div b, a[1] div b, a[2] div b]) +func `/`*[T: SomeFloat](a: Vec3[T], b: SomeFloat): auto = Vec3([a[0] / b, a[1] / b, a[2] / b]) +func `/`*[T: SomeInteger](a: Vec4[T], b: SomeInteger): auto = Vec4([a[0] div b, a[1] div b, a[2] div b, a[3] div b]) +func `/`*[T: SomeFloat](a: Vec4[T], b: SomeFloat): auto = Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]) + +func `+`*(a: SomeNumber, b: Vec2): auto = Vec2([a + b[0], a + b[1]]) +func `+`*(a: SomeNumber, b: Vec3): auto = Vec3([a + b[0], a + b[1], a + b[2]]) +func `+`*(a: SomeNumber, b: Vec4): auto = Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]) +func `-`*(a: SomeNumber, b: Vec2): auto = Vec2([a - b[0], a - b[1]]) +func `-`*(a: SomeNumber, b: Vec3): auto = Vec3([a - b[0], a - b[1], a - b[2]]) +func `-`*(a: SomeNumber, b: Vec4): auto = Vec4([a - b[0], a - b[1], a - b[2], a - b[3]]) +func `*`*(a: SomeNumber, b: Vec2): auto = Vec2([a * b[0], a * b[1]]) +func `*`*(a: SomeNumber, b: Vec3): auto = Vec3([a * b[0], a * b[1], a * b[2]]) +func `*`*(a: SomeNumber, b: Vec4): auto = Vec4([a * b[0], a * b[1], a * b[2], a * b[3]]) +func `/`*[T: SomeInteger](a: SomeInteger, b: Vec2[T]): auto = Vec2([a div b[0], a div b[1]]) +func `/`*[T: SomeFloat](a: SomeFloat, b: Vec2[T]): auto = Vec2([a / b[0], a / b[1]]) +func `/`*[T: SomeInteger](a: SomeInteger, b: Vec3[T]): auto = Vec3([a div b[0], a div b[1], a div b[2]]) +func `/`*[T: SomeFloat](a: SomeFloat, b: Vec3[T]): auto = Vec3([a / b[0], a / b[1], a / b[2]]) +func `/`*[T: SomeInteger](a: SomeInteger, b: Vec4[T]): auto = Vec4([a div b[0], a div b[1], a div b[2], a div b[3]]) +func `/`*[T: SomeFloat](a: SomeFloat, b: Vec4[T]): auto = Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]) + +# compontent-wise operations +func `+`*(a, b: Vec2): auto = Vec2([a[0] + b[0], a[1] + b[1]]) +func `+`*(a, b: Vec3): auto = Vec3([a[0] + b[0], a[1] + b[1], a[2] + b[2]]) +func `+`*(a, b: Vec4): auto = Vec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]]) +func `-`*(a: Vec2): auto = Vec2([-a[0], -a[1]]) +func `-`*(a: Vec3): auto = Vec3([-a[0], -a[1], -a[2]]) +func `-`*(a: Vec4): auto = Vec4([-a[0], -a[1], -a[2], -a[3]]) +func `-`*(a, b: Vec2): auto = Vec2([a[0] - b[0], a[1] - b[1]]) +func `-`*(a, b: Vec3): auto = Vec3([a[0] - b[0], a[1] - b[1], a[2] - b[2]]) +func `-`*(a, b: Vec4): auto = Vec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]]) +func `*`*(a, b: Vec2): auto = Vec2([a[0] * b[0], a[1] * b[1]]) +func `*`*(a, b: Vec3): auto = Vec3([a[0] * b[0], a[1] * b[1], a[2] * b[2]]) +func `*`*(a, b: Vec4): auto = Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]) +func `/`*[T: SomeInteger](a, b: Vec2[T]): auto = Vec2([a[0] div b[0], a[1] div b[1]]) +func `/`*[T: SomeFloat](a, b: Vec2[T]): auto = Vec2([a[0] / b[0], a[1] / b[1]]) +func `/`*[T: SomeInteger](a, b: Vec3[T]): auto = Vec3([a[0] div b[0], a[1] div b[1], a[2] div b[2]]) +func `/`*[T: SomeFloat](a, b: Vec3[T]): auto = Vec3([a[0] / b[0], a[1] / b[1], a[2] / b[2]]) +func `/`*[T: SomeInteger](a, b: Vec4[T]): auto = Vec4([a[0] div b[0], a[1] div b[1], a[2] div b[2], a[3] div b[3]]) +func `/`*[T: SomeFloat](a, b: Vec4[T]): auto = Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]) + +# special operations +func dot*(a, b: Vec2): auto = a[0] * b[0] + a[1] * b[1] +func dot*(a, b: Vec3): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] +func dot*(a, b: Vec4): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3] +func cross*(a, b: Vec3): auto = Vec3([ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], +]) + + +# macro to allow creation of new vectors by specifying vector components as attributes +# e.g. myVec.xxy will return a new Vec3 that contains the components x, x an y of the original vector +# (instead of x, y, z for a simple copy) +proc vectorAttributeAccessor(accessor: string): NimNode = + const ACCESSOR_INDICES = { + 'x': 0, + 'y': 1, + 'z': 2, + 'w': 3, + 'r': 0, + 'g': 1, + 'b': 2, + 'a': 3, + }.toTable + var ret: NimNode + let accessorvalue = accessor + + if accessorvalue.len == 0: + raise newException(Exception, "empty attribute") + elif accessorvalue.len == 1: + ret = nnkBracket.newTree(ident("value"), newLit(ACCESSOR_INDICES[accessorvalue[0]])) + if accessorvalue.len > 1: + var attrs = nnkBracket.newTree() + for attrname in accessorvalue: + attrs.add(nnkBracketExpr.newTree(ident("value"), newLit(ACCESSOR_INDICES[attrname]))) + ret = nnkCall.newTree(ident("Vec" & $accessorvalue.len), attrs) + + newProc( + name=nnkPostfix.newTree(ident("*"), ident(accessor)), + params=[ident("auto"), nnkIdentDefs.newTree(ident("value"), ident("Vec"), newEmptyNode())], + body=newStmtList(ret), + procType = nnkFuncDef, + ) + +macro createVectorAttribAccessorFuncs() = + const COORD_ATTRS = ["x", "y", "z", "w"] + const COLOR_ATTRS = ["r", "g", "b", "a"] + result = nnkStmtList.newTree() + for attlist in [COORD_ATTRS, COLOR_ATTRS]: + for i in attlist: + result.add(vectorAttributeAccessor(i)) + for j in attlist: + result.add(vectorAttributeAccessor(i & j)) + for k in attlist: + result.add(vectorAttributeAccessor(i & j & k)) + for l in attlist: + result.add(vectorAttributeAccessor(i & j & k & l)) + +createVectorAttribAccessorFuncs() + +# call e.g. Vec2[int]().randomized() to get a random matrix +template makeRandomInit(mattype: typedesc) = + proc randomized*[T: SomeInteger](m: mattype[T]): mattype[T] = + for i in 0 ..< result.len: + result[i] = rand(low(typeof(m[0])) .. high(typeof(m[0]))) + proc randomized*[T: SomeFloat](m: mattype[T]): mattype[T] = + for i in 0 ..< result.len: + result[i] = rand(1.0) + +makeRandomInit(Vec2) +makeRandomInit(Vec3) +makeRandomInit(Vec4)
--- a/src/matrix.nim Fri Dec 30 15:56:17 2022 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,357 +0,0 @@ -import std/math -import std/macros -import std/random -import std/strutils -import std/typetraits - -import ./vector - -type - # layout is row-first - # having an object instead of directly aliasing the array seems a bit ugly at - # first, but is necessary to be able to work correctly with distinguished - # types (i.e. Mat23 and Mat32 would be an alias for the same type array[6, T] - # which prevents the type system from identifying the correct type at times) - # - # Though, great news is that objects have zero overhead! - Mat22*[T: SomeNumber] = object - data: array[4, T] - Mat23*[T: SomeNumber] = object - data: array[6, T] - Mat32*[T: SomeNumber] = object - data: array[6, T] - Mat33*[T: SomeNumber] = object - data: array[9, T] - Mat34*[T: SomeNumber] = object - data: array[12, T] - Mat43*[T: SomeNumber] = object - data: array[12, T] - Mat44*[T: SomeNumber] = object - data: array[16, T] - MatMM* = Mat22|Mat33|Mat44 - MatMN* = Mat23|Mat32|Mat34|Mat43 - Mat* = MatMM|MatMN - IntegerMat = Mat22[SomeInteger]|Mat33[SomeInteger]|Mat44[SomeInteger]|Mat23[SomeInteger]|Mat32[SomeInteger]|Mat34[SomeInteger]|Mat43[SomeInteger] - FloatMat = Mat22[SomeFloat]|Mat33[SomeFloat]|Mat44[SomeFloat]|Mat23[SomeFloat]|Mat32[SomeFloat]|Mat34[SomeFloat]|Mat43[SomeFloat] - -func unit22[T: SomeNumber](): auto {.compiletime.} = Mat22[T](data:[ - T(1), T(0), - T(0), T(1), -]) -func unit33[T: SomeNumber](): auto {.compiletime.} = Mat33[T](data:[ - T(1), T(0), T(0), - T(0), T(1), T(0), - T(0), T(0), T(1), -]) -func unit44[T: SomeNumber](): auto {.compiletime.} = Mat44[T](data: [ - T(1), T(0), T(0), T(0), - T(0), T(1), T(0), T(0), - T(0), T(0), T(1), T(0), - T(0), T(0), T(0), T(1), -]) - -# generates constants: Unit -# Also for Y, Z, R, G, B -# not sure if this is necessary or even a good idea... -macro generateAllConsts() = - result = newStmtList() - for theType in ["int", "int8", "int16", "int32", "int64", "float", "float32", "float64"]: - var typename = theType[0 .. 0] - if theType[^2].isDigit: - typename = typename & theType[^2] - if theType[^1].isDigit: - typename = typename & theType[^1] - result.add(newConstStmt( - postfix(ident("Unit22" & typename), "*"), - newCall(nnkBracketExpr.newTree(ident("unit22"), ident(theType))) - )) - result.add(newConstStmt( - postfix(ident("Unit33" & typename), "*"), - newCall(nnkBracketExpr.newTree(ident("unit33"), ident(theType))) - )) - result.add(newConstStmt( - postfix(ident("Unit44" & typename), "*"), - newCall(nnkBracketExpr.newTree(ident("unit44"), ident(theType))) - )) - -generateAllConsts() - -const Unit22* = unit22[float]() -const Unit33* = unit33[float]() -const Unit44* = unit44[float]() - -template rowCount*(m: typedesc): int = - when m is Mat22: 2 - elif m is Mat23: 2 - elif m is Mat32: 3 - elif m is Mat33: 3 - elif m is Mat34: 3 - elif m is Mat43: 4 - elif m is Mat44: 4 -template columnCount*(m: typedesc): int = - when m is Mat22: 2 - elif m is Mat23: 3 - elif m is Mat32: 2 - elif m is Mat33: 3 - elif m is Mat34: 4 - elif m is Mat43: 3 - elif m is Mat44: 4 - - -func toString[T](value: T): string = - var - strvalues: seq[string] - maxwidth = 0 - - for n in value.data: - let strval = $n - strvalues.add(strval) - if strval.len > maxwidth: - maxwidth = strval.len - - for i in 0 ..< strvalues.len: - let filler = " ".repeat(maxwidth - strvalues[i].len) - if i mod T.columnCount == T.columnCount - 1: - result &= filler & strvalues[i] & "\n" - else: - if i mod T.columnCount == 0: - result &= " " - result &= filler & strvalues[i] & " " - result = $T & "\n" & result - -func `$`*(v: Mat22[SomeNumber]): string = toString[Mat22[SomeNumber]](v) -func `$`*(v: Mat23[SomeNumber]): string = toString[Mat23[SomeNumber]](v) -func `$`*(v: Mat32[SomeNumber]): string = toString[Mat32[SomeNumber]](v) -func `$`*(v: Mat33[SomeNumber]): string = toString[Mat33[SomeNumber]](v) -func `$`*(v: Mat34[SomeNumber]): string = toString[Mat34[SomeNumber]](v) -func `$`*(v: Mat43[SomeNumber]): string = toString[Mat43[SomeNumber]](v) -func `$`*(v: Mat44[SomeNumber]): string = toString[Mat44[SomeNumber]](v) - -func `[]`*[T: Mat](m: T, row, col: int): auto = m.data[col + row * T.columnCount] -proc `[]=`*[T: Mat, U](m: var T, row, col: int, value: U) = m.data[col + row * T.columnCount] = value - -func row*[T: Mat22](m: T, i: 0..1): auto = Vec2([m[i, 0], m[i, 1]]) -func row*[T: Mat32](m: T, i: 0..2): auto = Vec2([m[i, 0], m[i, 1]]) -func row*[T: Mat23](m: T, i: 0..1): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]]) -func row*[T: Mat33](m: T, i: 0..2): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]]) -func row*[T: Mat43](m: T, i: 0..3): auto = Vec3([m[i, 0], m[i, 1], m[i, 2]]) -func row*[T: Mat34](m: T, i: 0..2): auto = Vec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]]) -func row*[T: Mat44](m: T, i: 0..3): auto = Vec4([m[i, 0], m[i, 1], m[i, 2], m[i, 3]]) - -func col*[T: Mat22](m: T, i: 0..1): auto = Vec2([m[0, i], m[1, i]]) -func col*[T: Mat23](m: T, i: 0..2): auto = Vec2([m[0, i], m[1, i]]) -func col*[T: Mat32](m: T, i: 0..1): auto = Vec3([m[0, i], m[1, i], m[2, i]]) -func col*[T: Mat33](m: T, i: 0..2): auto = Vec3([m[0, i], m[1, i], m[2, i]]) -func col*[T: Mat34](m: T, i: 0..3): auto = Vec3([m[0, i], m[1, i], m[2, i]]) -func col*[T: Mat43](m: T, i: 0..2): auto = Vec4([m[0, i], m[1, i], m[2, i], m[3, i]]) -func col*[T: Mat44](m: T, i: 0..3): auto = Vec4([m[0, i], m[1, i], m[2, i], m[3, i]]) - -proc createMatMatMultiplicationOperator(leftType: typedesc, rightType: typedesc, outType: typedesc): NimNode = - var data = nnkBracket.newTree() - for i in 0 ..< rowCount(leftType): - for j in 0 ..< rightType.columnCount: - data.add(newCall( - ident("sum"), - infix( - newCall(newDotExpr(ident("a"), ident("row")), newLit(i)), - "*", - newCall(newDotExpr(ident("b"), ident("col")), newLit(j)) - ) - )) - - return newProc( - postfix(nnkAccQuoted.newTree(ident("*")), "*"), - params=[ - ident("auto"), - newIdentDefs(ident("a"), ident(leftType.name)), - newIdentDefs(ident("b"), ident(rightType.name)) - ], - body=nnkObjConstr.newTree(ident(outType.name), nnkExprColonExpr.newTree(ident("data"), data)), - procType=nnkFuncDef, - ) - -proc createVecMatMultiplicationOperator(matType: typedesc, vecType: typedesc): NimNode = - var data = nnkBracket.newTree() - for i in 0 ..< matType.rowCount: - data.add(newCall( - ident("sum"), - infix( - ident("v"), - "*", - newCall(newDotExpr(ident("m"), ident("row")), newLit(i)) - ) - )) - - let resultVec = newCall( - nnkBracketExpr.newTree(ident(vecType.name), ident("T")), - data, - ) - let name = postfix(nnkAccQuoted.newTree(ident("*")), "*") - let genericParams = nnkGenericParams.newTree(nnkIdentDefs.newTree(ident("T"), ident("SomeNumber"), newEmptyNode())) - let formalParams = nnkFormalParams.newTree( - ident("auto"), - newIdentDefs(ident("m"), nnkBracketExpr.newTree(ident(matType.name), ident("T"))), - newIdentDefs(ident("v"), nnkBracketExpr.newTree(ident(vecType.name), ident("T"))), - ) - - return nnkFuncDef.newTree( - name, - newEmptyNode(), - genericParams, - formalParams, - newEmptyNode(), - newEmptyNode(), - resultVec - ) - -proc createVecMatMultiplicationOperator1(vecType: typedesc, matType: typedesc): NimNode = - var data = nnkBracket.newTree() - for i in 0 ..< matType.columnCount: - data.add(newCall( - ident("sum"), - infix( - ident("v"), - "*", - newCall(newDotExpr(ident("m"), ident("col")), newLit(i)) - ) - )) - let resultVec = nnkObjConstr.newTree( - nnkBracketExpr.newTree(ident(vecType.name), ident("float")), - nnkExprColonExpr.newTree(ident("data"), data) - ) - - return nnkFuncDef.newTree( - ident("test"), - newEmptyNode(), - newEmptyNode(), - newEmptyNode(), - newEmptyNode(), - newEmptyNode(), - resultVec, - ) - -proc createMatScalarOperator(matType: typedesc, op: string): NimNode = - result = newStmtList() - - var data = nnkBracket.newTree() - for i in 0 ..< matType.rowCount * matType.columnCount: - data.add(infix(nnkBracketExpr.newTree(newDotExpr(ident("a"), ident("data")), newLit(i)), op, ident("b"))) - result.add(newProc( - postfix(nnkAccQuoted.newTree(ident(op)), "*"), - params=[ - ident("auto"), - newIdentDefs(ident("a"), ident(matType.name)), - newIdentDefs(ident("b"), ident("SomeNumber")), - ], - body=nnkObjConstr.newTree(ident(matType.name), nnkExprColonExpr.newTree(ident("data"), data)), - procType=nnkFuncDef, - )) - result.add(newProc( - postfix(nnkAccQuoted.newTree(ident(op)), "*"), - params=[ - ident("auto"), - newIdentDefs(ident("b"), ident("SomeNumber")), - newIdentDefs(ident("a"), ident(matType.name)), - ], - body=nnkObjConstr.newTree(ident(matType.name), nnkExprColonExpr.newTree(ident("data"), data)), - procType=nnkFuncDef, - )) - if op == "-": - var data2 = nnkBracket.newTree() - for i in 0 ..< matType.rowCount * matType.columnCount: - data2.add(prefix(nnkBracketExpr.newTree(newDotExpr(ident("a"), ident("data")), newLit(i)), op)) - result.add(newProc( - postfix(nnkAccQuoted.newTree(ident(op)), "*"), - params=[ - ident("auto"), - newIdentDefs(ident("a"), ident(matType.name)), - ], - body=nnkObjConstr.newTree(ident(matType.name), nnkExprColonExpr.newTree(ident("data"), data2)), - procType=nnkFuncDef, - )) - -macro createAllMultiplicationOperators() = - result = newStmtList() - - for op in ["+", "-", "*", "/"]: - result.add(createMatScalarOperator(Mat22, op)) - result.add(createMatScalarOperator(Mat23, op)) - result.add(createMatScalarOperator(Mat32, op)) - result.add(createMatScalarOperator(Mat33, op)) - result.add(createMatScalarOperator(Mat34, op)) - result.add(createMatScalarOperator(Mat43, op)) - result.add(createMatScalarOperator(Mat44, op)) - - result.add(createMatMatMultiplicationOperator(Mat22, Mat22, Mat22)) - result.add(createMatMatMultiplicationOperator(Mat22, Mat23, Mat23)) - result.add(createMatMatMultiplicationOperator(Mat23, Mat32, Mat22)) - result.add(createMatMatMultiplicationOperator(Mat23, Mat33, Mat23)) - result.add(createMatMatMultiplicationOperator(Mat32, Mat22, Mat32)) - result.add(createMatMatMultiplicationOperator(Mat32, Mat23, Mat33)) - result.add(createMatMatMultiplicationOperator(Mat33, Mat32, Mat32)) - result.add(createMatMatMultiplicationOperator(Mat33, Mat33, Mat33)) - result.add(createMatMatMultiplicationOperator(Mat33, Mat34, Mat34)) - result.add(createMatMatMultiplicationOperator(Mat43, Mat33, Mat43)) - result.add(createMatMatMultiplicationOperator(Mat43, Mat34, Mat44)) - result.add(createMatMatMultiplicationOperator(Mat44, Mat43, Mat43)) - result.add(createMatMatMultiplicationOperator(Mat44, Mat44, Mat44)) - - result.add(createVecMatMultiplicationOperator(Mat22, Vec2)) - result.add(createVecMatMultiplicationOperator(Mat33, Vec3)) - result.add(createVecMatMultiplicationOperator(Mat44, Vec4)) - -createAllMultiplicationOperators() - - -func transposed*[T](m: Mat22[T]): Mat22[T] = Mat22[T](data: [ - m[0, 0], m[1, 0], - m[0, 1], m[1, 1], -]) -func transposed*[T](m: Mat23[T]): Mat32[T] = Mat32[T](data: [ - m[0, 0], m[1, 0], - m[0, 1], m[1, 1], - m[0, 2], m[1, 2], -]) -func transposed*[T](m: Mat32[T]): Mat23[T] = Mat23[T](data: [ - m[0, 0], m[1, 0], m[2, 0], - m[0, 1], m[1, 1], m[2, 1], -]) -func transposed*[T](m: Mat33[T]): Mat33[T] = Mat33[T](data: [ - m[0, 0], m[1, 0], m[2, 0], - m[0, 1], m[1, 1], m[2, 1], - m[0, 2], m[1, 2], m[2, 2], -]) -func transposed*[T](m: Mat43[T]): Mat34[T] = Mat34[T](data: [ - m[0, 0], m[1, 0], m[2, 0], m[3, 0], - m[0, 1], m[1, 1], m[2, 1], m[3, 1], - m[0, 2], m[1, 2], m[2, 2], m[3, 2], -]) -func transposed*[T](m: Mat34[T]): Mat43[T] = Mat43[T](data: [ - m[0, 0], m[1, 0], m[2, 0], - m[0, 1], m[1, 1], m[2, 1], - m[0, 2], m[1, 2], m[2, 2], - m[0, 3], m[1, 3], m[2, 3], -]) -func transposed*[T](m: Mat44[T]): Mat44[T] = Mat44[T](data: [ - m[0, 0], m[1, 0], m[2, 0], m[3, 0], - m[0, 1], m[1, 1], m[2, 1], m[3, 1], - m[0, 2], m[1, 2], m[2, 2], m[3, 2], - m[0, 3], m[1, 3], m[2, 3], m[3, 3], -]) - -# call e.g. Mat32[int]().randomized() to get a random matrix -template makeRandomInit(mattype: typedesc) = - proc randomized*[T: SomeInteger](m: mattype[T]): mattype[T] = - for i in 0 ..< result.data.len: - result.data[i] = rand(low(typeof(m.data[0])) .. high(typeof(m.data[0]))) - proc randomized*[T: SomeFloat](m: mattype[T]): mattype[T] = - for i in 0 ..< result.data.len: - result.data[i] = rand(1.0) - -makeRandomInit(Mat22) -makeRandomInit(Mat23) -makeRandomInit(Mat32) -makeRandomInit(Mat33) -makeRandomInit(Mat34) -makeRandomInit(Mat43) -makeRandomInit(Mat44)
--- a/src/notes Fri Dec 30 15:56:17 2022 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -For implementation of font rendering: -https://developer.apple.com/fonts/TrueType-Reference-Manual/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/shader.nim Sun Jan 01 01:00:50 2023 +0700 @@ -0,0 +1,35 @@ +import std/tables + +import ./vulkan_helpers +import ./vulkan +import ./glslang/glslang + +type + ShaderProgram* = object + entryPoint*: string + programType*: VkShaderStageFlagBits + shader*: VkPipelineShaderStageCreateInfo + +proc initShaderProgram*(device: VkDevice, programType: VkShaderStageFlagBits, shader: string, entryPoint: string="main"): ShaderProgram = + result.entryPoint = entryPoint + result.programType = programType + + const VK_GLSL_MAP = { + VK_SHADER_STAGE_VERTEX_BIT: GLSLANG_STAGE_VERTEX, + VK_SHADER_STAGE_FRAGMENT_BIT: GLSLANG_STAGE_FRAGMENT, + }.toTable() + var code = compileGLSLToSPIRV(VK_GLSL_MAP[result.programType], shader, "<memory-shader>") + var createInfo = VkShaderModuleCreateInfo( + sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + codeSize: uint(code.len * sizeof(uint32)), + pCode: if code.len > 0: addr(code[0]) else: nil, + ) + var shaderModule: VkShaderModule + checkVkResult vkCreateShaderModule(device, addr(createInfo), nil, addr(shaderModule)) + + result.shader = VkPipelineShaderStageCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: programType, + module: shaderModule, + pName: cstring(result.entryPoint), # entry point for shader + )
--- a/src/vector.nim Fri Dec 30 15:56:17 2022 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,223 +0,0 @@ -import std/random -import std/math -import std/strutils -import std/macros -import std/typetraits -import std/tables - - -type - Vec2*[T: SomeNumber] = array[2, T] - Vec3*[T: SomeNumber] = array[3, T] - Vec4*[T: SomeNumber] = array[4, T] - Vec* = Vec2|Vec3|Vec4 - -# define some often used constants -func ConstOne2[T: SomeNumber](): auto {.compiletime.} = Vec2[T]([T(1), T(1)]) -func ConstOne3[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(1), T(1)]) -func ConstOne4[T: SomeNumber](): auto {.compiletime.} = Vec4[T]([T(1), T(1), T(1), T(1)]) -func ConstX[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(0), T(0)]) -func ConstY[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(1), T(0)]) -func ConstZ[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(0), T(1)]) -func ConstR[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(1), T(0), T(0)]) -func ConstG[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(1), T(0)]) -func ConstB[T: SomeNumber](): auto {.compiletime.} = Vec3[T]([T(0), T(0), T(1)]) - -# generates constants: Xf, Xf32, Xf64, Xi, Xi8, Xi16, Xi32, Xi64 -# Also for Y, Z, R, G, B and One -# not sure if this is necessary or even a good idea... -macro generateAllConsts() = - result = newStmtList() - for component in ["X", "Y", "Z", "R", "G", "B", "One2", "One3", "One4"]: - for theType in ["int", "int8", "int16", "int32", "int64", "float", "float32", "float64"]: - var typename = theType[0 .. 0] - if theType[^2].isDigit: - typename = typename & theType[^2] - if theType[^1].isDigit: - typename = typename & theType[^1] - result.add( - newConstStmt( - postfix(ident(component & typename), "*"), - newCall(nnkBracketExpr.newTree(ident("Const" & component), ident(theType))) - ) - ) - -generateAllConsts() - -const X* = ConstX[float]() -const Y* = ConstY[float]() -const Z* = ConstZ[float]() -const One2* = ConstOne2[float]() -const One3* = ConstOne3[float]() -const One4* = ConstOne4[float]() - -func newVec2*[T](x, y: T): auto = Vec2([x, y]) -func newVec3*[T](x, y, z: T): auto = Vec3([x, y, z]) -func newVec4*[T](x, y, z, w: T): auto = Vec4([x, y, z, w]) - -func to*[T](v: Vec2): auto = Vec2([T(v[0]), T(v[1])]) -func to*[T](v: Vec3): auto = Vec3([T(v[0]), T(v[1]), T(v[2])]) -func to*[T](v: Vec4): auto = Vec4([T(v[0]), T(v[1]), T(v[2]), T(v[3])]) - -func toString[T](value: T): string = - var items: seq[string] - for item in value: - items.add($item) - $T & "(" & join(items, " ") & ")" - -func `$`*(v: Vec2[SomeNumber]): string = toString[Vec2[SomeNumber]](v) -func `$`*(v: Vec3[SomeNumber]): string = toString[Vec3[SomeNumber]](v) -func `$`*(v: Vec4[SomeNumber]): string = toString[Vec4[SomeNumber]](v) - -func length*(vec: Vec2[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1]) -func length*(vec: Vec2[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1])) -func length*(vec: Vec3[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]) -func length*(vec: Vec3[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2])) -func length*(vec: Vec4[SomeFloat]): auto = sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3]) -func length*(vec: Vec4[SomeInteger]): auto = sqrt(float(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2] + vec[3] * vec[3])) - -func normalized*[T](vec: Vec2[T]): auto = - let l = vec.length - when T is SomeFloat: - Vec2[T]([vec[0] / l, vec[1] / l]) - else: - Vec2[float]([float(vec[0]) / l, float(vec[1]) / l]) -func normalized*[T](vec: Vec3[T]): auto = - let l = vec.length - when T is SomeFloat: - Vec3[T]([vec[0] / l, vec[1] / l, vec[2] / l]) - else: - Vec3[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l]) -func normalized*[T](vec: Vec4[T]): auto = - let l = vec.length - when T is SomeFloat: - Vec4[T]([vec[0] / l, vec[1] / l, vec[2] / l, vec[3] / l]) - else: - Vec4[float]([float(vec[0]) / l, float(vec[1]) / l, float(vec[2]) / l, float(vec[3]) / l]) - -# scalar operations -func `+`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] + b, a[1] + b]) -func `+`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] + b, a[1] + b, a[2] + b]) -func `+`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] + b, a[1] + b, a[2] + b, a[3] + b]) -func `-`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] - b, a[1] - b]) -func `-`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] - b, a[1] - b, a[2] - b]) -func `-`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] - b, a[1] - b, a[2] - b, a[3] - b]) -func `*`*(a: Vec2, b: SomeNumber): auto = Vec2([a[0] * b, a[1] * b]) -func `*`*(a: Vec3, b: SomeNumber): auto = Vec3([a[0] * b, a[1] * b, a[2] * b]) -func `*`*(a: Vec4, b: SomeNumber): auto = Vec4([a[0] * b, a[1] * b, a[2] * b, a[3] * b]) -func `/`*[T: SomeInteger](a: Vec2[T], b: SomeInteger): auto = Vec2([a[0] div b, a[1] div b]) -func `/`*[T: SomeFloat](a: Vec2[T], b: SomeFloat): auto = Vec2([a[0] / b, a[1] / b]) -func `/`*[T: SomeInteger](a: Vec3[T], b: SomeInteger): auto = Vec3([a[0] div b, a[1] div b, a[2] div b]) -func `/`*[T: SomeFloat](a: Vec3[T], b: SomeFloat): auto = Vec3([a[0] / b, a[1] / b, a[2] / b]) -func `/`*[T: SomeInteger](a: Vec4[T], b: SomeInteger): auto = Vec4([a[0] div b, a[1] div b, a[2] div b, a[3] div b]) -func `/`*[T: SomeFloat](a: Vec4[T], b: SomeFloat): auto = Vec4([a[0] / b, a[1] / b, a[2] / b, a[3] / b]) - -func `+`*(a: SomeNumber, b: Vec2): auto = Vec2([a + b[0], a + b[1]]) -func `+`*(a: SomeNumber, b: Vec3): auto = Vec3([a + b[0], a + b[1], a + b[2]]) -func `+`*(a: SomeNumber, b: Vec4): auto = Vec4([a + b[0], a + b[1], a + b[2], a + b[3]]) -func `-`*(a: SomeNumber, b: Vec2): auto = Vec2([a - b[0], a - b[1]]) -func `-`*(a: SomeNumber, b: Vec3): auto = Vec3([a - b[0], a - b[1], a - b[2]]) -func `-`*(a: SomeNumber, b: Vec4): auto = Vec4([a - b[0], a - b[1], a - b[2], a - b[3]]) -func `*`*(a: SomeNumber, b: Vec2): auto = Vec2([a * b[0], a * b[1]]) -func `*`*(a: SomeNumber, b: Vec3): auto = Vec3([a * b[0], a * b[1], a * b[2]]) -func `*`*(a: SomeNumber, b: Vec4): auto = Vec4([a * b[0], a * b[1], a * b[2], a * b[3]]) -func `/`*[T: SomeInteger](a: SomeInteger, b: Vec2[T]): auto = Vec2([a div b[0], a div b[1]]) -func `/`*[T: SomeFloat](a: SomeFloat, b: Vec2[T]): auto = Vec2([a / b[0], a / b[1]]) -func `/`*[T: SomeInteger](a: SomeInteger, b: Vec3[T]): auto = Vec3([a div b[0], a div b[1], a div b[2]]) -func `/`*[T: SomeFloat](a: SomeFloat, b: Vec3[T]): auto = Vec3([a / b[0], a / b[1], a / b[2]]) -func `/`*[T: SomeInteger](a: SomeInteger, b: Vec4[T]): auto = Vec4([a div b[0], a div b[1], a div b[2], a div b[3]]) -func `/`*[T: SomeFloat](a: SomeFloat, b: Vec4[T]): auto = Vec4([a / b[0], a / b[1], a / b[2], a / b[3]]) - -# compontent-wise operations -func `+`*(a, b: Vec2): auto = Vec2([a[0] + b[0], a[1] + b[1]]) -func `+`*(a, b: Vec3): auto = Vec3([a[0] + b[0], a[1] + b[1], a[2] + b[2]]) -func `+`*(a, b: Vec4): auto = Vec4([a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]]) -func `-`*(a: Vec2): auto = Vec2([-a[0], -a[1]]) -func `-`*(a: Vec3): auto = Vec3([-a[0], -a[1], -a[2]]) -func `-`*(a: Vec4): auto = Vec4([-a[0], -a[1], -a[2], -a[3]]) -func `-`*(a, b: Vec2): auto = Vec2([a[0] - b[0], a[1] - b[1]]) -func `-`*(a, b: Vec3): auto = Vec3([a[0] - b[0], a[1] - b[1], a[2] - b[2]]) -func `-`*(a, b: Vec4): auto = Vec4([a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3]]) -func `*`*(a, b: Vec2): auto = Vec2([a[0] * b[0], a[1] * b[1]]) -func `*`*(a, b: Vec3): auto = Vec3([a[0] * b[0], a[1] * b[1], a[2] * b[2]]) -func `*`*(a, b: Vec4): auto = Vec4([a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]]) -func `/`*[T: SomeInteger](a, b: Vec2[T]): auto = Vec2([a[0] div b[0], a[1] div b[1]]) -func `/`*[T: SomeFloat](a, b: Vec2[T]): auto = Vec2([a[0] / b[0], a[1] / b[1]]) -func `/`*[T: SomeInteger](a, b: Vec3[T]): auto = Vec3([a[0] div b[0], a[1] div b[1], a[2] div b[2]]) -func `/`*[T: SomeFloat](a, b: Vec3[T]): auto = Vec3([a[0] / b[0], a[1] / b[1], a[2] / b[2]]) -func `/`*[T: SomeInteger](a, b: Vec4[T]): auto = Vec4([a[0] div b[0], a[1] div b[1], a[2] div b[2], a[3] div b[3]]) -func `/`*[T: SomeFloat](a, b: Vec4[T]): auto = Vec4([a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]]) - -# special operations -func dot*(a, b: Vec2): auto = a[0] * b[0] + a[1] * b[1] -func dot*(a, b: Vec3): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] -func dot*(a, b: Vec4): auto = a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3] -func cross*(a, b: Vec3): auto = Vec3([ - a[1] * b[2] - a[2] * b[1], - a[2] * b[0] - a[0] * b[2], - a[0] * b[1] - a[1] * b[0], -]) - - -# macro to allow creation of new vectors by specifying vector components as attributes -# e.g. myVec.xxy will return a new Vec3 that contains the components x, x an y of the original vector -# (instead of x, y, z for a simple copy) -proc vectorAttributeAccessor(accessor: string): NimNode = - const ACCESSOR_INDICES = { - 'x': 0, - 'y': 1, - 'z': 2, - 'w': 3, - 'r': 0, - 'g': 1, - 'b': 2, - 'a': 3, - }.toTable - var ret: NimNode - let accessorvalue = accessor - - if accessorvalue.len == 0: - raise newException(Exception, "empty attribute") - elif accessorvalue.len == 1: - ret = nnkBracket.newTree(ident("value"), newLit(ACCESSOR_INDICES[accessorvalue[0]])) - if accessorvalue.len > 1: - var attrs = nnkBracket.newTree() - for attrname in accessorvalue: - attrs.add(nnkBracketExpr.newTree(ident("value"), newLit(ACCESSOR_INDICES[attrname]))) - ret = nnkCall.newTree(ident("Vec" & $accessorvalue.len), attrs) - - newProc( - name=nnkPostfix.newTree(ident("*"), ident(accessor)), - params=[ident("auto"), nnkIdentDefs.newTree(ident("value"), ident("Vec"), newEmptyNode())], - body=newStmtList(ret), - procType = nnkFuncDef, - ) - -macro createVectorAttribAccessorFuncs() = - const COORD_ATTRS = ["x", "y", "z", "w"] - const COLOR_ATTRS = ["r", "g", "b", "a"] - result = nnkStmtList.newTree() - for attlist in [COORD_ATTRS, COLOR_ATTRS]: - for i in attlist: - result.add(vectorAttributeAccessor(i)) - for j in attlist: - result.add(vectorAttributeAccessor(i & j)) - for k in attlist: - result.add(vectorAttributeAccessor(i & j & k)) - for l in attlist: - result.add(vectorAttributeAccessor(i & j & k & l)) - -createVectorAttribAccessorFuncs() - -# call e.g. Vec2[int]().randomized() to get a random matrix -template makeRandomInit(mattype: typedesc) = - proc randomized*[T: SomeInteger](m: mattype[T]): mattype[T] = - for i in 0 ..< result.len: - result[i] = rand(low(typeof(m[0])) .. high(typeof(m[0]))) - proc randomized*[T: SomeFloat](m: mattype[T]): mattype[T] = - for i in 0 ..< result.len: - result[i] = rand(1.0) - -makeRandomInit(Vec2) -makeRandomInit(Vec3) -makeRandomInit(Vec4)
--- a/src/vertex.nim Fri Dec 30 15:56:17 2022 +0700 +++ b/src/vertex.nim Sun Jan 01 01:00:50 2023 +0700 @@ -1,5 +1,179 @@ +import std/macros +import std/strutils +import std/strformat +import std/typetraits + +import ./math/vector +import ./vulkan + type - VertexAttribute = object + VertexAttributeType = SomeNumber|Vec + VertexAttribute*[T:VertexAttributeType] = object + + +# from https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap15.html +func nLocationSlots[T: VertexAttributeType](): int = + when (T is Vec3[float64] or T is Vec3[uint64] or T is Vec4[float64] or T is Vec4[float64]): + 2 + else: + 1 + +# numbers +func getVkFormat[T: VertexAttributeType](): VkFormat = + when T is uint8: VK_FORMAT_R8_UINT + elif T is int8: VK_FORMAT_R8_SINT + elif T is uint16: VK_FORMAT_R16_UINT + elif T is int16: VK_FORMAT_R16_SINT + elif T is uint32: VK_FORMAT_R32_UINT + elif T is int32: VK_FORMAT_R32_SINT + elif T is uint64: VK_FORMAT_R64_UINT + elif T is int64: VK_FORMAT_R64_SINT + elif T is float32: VK_FORMAT_R32_SFLOAT + elif T is float64: VK_FORMAT_R64_SFLOAT + elif T is Vec2[uint8]: VK_FORMAT_R8G8_UINT + elif T is Vec2[int8]: VK_FORMAT_R8G8_SINT + elif T is Vec2[uint16]: VK_FORMAT_R16G16_UINT + elif T is Vec2[int16]: VK_FORMAT_R16G16_SINT + elif T is Vec2[uint32]: VK_FORMAT_R32G32_UINT + elif T is Vec2[int32]: VK_FORMAT_R32G32_SINT + elif T is Vec2[uint64]: VK_FORMAT_R64G64_UINT + elif T is Vec2[int64]: VK_FORMAT_R64G64_SINT + elif T is Vec2[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is Vec2[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is Vec3[uint8]: VK_FORMAT_R8G8B8_UINT + elif T is Vec3[int8]: VK_FORMAT_R8G8B8_SINT + elif T is Vec3[uint16]: VK_FORMAT_R16G16B16_UINT + elif T is Vec3[int16]: VK_FORMAT_R16G16B16_SINT + elif T is Vec3[uint32]: VK_FORMAT_R32G32B32_UINT + elif T is Vec3[int32]: VK_FORMAT_R32G32B32_SINT + elif T is Vec3[uint64]: VK_FORMAT_R64G64B64_UINT + elif T is Vec3[int64]: VK_FORMAT_R64G64B64_SINT + elif T is Vec3[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is Vec3[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is Vec4[uint8]: VK_FORMAT_R8G8B8A8_UINT + elif T is Vec4[int8]: VK_FORMAT_R8G8B8A8_SINT + elif T is Vec4[uint16]: VK_FORMAT_R16G16B16A16_UINT + elif T is Vec4[int16]: VK_FORMAT_R16G16B16A16_SINT + elif T is Vec4[uint32]: VK_FORMAT_R32G32B32A32_UINT + elif T is Vec4[int32]: VK_FORMAT_R32G32B32A32_SINT + elif T is Vec4[uint64]: VK_FORMAT_R64G64B64A64_UINT + elif T is Vec4[int64]: VK_FORMAT_R64G64B64A64_SINT + elif T is Vec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is Vec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + +func getGLSLType[T: VertexAttributeType](): string = + # todo: likely not correct as we would need to enable some + # extensions somewhere (Vulkan/GLSL compiler?) to have + # everything work as intended. Or maybe the GPU driver does + # some automagic conversion stuf.. + when T is uint8: "uint" + elif T is int8: "int" + elif T is uint16: "uint" + elif T is int16: "int" + elif T is uint32: "uint" + elif T is int32: "int" + elif T is uint64: "uint" + elif T is int64: "int" + elif T is float32: "float" + elif T is float64: "double" + + elif T is Vec2[uint8]: "uvec2" + elif T is Vec2[int8]: "ivec2" + elif T is Vec2[uint16]: "uvec2" + elif T is Vec2[int16]: "ivec2" + elif T is Vec2[uint32]: "uvec2" + elif T is Vec2[int32]: "ivec2" + elif T is Vec2[uint64]: "uvec2" + elif T is Vec2[int64]: "ivec2" + elif T is Vec2[float32]: "vec2" + elif T is Vec2[float64]: "dvec2" - VertexType = object + elif T is Vec3[uint8]: "uvec3" + elif T is Vec3[int8]: "ivec3" + elif T is Vec3[uint16]: "uvec3" + elif T is Vec3[int16]: "ivec3" + elif T is Vec3[uint32]: "uvec3" + elif T is Vec3[int32]: "ivec3" + elif T is Vec3[uint64]: "uvec3" + elif T is Vec3[int64]: "ivec3" + elif T is Vec3[float32]: "vec3" + elif T is Vec3[float64]: "dvec3" + + elif T is Vec4[uint8]: "uvec4" + elif T is Vec4[int8]: "ivec4" + elif T is Vec4[uint16]: "uvec4" + elif T is Vec4[int16]: "ivec4" + elif T is Vec4[uint32]: "uvec4" + elif T is Vec4[int32]: "ivec4" + elif T is Vec4[uint64]: "uvec4" + elif T is Vec4[int64]: "ivec4" + elif T is Vec4[float32]: "vec4" + elif T is Vec4[float64]: "dvec4" + +template rawAttributeType(v: VertexAttribute): auto = get(genericParams(typeof(v)), 0) + +func generateGLSL[T](): string = + var stmtList: seq[string] + var i = 0 + for name, value in T().fieldPairs: + when typeof(value) is VertexAttribute: + let glsltype = getGLSLType[rawAttributeType(value)]() + let n = name + stmtList.add(&"layout(location = {i}) in {glsltype} {n};") + i += nLocationSlots[rawAttributeType(value)]() + + return stmtList.join("\n") +func generateInputVertexBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputBindingDescription] = + # packed attribute data, not interleaved (aks "struct of arrays") + var binding = bindingoffset + for name, value in T().fieldPairs: + when typeof(value) is VertexAttribute: + result.add( + VkVertexInputBindingDescription( + binding: uint32(binding), + stride: uint32(sizeof(rawAttributeType(value))), + inputRate: VK_VERTEX_INPUT_RATE_VERTEX, # VK_VERTEX_INPUT_RATE_INSTANCE for instances + ) + ) + binding += 1 + +func generateInputAttributeBinding*[T](bindingoffset: int = 0, locationoffset: int = 0): seq[VkVertexInputAttributeDescription] = + # packed attribute data, not interleaved (aks "struct of arrays") + var location = 0 + var binding = bindingoffset + for name, value in T().fieldPairs: + when typeof(value) is VertexAttribute: + result.add( + VkVertexInputAttributeDescription( + binding: uint32(binding), + location: uint32(location), + format: getVkFormat[rawAttributeType(value)](), + offset: 0, + ) + ) + location += nLocationSlots[rawAttributeType(value)]() + binding += 1 + +func getBindingDescription(binding: int): auto = + VkVertexInputBindingDescription( + binding: uint32(binding), + stride: 0, # either sizeof of vertex (array of structs) or of attribute (struct of arrays) + inputRate: VK_VERTEX_INPUT_RATE_VERTEX, # VK_VERTEX_INPUT_RATE_INSTANCE for instances + ) + +func getAttributeDescriptions(binding: int): auto = + [ + VkVertexInputAttributeDescription( + binding: 0'u32, + location: 0, + format: VK_FORMAT_R32G32_SFLOAT, + offset: 0, + ), + VkVertexInputAttributeDescription( + binding: 0'u32, + location: 1, + format: VK_FORMAT_R32G32B32_SFLOAT, + offset: uint32(sizeof(Vec2)), # use offsetOf? + ), + ]
--- a/src/vulkan_helpers.nim Fri Dec 30 15:56:17 2022 +0700 +++ b/src/vulkan_helpers.nim Sun Jan 01 01:00:50 2023 +0700 @@ -4,7 +4,6 @@ import std/logging import std/macros -import ./glslang/glslang import ./vulkan import ./window @@ -16,7 +15,11 @@ when defined(release): discard call else: - debug "CALLING vulkan: ", astToStr(call) + # yes, a bit cheap, but this is only for nice debug output + var callstr = astToStr(call).replace("\n", "") + while callstr.find(" ") >= 0: + callstr = callstr.replace(" ", " ") + debug "CALLING vulkan: ", callstr let value = call if value != VK_SUCCESS: error "Vulkan error: ", astToStr(call), " returned ", $value @@ -25,23 +28,23 @@ func addrOrNil[T](obj: var openArray[T]): ptr T = if obj.len > 0: addr(obj[0]) else: nil -proc VK_MAKE_API_VERSION*(variant: uint32, major: uint32, minor: uint32, patch: uint32): uint32 {.compileTime.} = +func VK_MAKE_API_VERSION*(variant: uint32, major: uint32, minor: uint32, patch: uint32): uint32 {.compileTime.} = (variant shl 29) or (major shl 22) or (minor shl 12) or patch -proc filterForSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] = +func filterForSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] = for format in formats: if format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: result.add(format) -proc getSuitableSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): VkSurfaceFormatKHR = +func getSuitableSurfaceFormat*(formats: seq[VkSurfaceFormatKHR]): VkSurfaceFormatKHR = let usableSurfaceFormats = filterForSurfaceFormat(formats) if len(usableSurfaceFormats) == 0: raise newException(Exception, "No suitable surface formats found") return usableSurfaceFormats[0] -proc cleanString*(str: openArray[char]): string = +func cleanString*(str: openArray[char]): string = for i in 0 ..< len(str): if str[i] == char(0): result = join(str[0 ..< i]) @@ -112,7 +115,7 @@ checkVkResult vkGetSwapchainImagesKHR(device, swapChain, addr(n_images), addrOrNil(result)); -proc getPresentMode*(modes: seq[VkPresentModeKHR]): VkPresentModeKHR = +func getPresentMode*(modes: seq[VkPresentModeKHR]): VkPresentModeKHR = let preferredModes = [ VK_PRESENT_MODE_MAILBOX_KHR, # triple buffering VK_PRESENT_MODE_FIFO_RELAXED_KHR, # double duffering @@ -212,27 +215,6 @@ vkGetDeviceQueue(result[0], graphicsQueueFamily, 0'u32, addr(result[1])); vkGetDeviceQueue(result[0], presentationQueueFamily, 0'u32, addr(result[2])); -proc createShaderStage*(device: VkDevice, stage: VkShaderStageFlagBits, shader: string): VkPipelineShaderStageCreateInfo = - const VK_GLSL_MAP = { - VK_SHADER_STAGE_VERTEX_BIT: GLSLANG_STAGE_VERTEX, - VK_SHADER_STAGE_FRAGMENT_BIT: GLSLANG_STAGE_FRAGMENT, - }.toTable() - var code = compileGLSLToSPIRV(VK_GLSL_MAP[stage], shader, "<memory-shader>") - var createInfo = VkShaderModuleCreateInfo( - sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, - codeSize: uint(code.len * sizeof(uint32)), - pCode: addrOrNil(code), - ) - var shaderModule: VkShaderModule - checkVkResult vkCreateShaderModule(device, addr(createInfo), nil, addr(shaderModule)) - - return VkPipelineShaderStageCreateInfo( - sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - stage: stage, - module: shaderModule, - pName: "main", # entry point for shader - ) - proc debugCallback*( messageSeverity: VkDebugUtilsMessageSeverityFlagBitsEXT, messageTypes: VkDebugUtilsMessageTypeFlagsEXT,