mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 10:59:39 +00:00 
			
		
		
		
	add mouse support
This commit is contained in:
		@ -71,6 +71,7 @@ public class Application {
 | 
				
			|||||||
  private func beginHandleEvents() {
 | 
					  private func beginHandleEvents() {
 | 
				
			||||||
    Keyboard.instance.newFrame()
 | 
					    Keyboard.instance.newFrame()
 | 
				
			||||||
    GameController.instance.newFrame()
 | 
					    GameController.instance.newFrame()
 | 
				
			||||||
 | 
					    Mouse.instance.newFrame(window!)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private func handleEvent(_ event: SDL_Event) -> ApplicationExecutionState {
 | 
					  private func handleEvent(_ event: SDL_Event) -> ApplicationExecutionState {
 | 
				
			||||||
@ -108,6 +109,17 @@ public class Application {
 | 
				
			|||||||
        btn: SDL_GamepadButton(Int32(event.gbutton.button)), state: event.gbutton.state)
 | 
					        btn: SDL_GamepadButton(Int32(event.gbutton.button)), state: event.gbutton.state)
 | 
				
			||||||
      return .running
 | 
					      return .running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case SDL_EVENT_MOUSE_BUTTON_DOWN, SDL_EVENT_MOUSE_BUTTON_UP:
 | 
				
			||||||
 | 
					      Mouse.instance.buttonEvent(
 | 
				
			||||||
 | 
					        btn: UInt32(event.button.button),
 | 
				
			||||||
 | 
					        state: event.button.state)
 | 
				
			||||||
 | 
					      return .running
 | 
				
			||||||
 | 
					    case SDL_EVENT_MOUSE_MOTION:
 | 
				
			||||||
 | 
					      Mouse.instance.motionEvent(
 | 
				
			||||||
 | 
					        absolute: SIMD2(event.motion.x, event.motion.y),
 | 
				
			||||||
 | 
					        relative: SIMD2(event.motion.xrel, event.motion.yrel))
 | 
				
			||||||
 | 
					      return .running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
 | 
					    case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
 | 
				
			||||||
      let backBufferSize = Size(Int(event.window.data1), Int(event.window.data2))
 | 
					      let backBufferSize = Size(Int(event.window.data1), Int(event.window.data2))
 | 
				
			||||||
      self.renderer!.resize(size: backBufferSize)
 | 
					      self.renderer!.resize(size: backBufferSize)
 | 
				
			||||||
 | 
				
			|||||||
@ -26,12 +26,15 @@ add_executable(Voxelotl MACOSX_BUNDLE
 | 
				
			|||||||
  # Resource classes
 | 
					  # Resource classes
 | 
				
			||||||
  NSImageLoader.swift
 | 
					  NSImageLoader.swift
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  # Input wrappers
 | 
				
			||||||
 | 
					  Input/Keyboard.swift
 | 
				
			||||||
 | 
					  Input/GameController.swift
 | 
				
			||||||
 | 
					  Input/Mouse.swift
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # Core utility classes
 | 
					  # Core utility classes
 | 
				
			||||||
  Color.swift
 | 
					  Color.swift
 | 
				
			||||||
  Camera.swift
 | 
					  Camera.swift
 | 
				
			||||||
  Renderer.swift
 | 
					  Renderer.swift
 | 
				
			||||||
  Keyboard.swift
 | 
					 | 
				
			||||||
  GameController.swift
 | 
					 | 
				
			||||||
  FPSCalculator.swift
 | 
					  FPSCalculator.swift
 | 
				
			||||||
  GameDelegate.swift
 | 
					  GameDelegate.swift
 | 
				
			||||||
  Application.swift
 | 
					  Application.swift
 | 
				
			||||||
@ -81,3 +84,4 @@ source_group("Resources" FILES Assets.xcassets test.png)
 | 
				
			|||||||
source_group("Source Files" REGULAR_EXPRESSION "\\.(swift|metal)$")
 | 
					source_group("Source Files" REGULAR_EXPRESSION "\\.(swift|metal)$")
 | 
				
			||||||
source_group("Source Files\\Random" REGULAR_EXPRESSION "Random/")
 | 
					source_group("Source Files\\Random" REGULAR_EXPRESSION "Random/")
 | 
				
			||||||
source_group("Source Files\\Math" REGULAR_EXPRESSION "Math/")
 | 
					source_group("Source Files\\Math" REGULAR_EXPRESSION "Math/")
 | 
				
			||||||
 | 
					source_group("Source Files\\Input" REGULAR_EXPRESSION "Input/")
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ public class Keyboard {
 | 
				
			|||||||
  private static let _REPEAT = UInt8(0b100)
 | 
					  private static let _REPEAT = UInt8(0b100)
 | 
				
			||||||
  private static let _PRESS: UInt8 = _DOWN | _IMPULSE
 | 
					  private static let _PRESS: UInt8 = _DOWN | _IMPULSE
 | 
				
			||||||
  private static let _RELEASE: UInt8 = _UP | _IMPULSE
 | 
					  private static let _RELEASE: UInt8 = _UP | _IMPULSE
 | 
				
			||||||
  
 | 
					
 | 
				
			||||||
  private var _state = [UInt8](repeating: _UP, count: Int(SDL_NUM_SCANCODES.rawValue))
 | 
					  private var _state = [UInt8](repeating: _UP, count: Int(SDL_NUM_SCANCODES.rawValue))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  internal func keyDownEvent(scan: SDL_Scancode, repeat rep: Bool) {
 | 
					  internal func keyDownEvent(scan: SDL_Scancode, repeat rep: Bool) {
 | 
				
			||||||
							
								
								
									
										81
									
								
								Sources/Voxelotl/Input/Mouse.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								Sources/Voxelotl/Input/Mouse.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					import SDL3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class Mouse {
 | 
				
			||||||
 | 
					  public struct Buttons: OptionSet {
 | 
				
			||||||
 | 
					    public let rawValue: UInt32
 | 
				
			||||||
 | 
					    public init(rawValue: UInt32) { self.rawValue = rawValue }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static let left    = Self(rawValue: UInt32(SDL_BUTTON_LEFT).buttonMask)
 | 
				
			||||||
 | 
					    static let middle  = Self(rawValue: UInt32(SDL_BUTTON_MIDDLE).buttonMask)
 | 
				
			||||||
 | 
					    static let right   = Self(rawValue: UInt32(SDL_BUTTON_RIGHT).buttonMask)
 | 
				
			||||||
 | 
					    static let button4 = Self(rawValue: UInt32(SDL_BUTTON_X1).buttonMask)
 | 
				
			||||||
 | 
					    static let button5 = Self(rawValue: UInt32(SDL_BUTTON_X2).buttonMask)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static var capture: Bool {
 | 
				
			||||||
 | 
					    get { self._instance.getCapture() }
 | 
				
			||||||
 | 
					    set { self._instance.setCapture(newValue) }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static var position: SIMD2<Float> { self._instance.getAbsolute() }
 | 
				
			||||||
 | 
					  public static var relative: SIMD2<Float> { self._instance.getDelta() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static func down(_ btn: Buttons) -> Bool {
 | 
				
			||||||
 | 
					    btn.isSubset(of: self._instance._btns)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public static func pressed(_ btn: Buttons) -> Bool {
 | 
				
			||||||
 | 
					    btn.isSubset(of: self._instance._btns.intersection(self._instance._btnImpulse))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  public static func released(_ btn: Buttons) -> Bool {
 | 
				
			||||||
 | 
					    btn.isSubset(of: self._instance._btnImpulse.subtracting(self._instance._btns))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //MARK: - Private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static let _instance = Mouse()
 | 
				
			||||||
 | 
					  public static var instance: Mouse { self._instance }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private var _window: OpaquePointer!
 | 
				
			||||||
 | 
					  private var _captured: Bool = false
 | 
				
			||||||
 | 
					  private var _abs: SIMD2<Float> = .zero, _delta: SIMD2<Float> = .zero
 | 
				
			||||||
 | 
					  private var _btns: Buttons = [], _btnImpulse: Buttons = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private func getCapture() -> Bool { self._captured }
 | 
				
			||||||
 | 
					  private func setCapture(_ toggle: Bool) {
 | 
				
			||||||
 | 
					    let sdlBool = toggle ? SDL_TRUE : SDL_FALSE
 | 
				
			||||||
 | 
					    if SDL_SetRelativeMouseMode(sdlBool) >= 0 && SDL_SetWindowMouseGrab(self._window, sdlBool) >= 0 {
 | 
				
			||||||
 | 
					      self._captured = toggle
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private func getAbsolute() -> SIMD2<Float> { self._abs }
 | 
				
			||||||
 | 
					  private func getDelta() -> SIMD2<Float> { self._delta }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func buttonEvent(btn: UInt32, state: UInt8) {
 | 
				
			||||||
 | 
					    if state == SDL_PRESSED {
 | 
				
			||||||
 | 
					      self._btns.formUnion(.init(rawValue: btn.buttonMask))
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      self._btns.subtract(.init(rawValue: btn.buttonMask))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    self._btnImpulse.formUnion(.init(rawValue: btn.buttonMask))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func motionEvent(absolute: SIMD2<Float>, relative: SIMD2<Float>) {
 | 
				
			||||||
 | 
					    self._abs = absolute
 | 
				
			||||||
 | 
					    self._delta += relative
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func newFrame(_ window: OpaquePointer) {
 | 
				
			||||||
 | 
					    self._window = window
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let grabbedFlag = SDL_WindowFlags(SDL_WINDOW_MOUSE_GRABBED)
 | 
				
			||||||
 | 
					    self._captured = (SDL_GetWindowFlags(window) & grabbedFlag) == grabbedFlag
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self._delta = .zero
 | 
				
			||||||
 | 
					    self._btnImpulse = []
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fileprivate extension UInt32 {
 | 
				
			||||||
 | 
					  var buttonMask: UInt32 { 1 &<< (self &- 1) }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -50,7 +50,7 @@ struct Player {
 | 
				
			|||||||
    // Read controller input (if one is plugged in)
 | 
					    // Read controller input (if one is plugged in)
 | 
				
			||||||
    if let pad = GameController.current?.state {
 | 
					    if let pad = GameController.current?.state {
 | 
				
			||||||
      turning += pad.rightStick.radialDeadzone(min: 0.1, max: 1)
 | 
					      turning += pad.rightStick.radialDeadzone(min: 0.1, max: 1)
 | 
				
			||||||
      movement += pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
 | 
					      movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
 | 
				
			||||||
      flying += (pad.down(.rightBumper) ? 1 : 0) - (pad.down(.leftBumper) ? 1 : 0)
 | 
					      flying += (pad.down(.rightBumper) ? 1 : 0) - (pad.down(.leftBumper) ? 1 : 0)
 | 
				
			||||||
      if pad.pressed(.east) {
 | 
					      if pad.pressed(.east) {
 | 
				
			||||||
        jumpInput = .press
 | 
					        jumpInput = .press
 | 
				
			||||||
@ -72,19 +72,22 @@ struct Player {
 | 
				
			|||||||
    if Keyboard.down(.s) { movement.y += 1 }
 | 
					    if Keyboard.down(.s) { movement.y += 1 }
 | 
				
			||||||
    if Keyboard.down(.a) { movement.x -= 1 }
 | 
					    if Keyboard.down(.a) { movement.x -= 1 }
 | 
				
			||||||
    if Keyboard.down(.d) { movement.x += 1 }
 | 
					    if Keyboard.down(.d) { movement.x += 1 }
 | 
				
			||||||
    if Keyboard.down(.up)    { turning.y -= 1 }
 | 
					    if Keyboard.down(.q) { flying += 1 }
 | 
				
			||||||
    if Keyboard.down(.down)  { turning.y += 1 }
 | 
					    if Keyboard.down(.e) { flying -= 1 }
 | 
				
			||||||
    if Keyboard.down(.left)  { turning.x -= 1 }
 | 
					    if Keyboard.pressed(.tab) { Mouse.capture = !Mouse.capture }
 | 
				
			||||||
    if Keyboard.down(.right) { turning.x += 1 }
 | 
					 | 
				
			||||||
    if Keyboard.down(.tab) { flying += 1 }
 | 
					 | 
				
			||||||
    if Keyboard.pressed(.q) { place   = true }
 | 
					 | 
				
			||||||
    if Keyboard.pressed(.e) { destroy = true }
 | 
					 | 
				
			||||||
    if Keyboard.pressed(.space) {
 | 
					    if Keyboard.pressed(.space) {
 | 
				
			||||||
      jumpInput = .press
 | 
					      jumpInput = .press
 | 
				
			||||||
    } else if jumpInput != .press && Keyboard.down(.space) {
 | 
					    } else if jumpInput != .press && Keyboard.down(.space) {
 | 
				
			||||||
      jumpInput = .held
 | 
					      jumpInput = .held
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Read mouse input
 | 
				
			||||||
 | 
					    if Mouse.pressed(.left)  { destroy = true }
 | 
				
			||||||
 | 
					    if Mouse.pressed(.right) { place   = true }
 | 
				
			||||||
 | 
					    if Mouse.capture {
 | 
				
			||||||
 | 
					      self._rotation += Mouse.relative / 2048 * Float.pi
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Turning input
 | 
					    // Turning input
 | 
				
			||||||
    self._rotation += turning * deltaTime * 3.0
 | 
					    self._rotation += turning * deltaTime * 3.0
 | 
				
			||||||
    if self._rotation.x < 0.0 {
 | 
					    if self._rotation.x < 0.0 {
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user