# HG changeset patch # User Sam # Date 1674010323 -25200 # Node ID 3f1111f3b9f8c2407eba033de1be179b28f6fab7 # Parent 2fa8e418deae6d51e40eb810ef6e1dff0769de12 did: tons of stuff, input, refactoring, fix some errors, some template improvment, sorry for super-commit diff -r 2fa8e418deae -r 3f1111f3b9f8 config.nims --- a/config.nims Mon Jan 16 00:51:03 2023 +0700 +++ b/config.nims Wed Jan 18 09:52:03 2023 +0700 @@ -120,5 +120,5 @@ exec &"mv {dirname}/bin/glslangValidator.exe examples/" exec &"rm -rf {dirname}" -if getCommand() in ["c", "compile", "r", "dump", "check"]: +if getCommand() in ["c", "compile", "r", "dump", "check", "idetools"]: compilerFlags() diff -r 2fa8e418deae -r 3f1111f3b9f8 examples/alotof_triangles.nim --- a/examples/alotof_triangles.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/examples/alotof_triangles.nim Wed Jan 18 09:52:03 2023 +0700 @@ -42,7 +42,6 @@ for i in 1 .. 300: var randommesh = new Mesh[VertexDataA] - # TODO: create randomized position11 from baseTriangle with random transformation matrix let randomcolor1 = Vec3([float32(rand(1)), float32(rand(1)), float32(rand(1))]) let transform1 = randomtransform() randommesh.vertexData = VertexDataA( @@ -81,13 +80,6 @@ const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms]() const fragmentShader = generateFragmentShaderCode[VertexDataA]() - static: - echo "--------------" - for (i, line) in enumerate(vertexShader.splitLines()): - echo $(i + 1) & " " & line - echo "--------------" - echo fragmentShader - echo "--------------" var pipeline = setupPipeline[VertexDataA, float32, uint16]( myengine, scene, diff -r 2fa8e418deae -r 3f1111f3b9f8 examples/hello_cube.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/hello_cube.nim Wed Jan 18 09:52:03 2023 +0700 @@ -0,0 +1,130 @@ +# +# TODO: Needs Depth-Buffer first! +# +# +# +# +# +# +# +# +import std/times +import std/strutils +import std/enumerate + +import zamikongine/engine +import zamikongine/math/vector +import zamikongine/math/matrix +import zamikongine/vertex +import zamikongine/descriptor +import zamikongine/mesh +import zamikongine/thing +import zamikongine/shader +import zamikongine/buffer + +type + # define type of vertex + VertexDataA = object + position: PositionAttribute[Vec3[float32]] + color: ColorAttribute[Vec3[float32]] + Uniforms = object + model: Descriptor[Mat44[float32]] + view: Descriptor[Mat44[float32]] + projection: Descriptor[Mat44[float32]] + +var + pipeline: RenderPipeline[VertexDataA, Uniforms] + uniforms:Uniforms + t: float32 + + +proc globalUpdate(engine: var Engine, dt: float32) = + let ratio = float32(engine.vulkan.frameDimension.height) / float32(engine.vulkan.frameDimension.width) + t += dt + uniforms.model.value = translate3d(0'f32, 0'f32, 10'f32) * rotate3d(t, Yf32) # * rotate3d(float32(PI), Yf32) + + uniforms.view.value = Unit44f32 + uniforms.projection.value = Mat44[float32](data:[ + ratio, 0'f32, 0'f32, 0'f32, + 0'f32, 1'f32, 0'f32, 0'f32, + 0'f32, 0'f32, 1'f32, 0'f32, + 0'f32, 0'f32, 0'f32, 1'f32, + ]) + uniforms.projection.value = perspective(float32(PI / 4), float32(engine.vulkan.frameDimension.width) / float32(engine.vulkan.frameDimension.height), 0.1'f32, 100'f32) + for buffer in pipeline.uniformBuffers: + buffer.updateData(uniforms) + echo uniforms.projection.value + +const + TopLeftFront = Vec3([ -0.5'f32, -0.5'f32, -0.5'f32]) + TopRightFront = Vec3([ 0.5'f32, -0.5'f32, -0.5'f32]) + BottomRightFront = Vec3([ 0.5'f32, 0.5'f32, -0.5'f32]) + BottomLeftFront = Vec3([ -0.5'f32, 0.5'f32, -0.5'f32]) + TopLeftBack = Vec3([ 0.5'f32, -0.5'f32, 0.5'f32]) + TopRightBack = Vec3([ -0.5'f32, -0.5'f32, 0.5'f32]) + BottomRightBack = Vec3([ -0.5'f32, 0.5'f32, 0.5'f32]) + BottomLeftBack = Vec3([ 0.5'f32, 0.5'f32, 0.5'f32]) +const + cube_pos = @[ + TopLeftFront, TopRightFront, BottomRightFront, BottomLeftFront, # front + TopLeftBack, TopRightBack, BottomRightBack, BottomLeftBack, # back + TopLeftBack, TopLeftFront, BottomLeftFront, BottomLeftBack, # left + TopRightBack, TopRightFront, BottomRightFront, BottomRightBack, # right + TopLeftBack, TopRightBack, TopRightFront, TopLeftFront, # top + BottomLeftFront, BottomRightFront, BottomRightBack, BottomLeftBack, # bottom + ] + cube_color = @[ + Rf32, Rf32, Rf32, Rf32, + Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32, Rf32 * 0.5'f32, + Gf32, Gf32, Gf32, Gf32, + Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32, Gf32 * 0.5'f32, + Bf32, Bf32, Bf32, Bf32, + Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32, Bf32 * 0.5'f32, + ] +var + tris: seq[array[3, uint16]] +# for i in 0'u16 ..< 6'u16: + # let off = i * 4 +var off = 0'u16 * 4 +# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] +# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] +# off = 1'u16 * 4 +# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] +# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] +# off = 4'u16 * 4 +# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] +# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] +# off = 3'u16 * 4 +# tris.add [off + 0'u16, off + 1'u16, off + 2'u16] +# tris.add [off + 2'u16, off + 3'u16, off + 0'u16] + +when isMainModule: + var myengine = igniteEngine("Hello cube") + + # build a mesh + var trianglemesh = new IndexedMesh[VertexDataA, uint16] + trianglemesh.vertexData = VertexDataA( + position: PositionAttribute[Vec3[float32]](data: cube_pos), + color: ColorAttribute[Vec3[float32]](data: cube_color), + ) + trianglemesh.indices = tris + # build a single-object scene graph + var triangle = new Thing + # add the triangle mesh to the object + triangle.parts.add trianglemesh + + # upload data, prepare shaders, etc + const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms](""" + out_position = (uniforms.projection * uniforms.view * uniforms.model) * vec4(in_position, 1); + """) + const fragmentShader = generateFragmentShaderCode[VertexDataA]() + pipeline = setupPipeline[VertexDataA, Uniforms, uint16]( + myengine, + triangle, + vertexShader, + fragmentShader + ) + # show something + myengine.run(pipeline, globalUpdate) + pipeline.trash() + myengine.trash() diff -r 2fa8e418deae -r 3f1111f3b9f8 examples/hello_triangle.nim --- a/examples/hello_triangle.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/examples/hello_triangle.nim Wed Jan 18 09:52:03 2023 +0700 @@ -55,7 +55,7 @@ # upload data, prepare shaders, etc const vertexShader = generateVertexShaderCode[VertexDataA, void]() const fragmentShader = generateFragmentShaderCode[VertexDataA]() - pipeline = setupPipeline[VertexDataA, void, uint16]( + pipeline = setupPipeline[VertexDataA, void, void]( myengine, triangle, vertexShader, diff -r 2fa8e418deae -r 3f1111f3b9f8 examples/input.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/input.nim Wed Jan 18 09:52:03 2023 +0700 @@ -0,0 +1,99 @@ +import std/times +import std/strutils +import std/enumerate + +import zamikongine/engine +import zamikongine/math/vector +import zamikongine/math/matrix +import zamikongine/vertex +import zamikongine/descriptor +import zamikongine/mesh +import zamikongine/thing +import zamikongine/shader +import zamikongine/buffer + +type + # define type of vertex + VertexDataA = object + position: PositionAttribute[Vec2[float32]] + color: ColorAttribute[Vec3[float32]] + Uniforms = object + cursor: Descriptor[Vec2[float32]] + aspect: Descriptor[float32] + +var + pipeline: RenderPipeline[VertexDataA, Uniforms] + uniforms: Uniforms +uniforms.aspect.value = 1 + + +proc globalUpdate(engine: var Engine, dt: float32) = + uniforms.aspect.value = float32(engine.vulkan.frameDimension.height) / float32(engine.vulkan.frameDimension.width) + uniforms.cursor.value[0] = ((float32(engine.input.mouseX) / float32(engine.vulkan.frameDimension.width)) * 2'f32 ) - 1'f32 + uniforms.cursor.value[1] = ((float32(engine.input.mouseY) / float32(engine.vulkan.frameDimension.height)) * 2'f32 ) - 1'f32 + for buffer in pipeline.uniformBuffers: + buffer.updateData(uniforms) + +# vertex data (types must match the above VertexAttributes) +const + shape = @[ + Vec2([- 1'f32, - 1'f32]), + Vec2([ 1'f32, - 1'f32]), + Vec2([-0.3'f32, -0.3'f32]), + Vec2([-0.3'f32, -0.3'f32]), + Vec2([- 1'f32, 1'f32]), + Vec2([- 1'f32, - 1'f32]), + ] + colors = @[ + Vec3([1'f32, 0'f32, 0'f32]), + Vec3([1'f32, 0'f32, 0'f32]), + Vec3([1'f32, 0'f32, 0'f32]), + Vec3([0.8'f32, 0'f32, 0'f32]), + Vec3([0.8'f32, 0'f32, 0'f32]), + Vec3([0.8'f32, 0'f32, 0'f32]), + ] + +when isMainModule: + var myengine = igniteEngine("Input") + + # build a single-object scene graph + var cursor = new Thing + var cursorpart = new Mesh[VertexDataA] + cursorpart.vertexData = VertexDataA( + position: PositionAttribute[Vec2[float32]](data: shape), + color: ColorAttribute[Vec3[float32]](data: colors), + ) + # transform the cursor a bit to make it look nice + for i in 0 ..< cursorpart.vertexData.position.data.len: + let cursorscale = ( + scale2d(0.07'f32, 0.07'f32) * + translate2d(1'f32, 1'f32) * + rotate2d(-float32(PI) / 4'f32) * + scale2d(0.5'f32, 1'f32) * + rotate2d(float32(PI) / 4'f32) + ) + let pos = Vec3[float32]([cursorpart.vertexData.position.data[i][0], cursorpart.vertexData.position.data[i][1], 1'f32]) + cursorpart.vertexData.position.data[i] = (cursorscale * pos).xy + cursor.parts.add cursorpart + + var scene = new Thing + scene.children.add cursor + + # upload data, prepare shaders, etc + const vertexShader = generateVertexShaderCode[VertexDataA, Uniforms](""" + out_position.x = in_position.x * uniforms.aspect + uniforms.cursor.x; + out_position.y = in_position.y + uniforms.cursor.y; + """) + const fragmentShader = generateFragmentShaderCode[VertexDataA]() + echo vertexShader + echo fragmentShader + pipeline = setupPipeline[VertexDataA, Uniforms, uint16]( + myengine, + scene, + vertexShader, + fragmentShader + ) + # show something + myengine.run(pipeline, globalUpdate) + pipeline.trash() + myengine.trash() diff -r 2fa8e418deae -r 3f1111f3b9f8 examples/squares.nim --- a/examples/squares.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/examples/squares.nim Wed Jan 18 09:52:03 2023 +0700 @@ -33,7 +33,7 @@ when isMainModule: randomize() - var myengine = igniteEngine("A lot of triangles") + var myengine = igniteEngine("Squares") const COLUMNS = 10 ROWS = 10 diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/engine.nim --- a/src/zamikongine/engine.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/engine.nim Wed Jan 18 09:52:03 2023 +0700 @@ -79,10 +79,20 @@ imageAvailableSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] renderFinishedSemaphores*: array[MAX_FRAMES_IN_FLIGHT, VkSemaphore] inFlightFences*: array[MAX_FRAMES_IN_FLIGHT, VkFence] + Input* = object + keysDown*: set[Key] + keysPressed*: set[Key] + keysReleased*: set[Key] + mouseDown*: set[MouseButton] + mousePressed*: set[MouseButton] + mouseReleased*: set[MouseButton] + mouseX*: int + mouseY*: int Engine* = object vulkan*: Vulkan window*: NativeWindow currentscenedata*: ref Thing + input*: Input proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhysicalDevice] = for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance): @@ -518,13 +528,14 @@ var ubermesh = createUberMesh(allmeshes) result.vertexBuffers.add createVertexBuffers(ubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) - # vertex buffers with indexes - var allindexedmeshes: seq[IndexedMesh[VertexType, IndexType]] - for mesh in partsOfType[ref IndexedMesh[VertexType, IndexType]](engine.currentscenedata): - allindexedmeshes.add(mesh[]) - if allindexedmeshes.len > 0: - var indexedubermesh = createUberMesh(allindexedmeshes) - result.indexedVertexBuffers.add createIndexedVertexBuffers(indexedubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) + when not IndexType is void: + # vertex buffers with indexes + var allindexedmeshes: seq[IndexedMesh[VertexType, IndexType]] + for mesh in partsOfType[ref IndexedMesh[VertexType, IndexType]](engine.currentscenedata): + allindexedmeshes.add(mesh[]) + if allindexedmeshes.len > 0: + var indexedubermesh = createUberMesh(allindexedmeshes) + result.indexedVertexBuffers.add createIndexedVertexBuffers(indexedubermesh, result.device, engine.vulkan.device.physicalDevice.device, engine.vulkan.commandPool, engine.vulkan.device.graphicsQueue) # uniform buffers result.uniformBuffers = createUniformBuffers[MAX_FRAMES_IN_FLIGHT, UniformType]( @@ -704,16 +715,31 @@ while not killed: # process input + engine.input.keysPressed = {} + engine.input.keysReleased = {} + engine.input.mousePressed = {} + engine.input.mouseReleased = {} for event in engine.window.pendingEvents(): case event.eventType: of Quit: killed = true of ResizedWindow: resized = true - of KeyDown: - echo event - if event.key == Escape: - killed = true + of KeyPressed: + engine.input.keysPressed.incl event.key + engine.input.keysDown.incl event.key + of KeyReleased: + engine.input.keysReleased.incl event.key + engine.input.keysDown.excl event.key + of MousePressed: + engine.input.mousePressed.incl event.button + engine.input.mouseDown.incl event.button + of MouseReleased: + engine.input.mouseReleased.incl event.button + engine.input.mouseDown.excl event.button + of MouseMoved: + engine.input.mouseX = event.x + engine.input.mouseY = event.y else: discard diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/events.nim --- a/src/zamikongine/events.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/events.nim Wed Jan 18 09:52:03 2023 +0700 @@ -2,19 +2,28 @@ EventType* = enum Quit ResizedWindow - KeyDown - KeyUp - Key* = enum + KeyPressed, KeyReleased + MousePressed, MouseReleased, MouseMoved, + Key* {.size: sizeof(cint), pure.} = enum UNKNOWN + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12 + NumberRowExtra1, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `0`, NumberRowExtra2, NumberRowExtra3 # tilde, minus, plus A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z - a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z - `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `0` - Minus, Plus, Underscore, Equals, Space, Enter, Backspace, Tab - Comma, Period, Semicolon, Colon, - Escape, CtrlL, ShirtL, AltL, CtrlR, ShirtR, AltR + Tab, CapsLock, ShiftL, ShiftR, CtrlL, CtrlR, SuperL, SuperR, AltL, AltR, Space, Enter, Backspace + LetterRow1Extra1, LetterRow1Extra2, LetterRow1Extra3 # open bracket, close brackt, backslash + LetterRow2Extra1, LetterRow2Extra2 # semicolon, quote + LetterRow3Extra1, LetterRow3Extra2, LetterRow3Extra3 # comma, period, slash + Up, Down, Left, Right + PageUp, PageDown, Home, End, Insert, Delete + MouseButton* {.size: sizeof(cint), pure.} = enum + UNKNOWN, Mouse1, Mouse2, Mouse3 Event* = object case eventType*: EventType - of KeyDown, KeyUp: + of KeyPressed, KeyReleased: key*: Key + of MousePressed, MouseReleased: + button*: MouseButton + of MouseMoved: + x*, y*: int else: discard diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/math/matrix.nim --- a/src/zamikongine/math/matrix.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/math/matrix.nim Wed Jan 18 09:52:03 2023 +0700 @@ -6,6 +6,8 @@ import ./vector +export math + type # layout is row-first # having an object instead of directly aliasing the array seems a bit ugly at @@ -15,19 +17,19 @@ # # Though, great news is that objects have zero overhead! Mat22*[T: SomeNumber] = object - data: array[4, T] + data*: array[4, T] Mat23*[T: SomeNumber] = object - data: array[6, T] + data*: array[6, T] Mat32*[T: SomeNumber] = object - data: array[6, T] + data*: array[6, T] Mat33*[T: SomeNumber] = object - data: array[9, T] + data*: array[9, T] Mat34*[T: SomeNumber] = object - data: array[12, T] + data*: array[12, T] Mat43*[T: SomeNumber] = object - data: array[12, T] + data*: array[12, T] Mat44*[T: SomeNumber] = object - data: array[16, T] + data*: array[16, T] MatMM* = Mat22|Mat33|Mat44 MatMN* = Mat23|Mat32|Mat34|Mat43 Mat* = MatMM|MatMN @@ -343,14 +345,14 @@ let cosa = cos(angle) sina = sin(angle) - x = a.x - y = a.y - z = a.z + x = a[0] + y = a[1] + z = a[2] Mat44[T](data: [ x * x * (1 - cosa) + cosa, y * x * (1 - cosa) - z * sina, z * x * (1 - cosa) + y * sina, T(0), x * y * (1 - cosa) + z * sina, y * y * (1 - cosa) + cosa, z * y * (1 - cosa) - x * sina, T(0), x * z * (1 - cosa) - y * sina, y * z * (1 - cosa) + x * sina, z * z * (1 - cosa) + cosa, T(0), - T(0), T(0), T(0), 1, + T(0), T(0), T(0), T(1), ]) @@ -370,3 +372,20 @@ makeRandomInit(Mat34) makeRandomInit(Mat43) makeRandomInit(Mat44) + +func perspective*[T: SomeFloat](fovy, aspect, zNear, zFar: T): Mat44[T] = + let tanHalfFovy = tan(fovy / T(2)) + return Mat44[T](data:[ + T(1) / (aspect * tanHalfFovy), T(0), T(0), T(0), + T(0), T(1) / tanHalfFovy, T(0), T(0), + T(0), T(0), T(zFar / (zFar - zNear)), T(-(zFar * zNear) / (zFar - zNear)), + T(0), T(0), T(1), T(1), + ]) + +func ortho*[T: SomeFloat](left, right, bottom, top, zNear, zFar: T): Mat44[T] = + Mat44[T](data:[ + T(2) / (right - left), T(0), T(0), -(right + left) / (right - left), + T(0), T(2) / (top - bottom), T(0), -(top + bottom) / (top - bottom), + T(0), T(0), T(1) / (zFar - zNear), -zNear / (zFar - zNear), + T(0), T(0), T(1), T(1), + ]) diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/math/vector.nim --- a/src/zamikongine/math/vector.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/math/vector.nim Wed Jan 18 09:52:03 2023 +0700 @@ -5,6 +5,8 @@ import std/typetraits import std/tables +export math + type Vec2*[T: SomeNumber] = array[2, T] diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/mesh.nim --- a/src/zamikongine/mesh.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/mesh.nim Wed Jan 18 09:52:03 2023 +0700 @@ -107,5 +107,5 @@ data: @[Vec2[T]([T(0), T(0)]), Vec2[T]([T(0), T(1)]), Vec2[T]([T(1), T(1)]), Vec2[T]([T(1), T(0)])] ) func squareIndices*[T:uint16|uint32](): auto = seq[array[3, T]]( - @[[T(1), T(0), T(3)], [T(2), T(1), T(3)], ] + @[[T(0), T(1), T(3)], [T(2), T(1), T(3)]] ) diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/platform/linux/symkey_map.nim --- a/src/zamikongine/platform/linux/symkey_map.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/platform/linux/symkey_map.nim Wed Jan 18 09:52:03 2023 +0700 @@ -1,16 +1,27 @@ import std/tables export tables -import x11/keysym -# import x11/x +import x11/x + import ../../events +# got values (keycodes) from xev const KeyTypeMap* = { - XK_A: A, XK_B: B, XK_C: C, XK_D: D, XK_E: E, XK_F: F, XK_G: G, XK_H: H, XK_I: I, XK_J: J, XK_K: K, XK_L: L, XK_M: M, XK_N: N, XK_O: O, XK_P: P, XK_Q: Q, XK_R: R, XK_S: S, XK_T: T, XK_U: U, XK_V: V, XK_W: W, XK_X: X, XK_Y: Y, XK_Z: Z, - XK_a: a, XK_b: b, XK_c: c, XK_d: d, XK_e: e, XK_f: f, XK_g: g, XK_h: h, XK_i: i, XK_j: j, XK_k: k, XK_l: l, XK_m: m, XK_n: n, XK_o: o, XK_p: p, XK_q: q, XK_r: r, XK_s: s, XK_t: t, XK_u: u, XK_v: v, XK_w: w, XK_x: Key.x, XK_y: y, XK_z: z, - XK_1: `1`, XK_2: `2`, XK_3: `3`, XK_4: `4`, XK_5: `5`, XK_6: `6`, XK_7: `7`, XK_8: `8`, XK_9: `9`, XK_0: `0`, - XK_minus: Minus, XK_plus: Plus, XK_underscore: Underscore, XK_equal: Equals, XK_space: Space, XK_Return: Enter, XK_BackSpace: Backspace, XK_Tab: Tab, - XK_comma: Comma, XK_period: Period, XK_semicolon: Semicolon, XK_colon: Colon, - XK_Escape: Escape, XK_Control_L: CtrlL, XK_Shift_L: ShirtL, XK_Alt_L: AltL, XK_Control_R: CtrlR, XK_Shift_R: ShirtR, XK_Alt_R: AltR + 9: Escape, 67: F1, 68: F2, 69: F3, 70: F4, 71: F5, 72: F6, 73: F7, 74: F8, 75: F9, 76: F10, 95: F11, 96: F12, + 49: NumberRowExtra1, 10: `1`, 11: `2`, 12: `3`, 13: `4`, 14: `5`, 15: `6`, 16: `7`, 17: `8`, 18: `9`, 19: `0`, 20: NumberRowExtra2, 21: NumberRowExtra3, + 24: Q, 25: W, 26: E, 27: R, 28: T, 29: Y, 30: U, 31: I, 32: O, 33: P, 38: A, 39: S, 40: D, 41: Key.F, 42: G, 43: H, 44: J, 45: K, 46: L, 52: Z, 53: X, 54: C, 55: V, 56: B, 57: N, 58: M, + + 23: Tab, 66: CapsLock, 50: ShiftL, 62: ShiftR, 37: CtrlL, 105: CtrlR, 133: SuperL, #[ SuperR, ]# 64: AltL, #[ AltR, ]# 65: Space, 36: Enter, 22: Backspace, + 34: LetterRow1Extra1, 35: LetterRow1Extra2, 51: LetterRow1Extra3, + 47: LetterRow2Extra1, 48: LetterRow2Extra2, + 59: LetterRow3Extra1, 60: LetterRow3Extra2, 61: LetterRow3Extra3, + 111: Up, 116: Down, 113: Left, 114: Right, + 112: PageUp, 117: PageDown, 110: Home, 115: End, 118: Insert, 119: Delete, }.toTable + +const MouseButtonTypeMap* = { + Button1: MouseButton.Mouse1, + Button2: MouseButton.Mouse2, + Button3: MouseButton.Mouse3, +}.toTable diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/platform/linux/vulkan.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/zamikongine/platform/linux/vulkan.nim Wed Jan 18 09:52:03 2023 +0700 @@ -0,0 +1,13 @@ +# included in zamikongine/vulkan_helpers +const REQUIRED_PLATFORM_EXTENSIONS = @["VK_KHR_xlib_surface".cstring] + +proc load_platform_extensions() = + loadVK_KHR_xlib_surface() + +proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = + var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( + sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, + dpy: window.display, + window: window.window, + ) + checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/platform/linux/xlib.nim --- a/src/zamikongine/platform/linux/xlib.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/platform/linux/xlib.nim Wed Jan 18 09:52:03 2023 +0700 @@ -1,7 +1,9 @@ import x11/xlib, x11/xutil, - x11/keysym + x11/keysym, + x11/xinput, + x11/xinput2 import x11/x import ../../events @@ -16,6 +18,7 @@ NativeWindow* = object display*: PDisplay window*: Window + emptyCursor: Cursor template checkXlibResult*(call: untyped) = let value = call @@ -36,15 +39,24 @@ let window = XCreateSimpleWindow(display, rootWindow, -1, -1, 800, 600, 0, foregroundColor, backgroundColor) checkXlibResult XSetStandardProperties(display, window, title, "window", 0, nil, 0, nil) - checkXlibResult XSelectInput(display, window, ButtonPressMask or KeyPressMask or ExposureMask) + checkXlibResult XSelectInput(display, window, PointerMotionMask or ButtonPressMask or ButtonReleaseMask or KeyPressMask or KeyReleaseMask or ExposureMask) checkXlibResult XMapWindow(display, window) deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", XBool(false)) checkXlibResult XSetWMProtocols(display, window, addr(deleteMessage), 1) - return NativeWindow(display: display, window: window) + # quite a lot of work to hide the cursor... + var data = "\0".cstring + var pixmap = XCreateBitmapFromData(display, window, data, 1, 1) + var color: XColor + var empty_cursor = XCreatePixmapCursor(display, pixmap, pixmap, addr(color), addr(color), 0, 0) + checkXlibResult XFreePixmap(display, pixmap) + checkXlibResult XDefineCursor(display, window, empty_cursor) + + return NativeWindow(display: display, window: window, emptyCursor: empty_cursor) proc trash*(window: NativeWindow) = + checkXlibResult window.display.XFreeCursor(window.emptyCursor) checkXlibResult window.display.XDestroyWindow(window.window) discard window.display.XCloseDisplay() # always returns 0 @@ -62,12 +74,21 @@ if cast[Atom](event.xclient.data.l[0]) == deleteMessage: result.add(Event(eventType: Quit)) of KeyPress: - let xkey: KeySym = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) - result.add(Event(eventType: KeyDown, key: KeyTypeMap.getOrDefault(xkey, UNKNOWN))) + let xkey = int(cast[PXKeyEvent](addr(event)).keycode) + result.add Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN)) of KeyRelease: - let xkey: KeySym = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0) - result.add(Event(eventType: KeyUp, key: KeyTypeMap.getOrDefault(xkey, UNKNOWN))) + let xkey = int(cast[PXKeyEvent](addr(event)).keycode) + result.add Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(xkey, Key.UNKNOWN)) + of ButtonPress: + let button = int(cast[PXButtonEvent](addr(event)).button) + result.add Event(eventType: MousePressed, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN)) + of ButtonRelease: + let button = int(cast[PXButtonEvent](addr(event)).button) + result.add Event(eventType: MouseReleased, button: MouseButtonTypeMap.getOrDefault(button, MouseButton.UNKNOWN)) + of MotionNotify: + let motion = cast[PXMotionEvent](addr(event)) + result.add Event(eventType: MouseMoved, x: motion.x, y: motion.y) of ConfigureNotify: - result.add(Event(eventType: ResizedWindow)) + result.add Event(eventType: ResizedWindow) else: discard diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/platform/windows/virtualkey_map.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/zamikongine/platform/windows/virtualkey_map.nim Wed Jan 18 09:52:03 2023 +0700 @@ -0,0 +1,18 @@ +import std/tables +export tables + +import winim/core + +import ../../events + +const KeyTypeMap* = { + VK_ESCAPE: Key.Escape, VK_F1: F1, VK_F2: F2, VK_F3: F3, VK_F4: F4, VK_F5: F5, VK_F6: F6, VK_F7: F7, VK_F8: F8, VK_F9: F9, VK_F10: F10, VK_F11: F11, VK_F12: F12, + VK_OEM_3: NumberRowExtra1, int('0'): `0`, int('1'): `1`, int('2'): `2`, int('3'): `3`, int('4'): `4`, int('5'): `5`, int('6'): `6`, int('7'): `7`, int('8'): `8`, int('9'): `9`, VK_OEM_MINUS: NumberRowExtra2, VK_OEM_PLUS: NumberRowExtra3, + int('A'): A, int('B'): B, int('C'): C, int('D'): D, int('E'): E, int('F'): F, int('G'): G, int('H'): H, int('I'): I, int('J'): J, int('K'): K, int('L'): L, int('M'): M, int('N'): N, int('O'): O, int('P'): P, int('Q'): Q, int('R'): R, int('S'): S, int('T'): T, int('U'): U, int('V'): V, int('W'): W, int('X'): X, int('Y'): Y, int('Z'): Z, + VK_TAB: Tab, VK_CAPITAL: CapsLock, VK_LSHIFT: ShiftL, VK_SHIFT: ShiftL, VK_RSHIFT: ShiftR, VK_LCONTROL: CtrlL, VK_CONTROL: CtrlL, VK_RCONTROL: CtrlR, VK_LWIN: SuperL, VK_RWIN: SuperR, VK_LMENU: AltL, VK_RMENU: AltR, VK_SPACE: Space, VK_RETURN: Enter, VK_BACK: Backspace, + VK_OEM_4: LetterRow1Extra1, VK_OEM_6: LetterRow1Extra2, VK_OEM_5: LetterRow1Extra3, + VK_OEM_1: LetterRow2Extra1, VK_OEM_7: LetterRow2Extra2, + VK_OEM_COMMA: LetterRow3Extra1, VK_OEM_PERIOD: LetterRow3Extra2, VK_OEM_2: LetterRow3Extra3, + VK_UP: Up, VK_DOWN: Down, VK_LEFT: Left, VK_RIGHT: Right, + VK_PRIOR: PageUp, VK_NEXT: PageDown, VK_HOME: Home, VK_END: End, VK_INSERT: Insert, VK_DELETE: Key.Delete, +}.toTable diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/platform/windows/vulkan.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/zamikongine/platform/windows/vulkan.nim Wed Jan 18 09:52:03 2023 +0700 @@ -0,0 +1,13 @@ +# included in zamikongine/vulkan_helpers +const REQUIRED_PLATFORM_EXTENSIONS* = @["VK_KHR_win32_surface".cstring] + +proc load_platform_extensions() = + loadVK_KHR_win32_surface() + +proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = + var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR( + sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, + hinstance: window.hinstance, + hwnd: window.hwnd, + ) + checkVkResult vkCreateWin32SurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/platform/windows/win32.nim --- a/src/zamikongine/platform/windows/win32.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/platform/windows/win32.nim Wed Jan 18 09:52:03 2023 +0700 @@ -1,5 +1,6 @@ import winim +import ./virtualkey_map import ../../events type @@ -7,6 +8,7 @@ hinstance*: HINSTANCE hwnd*: HWND +# sorry, have to use module-global variable to capture windows events var currentEvents: seq[Event] template checkWin32Result*(call: untyped) = @@ -14,10 +16,42 @@ if value != 0: raise newException(Exception, "Win32 error: " & astToStr(call) & " returned " & $value) + +proc MapLeftRightKeys(key: INT, lparam: LPARAM): INT = + case key + of VK_SHIFT: + MapVirtualKey(UINT((lParam and 0x00ff0000) shr 16), MAPVK_VSC_TO_VK_EX) + of VK_CONTROL: + if (lParam and 0x01000000) == 0: VK_LCONTROL else: VK_RCONTROL + of VK_MENU: + if (lParam and 0x01000000) == 0: VK_LMENU else: VK_RMENU + else: + key + proc WindowHandler(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM): LRESULT {.stdcall.} = case uMsg of WM_DESTROY: currentEvents.add(Event(eventType: events.EventType.Quit)) + of WM_KEYDOWN, WM_SYSKEYDOWN: + let key = MapLeftRightKeys(INT(wParam), lParam) + currentEvents.add(Event(eventType: KeyPressed, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN))) + of WM_KEYUP, WM_SYSKEYUP: + let key = MapLeftRightKeys(INT(wParam), lParam) + currentEvents.add(Event(eventType: KeyReleased, key: KeyTypeMap.getOrDefault(key, Key.UNKNOWN))) + of WM_LBUTTONDOWN: + currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse1)) + of WM_LBUTTONUP: + currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse1)) + of WM_MBUTTONDOWN: + currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse2)) + of WM_MBUTTONUP: + currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse2)) + of WM_RBUTTONDOWN: + currentEvents.add(Event(eventType: MousePressed, button: MouseButton.Mouse3)) + of WM_RBUTTONUP: + currentEvents.add(Event(eventType: MouseReleased, button: MouseButton.Mouse3)) + of WM_MOUSEMOVE: + currentEvents.add(Event(eventType: events.MouseMoved, x: GET_X_LPARAM(lParam), y: GET_Y_LPARAM(lParam))) else: return DefWindowProc(hwnd, uMsg, wParam, lParam) @@ -49,7 +83,8 @@ nil ) - discard ShowWindow(result.hwnd, 1) + discard ShowWindow(result.hwnd, SW_SHOW) + discard ShowCursor(false) proc trash*(window: NativeWindow) = discard @@ -60,8 +95,10 @@ (int(rect.right - rect.left), int(rect.bottom - rect.top)) proc pendingEvents*(window: NativeWindow): seq[Event] = + # empty queue currentEvents = newSeq[Event]() var msg: MSG + # fill queue while PeekMessage(addr(msg), window.hwnd, 0, 0, PM_REMOVE): DispatchMessage(addr(msg)) return currentEvents diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/shader.nim --- a/src/zamikongine/shader.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/shader.nim Wed Jan 18 09:52:03 2023 +0700 @@ -1,9 +1,10 @@ +import std/os +import std/hashes import std/strformat import std/strutils import std/tables import std/compilesettings - import ./vulkan_helpers import ./glsl_helpers import ./vulkan @@ -31,20 +32,22 @@ of VK_SHADER_STAGE_COMPUTE_BIT: "comp" of VK_SHADER_STAGE_ALL: "" -proc compileGLSLToSPIRV(stage: VkShaderStageFlagBits, shaderSource: string, entrypoint: string): seq[uint32] {.compileTime.} = - let stagename = stage2string(stage) +proc compileGLSLToSPIRV(stage: static VkShaderStageFlagBits, shaderSource: static string, entrypoint: string): seq[uint32] {.compileTime.} = + const + stagename = stage2string(stage) + shaderHash = hash(shaderSource) + # cross compilation for windows workaround, sorry computer + shaderout = getTempDir().replace("\\", "/") & "/" & fmt"shader_{shaderHash}.{stagename}" + projectPath = querySetting(projectPath) - # TODO: compiles only on linux for now (because we don't have compile-time functionality in std/tempfile) - let (tmpfile, exitCode) = gorgeEx(command=fmt"mktemp --tmpdir shader_XXXXXXX.{stagename}") - if exitCode != 0: - raise newException(Exception, tmpfile) - - let (output, exitCode_glsl) = gorgeEx(command=fmt"{querySetting(projectPath)}/glslangValidator --entry-point {entrypoint} -V --stdin -S {stagename} -o {tmpfile}", input=shaderSource) + let (output, exitCode_glsl) = gorgeEx(command=fmt"{projectPath}/glslangValidator --entry-point {entrypoint} -V --stdin -S {stagename} -o {shaderout}", input=shaderSource) if exitCode_glsl != 0: raise newException(Exception, output) - let shaderbinary = staticRead tmpfile + if output == "": # this happens when the nim was invoked with "check" instead of "compile/c", as it prevents the gorgeEx command to really run. However, there is hope, see https://github.com/nim-lang/RFCs/issues/430 + return result + let shaderbinary = staticRead shaderout - let (output_rm, exitCode_rm) = gorgeEx(command=fmt"rm {tmpfile}") + let (output_rm, exitCode_rm) = gorgeEx(command=fmt"rm {shaderout}") if exitCode_rm != 0: raise newException(Exception, output_rm) diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/vulkan.nim --- a/src/zamikongine/vulkan.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/vulkan.nim Wed Jan 18 09:52:03 2023 +0700 @@ -8,23 +8,30 @@ when defined(linux): import x11/x import x11/xlib + const vkDLL = "libvulkan.so.1" +else: + type + Display* = ptr object + VisualID* = ptr object + Window* = ptr object + when defined(windows): + const vkDLL = "vulkan-1.dll" import winim +else: + type + HINSTANCE* = ptr object + HWND* = ptr object + HMONITOR* = ptr object + HANDLE* = ptr object + SECURITY_ATTRIBUTES* = ptr object + DWORD* = ptr object + LPCWSTR* = ptr object var vkGetProc: proc(procName: cstring): pointer {.cdecl.} import dynlib -when defined(windows): - {. emit: """#define VK_USE_PLATFORM_WIN32_KHR""" .} - const vkDLL = "vulkan-1.dll" -elif defined(linux): - {.passl: gorge("pkg-config --libs vulkan").} - {. emit: """#define VK_USE_PLATFORM_X11_KHR""" .} - const vkDLL = "libvulkan.so.1" -else: - raise quit("Unsupported platform") - let vkHandleDLL = loadLib(vkDLL) if isNil(vkHandleDLL): quit("could not load: " & vkDLL) @@ -1188,22 +1195,6 @@ # Types -# stub types if we are on the wrong platform, so we don't need to "when" all platform functions -when not defined(linux): - type - Display* = ptr object - VisualID* = ptr object - Window* = ptr object -when not defined(windows): - type - HINSTANCE* = ptr object - HWND* = ptr object - HMONITOR* = ptr object - HANDLE* = ptr object - SECURITY_ATTRIBUTES* = ptr object - DWORD* = ptr object - LPCWSTR* = ptr object - type RROutput* = ptr object wl_display* = ptr object diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/vulkan_helpers.nim --- a/src/zamikongine/vulkan_helpers.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/vulkan_helpers.nim Wed Jan 18 09:52:03 2023 +0700 @@ -7,10 +7,6 @@ import ./vulkan import ./window - -const ENABLEVULKANVALIDATIONLAYERS* = not defined(release) - - template checkVkResult*(call: untyped) = when defined(release): discard call @@ -25,6 +21,14 @@ error "Vulkan error: ", astToStr(call), " returned ", $value raise newException(Exception, "Vulkan error: " & astToStr(call) & " returned " & $value) +# the included code need checkVkResult, therefore having the template above +when defined(linux): + include ./platform/linux/vulkan +when defined(windows): + include ./platform/windows/vulkan + +const ENABLEVULKANVALIDATIONLAYERS* = not defined(release) + func addrOrNil[T](obj: var openArray[T]): ptr T = if obj.len > 0: addr(obj[0]) else: nil @@ -132,11 +136,7 @@ proc createVulkanInstance*(vulkanVersion: uint32): VkInstance = - var requiredExtensions = @["VK_KHR_surface".cstring] - when defined(linux): - requiredExtensions.add("VK_KHR_xlib_surface".cstring) - when defined(windows): - requiredExtensions.add("VK_KHR_win32_surface".cstring) + var requiredExtensions = @["VK_KHR_surface".cstring] & REQUIRED_PLATFORM_EXTENSIONS when ENABLEVULKANVALIDATIONLAYERS: requiredExtensions.add("VK_EXT_debug_utils".cstring) @@ -177,10 +177,7 @@ checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result)) loadVK_KHR_surface() - when defined(linux): - loadVK_KHR_xlib_surface() - when defined(windows): - loadVK_KHR_win32_surface() + load_platform_extensions() loadVK_KHR_swapchain() when ENABLEVULKANVALIDATIONLAYERS: loadVK_EXT_debug_utils(result) @@ -228,20 +225,3 @@ proc getSurfaceCapabilities*(device: VkPhysicalDevice, surface: VkSurfaceKHR): VkSurfaceCapabilitiesKHR = checkVkResult device.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(surface, addr(result)) - -when defined(linux): - proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = - var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR( - sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, - dpy: window.display, - window: window.window, - ) - checkVkResult vkCreateXlibSurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) -when defined(windows): - proc createVulkanSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = - var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR( - sType: VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, - hinstance: window.hinstance, - hwnd: window.hwnd, - ) - checkVkResult vkCreateWin32SurfaceKHR(instance, addr(surfaceCreateInfo), nil, addr(result)) diff -r 2fa8e418deae -r 3f1111f3b9f8 src/zamikongine/window.nim --- a/src/zamikongine/window.nim Mon Jan 16 00:51:03 2023 +0700 +++ b/src/zamikongine/window.nim Wed Jan 18 09:52:03 2023 +0700 @@ -1,6 +1,4 @@ when defined(linux): - import ./platform/linux/xlib - export xlib + include ./platform/linux/xlib elif defined(windows): - import ./platform/windows/win32 - export win32 + include ./platform/windows/win32