changeset 590:1edf3e16144e

add: input handling, small refactoring for renderer
author Sam <sam@basx.dev>
date Tue, 18 Apr 2023 03:04:37 +0700
parents b434feaf2b67
children 20aeaaf3dc40
files src/semicongine/engine.nim src/semicongine/renderer.nim src/semicongine/vulkan/swapchain.nim tests/test_vulkan_wrapper.nim
diffstat 4 files changed, 120 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/semicongine/engine.nim	Mon Apr 17 18:02:19 2023 +0700
+++ b/src/semicongine/engine.nim	Tue Apr 18 03:04:37 2023 +0700
@@ -4,21 +4,60 @@
 import ./vulkan/instance
 import ./vulkan/device
 import ./vulkan/physicaldevice
+import ./vulkan/renderpass
 
+import ./gpu_data
+import ./entity
+import ./renderer
+import ./events
 import ./config
+import ./math
 
 type
+  Input = object
+    keyIsDown: set[Key]
+    keyWasPressed: set[Key]
+    keyWasReleased: set[Key]
+    mouseIsDown: set[MouseButton]
+    mouseWasPressed: set[MouseButton]
+    mouseWasReleased: set[MouseButton]
+    mousePosition: Vec2f
+    eventsProcessed: uint64
   Engine* = object
+    running*: bool
     device: Device
     debugger: Debugger
     instance: Instance
     window: NativeWindow
+    renderer: Renderer
+    input: Input
+    exitHandler: proc(engine: var Engine)
+    resizeHandler: proc(engine: var Engine)
+    eventHandler: proc(engine: var Engine, event: Event)
 
-func gpuDevice*(engine: Engine): Device =
-  engine.device
+
+proc destroy*(engine: var Engine) =
+  checkVkResult engine.device.vk.vkDeviceWaitIdle()
+  engine.renderer.destroy()
+  engine.device.destroy()
+  engine.debugger.destroy()
+  engine.instance.destroy()
+  engine.window.destroy()
+
 
-proc initEngine*(applicationName: string, debug=DEBUG): Engine =
+proc initEngine*(
+  applicationName: string,
+  debug=DEBUG,
+  exitHandler: proc(engine: var Engine) = nil,
+  resizeHandler: proc(engine: var Engine) = nil,
+  eventHandler: proc(engine: var Engine, event: Event) = nil
+): Engine =
+  result.running = true
+  result.exitHandler = exitHandler
+  result.resizeHandler = resizeHandler
+  result.eventHandler = eventHandler
   result.window = createWindow(applicationName)
+
   var
     instanceExtensions: seq[string]
     enabledLayers: seq[string]
@@ -42,8 +81,62 @@
     selectedPhysicalDevice.filterForGraphicsPresentationQueues()
   )
 
-proc destroy*(engine: var Engine) =
-  engine.device.destroy()
-  engine.debugger.destroy()
-  engine.instance.destroy()
-  engine.window.destroy()
+proc setRenderer*(engine: var Engine, renderPasses: openArray[RenderPass]) =
+  engine.renderer = engine.device.initRenderer(renderPasses)
+
+proc addScene*(engine: var Engine, entity: Entity, vertexInput: seq[ShaderAttribute]) =
+  engine.renderer.setupDrawableBuffers(entity, vertexInput)
+
+proc renderScene*(engine: var Engine, entity: Entity): auto =
+  assert engine.running
+  engine.renderer.render(entity)
+
+proc updateInputs*(engine: var Engine) =
+  assert engine.running
+  engine.input.keyWasPressed = {}
+  engine.input.keyWasReleased = {}
+  engine.input.mouseWasPressed = {}
+  engine.input.mouseWasReleased = {}
+  var
+    killed = false
+    resized = false
+  for event in engine.window.pendingEvents():
+    inc engine.input.eventsProcessed
+    if engine.eventHandler != nil:
+      engine.eventHandler(engine, event)
+    case event.eventType:
+      of Quit:
+        killed = true
+      of ResizedWindow:
+        resized = true
+      of KeyPressed:
+        engine.input.keyWasPressed.incl event.key
+        engine.input.keyIsDown.incl event.key
+      of KeyReleased:
+        engine.input.keyWasReleased.incl event.key
+        engine.input.keyIsDown.excl event.key
+      of MousePressed:
+        engine.input.mouseWasPressed.incl event.button
+        engine.input.mouseIsDown.incl event.button
+      of MouseReleased:
+        engine.input.mouseWasReleased.incl event.button
+        engine.input.mouseIsDown.excl event.button
+      of MouseMoved:
+        engine.input.mousePosition = newVec2(float32(event.x), float32(event.y))
+  if killed:
+    engine.running = false
+    if engine.exitHandler != nil:
+      engine.exitHandler(engine)
+  if resized and engine.resizeHandler != nil:
+    engine.resizeHandler(engine)
+
+func keyIsDown*(engine: Engine, key: Key): auto = key in engine.input.keyIsDown
+func keyWasPressed*(engine: Engine, key: Key): auto = key in engine.input.keyWasPressed
+func keyWasReleased*(engine: Engine, key: Key): auto = key in engine.input.keyWasReleased
+func mouseIsDown*(engine: Engine, button: MouseButton): auto = button in engine.input.mouseIsDown
+func mouseWasPressed*(engine: Engine, button: MouseButton): auto = button in engine.input.mouseWasPressed
+func mouseWasReleased*(engine: Engine, button: MouseButton): auto = button in engine.input.mouseWasReleased
+func mousePosition*(engine: Engine, key: Key): auto = engine.input.mousePosition
+func eventsProcessed*(engine: Engine): auto = engine.input.eventsProcessed
+func framesRendered*(engine: Engine): auto = engine.renderer.framesRendered
+func gpuDevice*(engine: Engine): Device = engine.device
--- a/src/semicongine/renderer.nim	Mon Apr 17 18:02:19 2023 +0700
+++ b/src/semicongine/renderer.nim	Tue Apr 18 03:04:37 2023 +0700
@@ -8,7 +8,6 @@
 import ./vulkan/buffer
 import ./vulkan/device
 import ./vulkan/drawable
-import ./vulkan/framebuffer
 import ./vulkan/pipeline
 import ./vulkan/physicaldevice
 import ./vulkan/renderpass
@@ -130,6 +129,7 @@
   renderer.scenedata[tree] = data
 
 proc render*(renderer: var Renderer, entity: Entity): bool =
+  # TODO: check if nextFrame had any problems
   var commandBuffer = renderer.swapchain.nextFrame()
 
   commandBuffer.beginRenderCommands(renderer.swapchain.renderPass, renderer.swapchain.currentFramebuffer())
--- a/src/semicongine/vulkan/swapchain.nim	Mon Apr 17 18:02:19 2023 +0700
+++ b/src/semicongine/vulkan/swapchain.nim	Tue Apr 18 03:04:37 2023 +0700
@@ -122,6 +122,8 @@
     VkFence(0),
     addr(swapchain.currentFramebufferIndex)
   )
+  # TODO: resize swapchain on VK_SUBOPTIMAL_KHR
+  echo "HIIIIIIIIIIII next image result", nextImageResult
   if not (nextImageResult in [VK_SUCCESS, VK_TIMEOUT, VK_NOT_READY, VK_SUBOPTIMAL_KHR]):
     return
 
@@ -161,6 +163,8 @@
     pResults: nil,
   )
   let presentResult = vkQueuePresentKHR(swapchain.device.firstPresentationQueue().get().vk, addr(presentInfo))
+  # TODO: resize swapchain on VK_SUBOPTIMAL_KHR
+  echo "HIIIIIIIIIIII Present Result:", presentResult
   if not (presentResult in [VK_SUCCESS, VK_SUBOPTIMAL_KHR]):
     return false
 
--- a/tests/test_vulkan_wrapper.nim	Mon Apr 17 18:02:19 2023 +0700
+++ b/tests/test_vulkan_wrapper.nim	Tue Apr 18 03:04:37 2023 +0700
@@ -105,7 +105,7 @@
   setInstanceData[Vec3f](c, "translate", @[newVec3f(-0.3,  0.1)])
   result = newEntity("root", t, r, c)
 
-when isMainModule:
+proc main() =
   var engine = initEngine("Test")
 
   # INIT RENDERER:
@@ -135,27 +135,32 @@
   var
     surfaceFormat = engine.gpuDevice.physicalDevice.getSurfaceFormats().filterSurfaceFormat()
     renderPass = engine.gpuDevice.simpleForwardRenderPass(surfaceFormat.format, vertexCode, fragmentCode, 2)
-    renderer = engine.gpuDevice.initRenderer([renderPass])
+  engine.setRenderer([renderPass])
 
-  # INIT SCENE
-
+  # INIT SCENES
   var scenes = [scene_simple(), scene_different_mesh_types(), scene_primitives()]
   var time = initShaderGlobal("time", 0.0'f32)
   for scene in scenes.mitems:
     scene.components.add time
-    renderer.setupDrawableBuffers(scene, vertexInput)
+    engine.addScene(scene, vertexInput)
 
   # MAINLOOP
   echo "Setup successfull, start rendering"
   for i in 0 ..< 3:
     for scene in scenes:
       for i in 0 ..< 1000:
+        engine.updateInputs()
         setValue[float32](time.value, get[float32](time.value) + 0.0005)
-        discard renderer.render(scene)
-  echo "Rendered ", renderer.framesRendered, " frames"
+        if not engine.running or engine.keyIsDown(Escape):
+          engine.destroy()
+          return
+        discard engine.renderScene(scene)
+  echo "Rendered ", engine.framesRendered, " frames"
+  echo "Processed ", engine.eventsProcessed, " events"
 
   # cleanup
   echo "Start cleanup"
-  checkVkResult engine.gpuDevice.vk.vkDeviceWaitIdle()
-  renderer.destroy()
   engine.destroy()
+
+when isMainModule:
+  main()