mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 10:59:39 +00:00 
			
		
		
		
	use controller for moving around a test plane
This commit is contained in:
		@ -17,13 +17,13 @@ public class Application {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private func initialize() -> ApplicationExecutionState {
 | 
					  private func initialize() -> ApplicationExecutionState {
 | 
				
			||||||
    guard SDL_Init(SDL_INIT_VIDEO) >= 0 else {
 | 
					    guard SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD) >= 0 else {
 | 
				
			||||||
      printErr("SDL_Init() error: \(String(cString: SDL_GetError()))")
 | 
					      printErr("SDL_Init() error: \(String(cString: SDL_GetError()))")
 | 
				
			||||||
      return .exitFailure
 | 
					      return .exitFailure
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create SDL window
 | 
					    // Create SDL window
 | 
				
			||||||
    var windowFlags = SDL_WindowFlags(0)
 | 
					    var windowFlags = SDL_WindowFlags(SDL_WINDOW_METAL)
 | 
				
			||||||
    if cfg.flags.contains(.resizable) {
 | 
					    if cfg.flags.contains(.resizable) {
 | 
				
			||||||
      windowFlags |= SDL_WindowFlags(SDL_WINDOW_RESIZABLE)
 | 
					      windowFlags |= SDL_WindowFlags(SDL_WINDOW_RESIZABLE)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -67,6 +67,10 @@ public class Application {
 | 
				
			|||||||
    SDL_Quit()
 | 
					    SDL_Quit()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private func beginHandleEvents() {
 | 
				
			||||||
 | 
					    GameController.instance.newFrame()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private func handleEvent(_ event: SDL_Event) -> ApplicationExecutionState {
 | 
					  private func handleEvent(_ event: SDL_Event) -> ApplicationExecutionState {
 | 
				
			||||||
    switch SDL_EventType(event.type) {
 | 
					    switch SDL_EventType(event.type) {
 | 
				
			||||||
    case SDL_EVENT_QUIT:
 | 
					    case SDL_EVENT_QUIT:
 | 
				
			||||||
@ -81,6 +85,23 @@ public class Application {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      return .running
 | 
					      return .running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case SDL_EVENT_GAMEPAD_ADDED:
 | 
				
			||||||
 | 
					      if SDL_IsGamepad(event.gdevice.which) != SDL_FALSE {
 | 
				
			||||||
 | 
					        GameController.instance.connectedEvent(id: event.gdevice.which)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return .running
 | 
				
			||||||
 | 
					    case SDL_EVENT_GAMEPAD_REMOVED:
 | 
				
			||||||
 | 
					      GameController.instance.removedEvent(id: event.gdevice.which)
 | 
				
			||||||
 | 
					      return .running
 | 
				
			||||||
 | 
					    case SDL_EVENT_GAMEPAD_AXIS_MOTION:
 | 
				
			||||||
 | 
					      GameController.instance.axisEvent(id: event.gaxis.which,
 | 
				
			||||||
 | 
					        axis: SDL_GamepadAxis(Int32(event.gaxis.axis)), value: event.gaxis.value)
 | 
				
			||||||
 | 
					      return .running
 | 
				
			||||||
 | 
					    case SDL_EVENT_GAMEPAD_BUTTON_DOWN, SDL_EVENT_GAMEPAD_BUTTON_UP:
 | 
				
			||||||
 | 
					      GameController.instance.buttonEvent(id: event.gbutton.which,
 | 
				
			||||||
 | 
					        btn: SDL_GamepadButton(Int32(event.gbutton.button)), state: event.gbutton.state)
 | 
				
			||||||
 | 
					      return .running
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
 | 
					    case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
 | 
				
			||||||
      let backBufferSize = SIMD2(Int(event.window.data1), Int(event.window.data2))
 | 
					      let backBufferSize = SIMD2(Int(event.window.data1), Int(event.window.data2))
 | 
				
			||||||
      renderer!.resize(size: backBufferSize)
 | 
					      renderer!.resize(size: backBufferSize)
 | 
				
			||||||
@ -120,6 +141,7 @@ public class Application {
 | 
				
			|||||||
    var res = initialize()
 | 
					    var res = initialize()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    quit: while res == .running {
 | 
					    quit: while res == .running {
 | 
				
			||||||
 | 
					      beginHandleEvents()
 | 
				
			||||||
      var event = SDL_Event()
 | 
					      var event = SDL_Event()
 | 
				
			||||||
      while SDL_PollEvent(&event) > 0 {
 | 
					      while SDL_PollEvent(&event) > 0 {
 | 
				
			||||||
        res = handleEvent(event)
 | 
					        res = handleEvent(event)
 | 
				
			||||||
 | 
				
			|||||||
@ -11,6 +11,8 @@ add_executable(Voxelotl MACOSX_BUNDLE
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  NSImageLoader.swift
 | 
					  NSImageLoader.swift
 | 
				
			||||||
  Renderer.swift
 | 
					  Renderer.swift
 | 
				
			||||||
 | 
					  GameController.swift
 | 
				
			||||||
 | 
					  Camera.swift
 | 
				
			||||||
  FPSCalculator.swift
 | 
					  FPSCalculator.swift
 | 
				
			||||||
  Application.swift
 | 
					  Application.swift
 | 
				
			||||||
  main.swift)
 | 
					  main.swift)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										37
									
								
								Sources/Voxelotl/Camera.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								Sources/Voxelotl/Camera.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					import simd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct Camera {
 | 
				
			||||||
 | 
					  private var position = SIMD3<Float>.zero
 | 
				
			||||||
 | 
					  private var rotation = SIMD2<Float>.zero
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var view: matrix_float4x4 {
 | 
				
			||||||
 | 
					    .rotate(yawPitch: rotation) * .translate(-position)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mutating func update(deltaTime: Float) {
 | 
				
			||||||
 | 
					    if let pad = GameController.current?.state {
 | 
				
			||||||
 | 
					      let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1)
 | 
				
			||||||
 | 
					      rotation += turning * deltaTime
 | 
				
			||||||
 | 
					      if rotation.x < 0.0 {
 | 
				
			||||||
 | 
					        rotation.x += .pi * 2
 | 
				
			||||||
 | 
					      } else if rotation.x > .pi * 2 {
 | 
				
			||||||
 | 
					        rotation.x -= .pi * 2
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      rotation.y = rotation.y.clamp(-.pi * 0.5, .pi * 0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let rotc = cos(rotation.x), rots = sin(rotation.x)
 | 
				
			||||||
 | 
					      position += .init(
 | 
				
			||||||
 | 
					        movement.x * rotc - movement.y * rots,
 | 
				
			||||||
 | 
					        0,
 | 
				
			||||||
 | 
					        movement.y * rotc + movement.x * rots
 | 
				
			||||||
 | 
					      ) * deltaTime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if pad.pressed(.back) {
 | 
				
			||||||
 | 
					        position = .zero
 | 
				
			||||||
 | 
					        rotation = .zero
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -4,4 +4,7 @@ public extension FloatingPoint {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @inline(__always) func lerp(_ a: Self, _ b: Self) -> Self { b * self + a * (1 - self) }
 | 
					  @inline(__always) func lerp(_ a: Self, _ b: Self) -> Self { b * self + a * (1 - self) }
 | 
				
			||||||
  @inline(__always) func mlerp(_ a: Self, _ b: Self) -> Self { a + (b - a) * self }
 | 
					  @inline(__always) func mlerp(_ a: Self, _ b: Self) -> Self { a + (b - a) * self }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @inline(__always) func clamp(_ a: Self, _ b: Self) -> Self { min(max(self, a), b) }
 | 
				
			||||||
 | 
					  @inline(__always) func saturate() -> Self { self.clamp(0, 1) }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										224
									
								
								Sources/Voxelotl/GameController.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								Sources/Voxelotl/GameController.swift
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,224 @@
 | 
				
			|||||||
 | 
					import SDL3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class GameController {
 | 
				
			||||||
 | 
					  public struct Pad {
 | 
				
			||||||
 | 
					    public enum Axes {
 | 
				
			||||||
 | 
					      case leftStickX, leftStickY
 | 
				
			||||||
 | 
					      case rightStickX, rightStickY
 | 
				
			||||||
 | 
					      case leftTrigger, rightTrigger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      internal var sdlEnum: SDL_GamepadAxis {
 | 
				
			||||||
 | 
					        switch self {
 | 
				
			||||||
 | 
					        case .leftStickX:   SDL_GAMEPAD_AXIS_LEFTX
 | 
				
			||||||
 | 
					        case .leftStickY:   SDL_GAMEPAD_AXIS_LEFTY
 | 
				
			||||||
 | 
					        case .rightStickX:  SDL_GAMEPAD_AXIS_RIGHTX
 | 
				
			||||||
 | 
					        case .rightStickY:  SDL_GAMEPAD_AXIS_RIGHTY
 | 
				
			||||||
 | 
					        case .leftTrigger:  SDL_GAMEPAD_AXIS_LEFT_TRIGGER
 | 
				
			||||||
 | 
					        case .rightTrigger: SDL_GAMEPAD_AXIS_RIGHT_TRIGGER
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public struct Buttons: OptionSet {
 | 
				
			||||||
 | 
					      public let rawValue: Int
 | 
				
			||||||
 | 
					      public init(rawValue: Int) { self.rawValue = rawValue }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      static let east        = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_EAST.rawValue)
 | 
				
			||||||
 | 
					      static let south       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_SOUTH.rawValue)
 | 
				
			||||||
 | 
					      static let north       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_NORTH.rawValue)
 | 
				
			||||||
 | 
					      static let west        = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_WEST.rawValue)
 | 
				
			||||||
 | 
					      static let back        = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_BACK.rawValue)
 | 
				
			||||||
 | 
					      static let start       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_START.rawValue)
 | 
				
			||||||
 | 
					      static let guide       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_GUIDE.rawValue)
 | 
				
			||||||
 | 
					      static let leftStick   = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_LEFT_STICK.rawValue)
 | 
				
			||||||
 | 
					      static let rightStick  = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_RIGHT_STICK.rawValue)
 | 
				
			||||||
 | 
					      static let leftBumper  = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_LEFT_SHOULDER.rawValue)
 | 
				
			||||||
 | 
					      static let rightBumper = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER.rawValue)
 | 
				
			||||||
 | 
					      static let dpadLeft    = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_DPAD_LEFT.rawValue)
 | 
				
			||||||
 | 
					      static let dpadRight   = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_DPAD_RIGHT.rawValue)
 | 
				
			||||||
 | 
					      static let dpadUp      = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_DPAD_UP.rawValue)
 | 
				
			||||||
 | 
					      static let dpadDown    = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_DPAD_DOWN.rawValue)
 | 
				
			||||||
 | 
					      static let misc1       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_MISC1.rawValue)
 | 
				
			||||||
 | 
					      static let misc2       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_MISC2.rawValue)
 | 
				
			||||||
 | 
					      static let misc3       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_MISC3.rawValue)
 | 
				
			||||||
 | 
					      static let misc4       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_MISC4.rawValue)
 | 
				
			||||||
 | 
					      static let misc5       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_MISC5.rawValue)
 | 
				
			||||||
 | 
					      static let misc6       = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_MISC6.rawValue)
 | 
				
			||||||
 | 
					      static let paddle1     = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE1.rawValue)
 | 
				
			||||||
 | 
					      static let paddle2     = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE1.rawValue)
 | 
				
			||||||
 | 
					      static let paddle3     = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_RIGHT_PADDLE2.rawValue)
 | 
				
			||||||
 | 
					      static let paddle4     = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_LEFT_PADDLE2.rawValue)
 | 
				
			||||||
 | 
					      static let touchPad    = Self(rawValue: 1 << SDL_GAMEPAD_BUTTON_TOUCHPAD.rawValue)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public struct State {
 | 
				
			||||||
 | 
					      private let _axes: [Int16]
 | 
				
			||||||
 | 
					      private let _btns: Buttons, _btnImpulse: Buttons
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      internal init(axes: [Int16], btns: Buttons, btnImpulse: Buttons) {
 | 
				
			||||||
 | 
					        self._axes = axes
 | 
				
			||||||
 | 
					        self._btns = btns
 | 
				
			||||||
 | 
					        self._btnImpulse = btnImpulse
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      public func axis(_ axis: Axes) -> Float {
 | 
				
			||||||
 | 
					        let raw = rawAxis(axis)
 | 
				
			||||||
 | 
					        let rescale = raw < 0 ? 1 / Float(-Int(Int16.min)) : 1 / Float(Int16.max)
 | 
				
			||||||
 | 
					        return Float(raw) * rescale
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      @inline(__always) func rawAxis(_ axis: Axes) -> Int16 {
 | 
				
			||||||
 | 
					        _axes[Int(axis.sdlEnum.rawValue)]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      public func down(_ btn: Buttons) -> Bool {
 | 
				
			||||||
 | 
					        btn.isSubset(of: _btns)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      public func pressed(_ btn: Buttons) -> Bool {
 | 
				
			||||||
 | 
					        btn.isSubset(of: _btns.intersection(_btnImpulse))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      public func released(_ btn: Buttons) -> Bool {
 | 
				
			||||||
 | 
					        btn.isSubset(of: _btnImpulse.subtracting(_btns))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public var name: String { String(cString: SDL_GetGamepadName(_sdlPad)) }
 | 
				
			||||||
 | 
					    public var state: State {
 | 
				
			||||||
 | 
					      .init(
 | 
				
			||||||
 | 
					        axes: self._axes,
 | 
				
			||||||
 | 
					        btns: self._btnCur,
 | 
				
			||||||
 | 
					        btnImpulse: self._btnCur.symmetricDifference(self._btnPrv))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //MARK: - Private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var _joyInstance: SDL_JoystickID, _sdlPad: OpaquePointer
 | 
				
			||||||
 | 
					    private var _axes = [Int16](repeating: 0, count: Int(SDL_GAMEPAD_AXIS_MAX.rawValue))
 | 
				
			||||||
 | 
					    private var _btnCur: Buttons = [], _btnPrv: Buttons = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal var instanceID: SDL_JoystickID { _joyInstance }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private init(instance: SDL_JoystickID, pad: OpaquePointer) {
 | 
				
			||||||
 | 
					      self._joyInstance = instance
 | 
				
			||||||
 | 
					      self._sdlPad = pad
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal static func open(joystickID: SDL_JoystickID) -> Self? {
 | 
				
			||||||
 | 
					      return if let sdlPad = SDL_OpenGamepad(joystickID) {
 | 
				
			||||||
 | 
					        .init(instance: joystickID, pad: sdlPad)
 | 
				
			||||||
 | 
					      } else { nil }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal func close() {
 | 
				
			||||||
 | 
					      SDL_CloseGamepad(self._sdlPad)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal mutating func buttonEvent(_ btn: SDL_GamepadButton, _ down: Bool) {
 | 
				
			||||||
 | 
					      if down {
 | 
				
			||||||
 | 
					        self._btnCur.formUnion(.init(rawValue: 1 << btn.rawValue))
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        self._btnCur.subtract(.init(rawValue: 1 << btn.rawValue))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal mutating func axisEvent(_ axis: SDL_GamepadAxis, _ value: Int16) {
 | 
				
			||||||
 | 
					      self._axes[Int(axis.rawValue)] = value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal mutating func newTick() {
 | 
				
			||||||
 | 
					      self._btnPrv = self._btnCur
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static func getPad(id: Int32) -> Pad? {
 | 
				
			||||||
 | 
					    _instance._pads[SDL_JoystickID(id)] ?? nil
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @inline(__always) public static var current: Pad? {
 | 
				
			||||||
 | 
					    getPad(id: Int32(_instance._firstID))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  //MARK: - Private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static let _instance = GameController()
 | 
				
			||||||
 | 
					  public static var instance: GameController { _instance }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private var _pads = Dictionary<SDL_JoystickID, Pad>()
 | 
				
			||||||
 | 
					  private var _firstID: SDL_JoystickID = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func connectedEvent(id: SDL_JoystickID) {
 | 
				
			||||||
 | 
					    if _pads.keys.contains(id) {
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if let pad = Pad.open(joystickID: id) {
 | 
				
			||||||
 | 
					      printErr("Using gamepad #\(pad.instanceID), \"\(pad.name)\"")
 | 
				
			||||||
 | 
					      if self._firstID == 0 {
 | 
				
			||||||
 | 
					        self._firstID = id
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      self._pads[id] = pad
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func removedEvent(id: SDL_JoystickID) {
 | 
				
			||||||
 | 
					    if let pad = self._pads.removeValue(forKey: id) {
 | 
				
			||||||
 | 
					      pad.close()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if id == _firstID {
 | 
				
			||||||
 | 
					      _firstID = _pads.keys.first ?? 0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func buttonEvent(id: SDL_JoystickID, btn: SDL_GamepadButton, state: UInt8) {
 | 
				
			||||||
 | 
					    _pads[id]?.buttonEvent(btn, state == SDL_PRESSED)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func axisEvent(id: SDL_JoystickID, axis: SDL_GamepadAxis, value: Int16) {
 | 
				
			||||||
 | 
					    _pads[id]?.axisEvent(axis, value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  internal func newFrame() {
 | 
				
			||||||
 | 
					    for idx in _pads.values.indices {
 | 
				
			||||||
 | 
					      _pads.values[idx].newTick()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//MARK: - Stick convenience functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public extension GameController.Pad.State {
 | 
				
			||||||
 | 
					  var leftStick: SIMD2<Float> {
 | 
				
			||||||
 | 
					    .init(axis(.leftStickX), axis(.leftStickY))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  var rightStick: SIMD2<Float> {
 | 
				
			||||||
 | 
					    .init(axis(.rightStickX), axis(.rightStickY))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public extension FloatingPoint {
 | 
				
			||||||
 | 
					  @inline(__always) internal func axisDeadzone(_ min: Self, _ max: Self) -> Self {
 | 
				
			||||||
 | 
					    let x = abs(self)
 | 
				
			||||||
 | 
					    return if x <= min { 0 } else if x >= max {
 | 
				
			||||||
 | 
					      .init(signOf: self, magnitudeOf: 1)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      .init(signOf: self, magnitudeOf: x - min) / (max - min)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public extension SIMD2 where Scalar: FloatingPoint {
 | 
				
			||||||
 | 
					  func cardinalDeadzone(min: Scalar, max: Scalar) -> Self {
 | 
				
			||||||
 | 
					    .init(self.x.axisDeadzone(min, max), self.y.axisDeadzone(min, max))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  func radialDeadzone(min: Scalar, max: Scalar) -> Self {
 | 
				
			||||||
 | 
					    let magnitude = (x * x + y * y).squareRoot()
 | 
				
			||||||
 | 
					    if magnitude == .zero || magnitude < min {
 | 
				
			||||||
 | 
					      return .zero
 | 
				
			||||||
 | 
					    } else if magnitude > max {
 | 
				
			||||||
 | 
					      return self / magnitude
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      let rescale = (magnitude - min) / (max - min)
 | 
				
			||||||
 | 
					      return self / magnitude * rescale
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -43,6 +43,19 @@ public extension simd_float4x4 {
 | 
				
			|||||||
      .init(0,  0, 0, 1))
 | 
					      .init(0,  0, 0, 1))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @inline(__always) static func rotate(yawPitch yp: SIMD2<T>) -> Self { rotate(yaw: yp.x, pitch: yp.y) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static func rotate(yaw ytheta: T, pitch xtheta: T) -> Self {
 | 
				
			||||||
 | 
					    let xc = cos(xtheta), xs = sin(xtheta)
 | 
				
			||||||
 | 
					    let yc = cos(ytheta), ys = sin(ytheta)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return .init(
 | 
				
			||||||
 | 
					      .init(yc, ys *  xs, -ys * xc, 0),
 | 
				
			||||||
 | 
					      .init( 0,       xc,       xs, 0),
 | 
				
			||||||
 | 
					      .init(ys, yc * -xs,  yc * xc, 0),
 | 
				
			||||||
 | 
					      .init( 0,        0,        0, 1))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static func orthographic(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self {
 | 
					  static func orthographic(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self {
 | 
				
			||||||
    let
 | 
					    let
 | 
				
			||||||
      invWidth  = 1 / (right - left),
 | 
					      invWidth  = 1 / (right - left),
 | 
				
			||||||
 | 
				
			|||||||
@ -280,26 +280,30 @@ class Renderer {
 | 
				
			|||||||
      zfar: -1.0)
 | 
					      zfar: -1.0)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var time: Float = 0  //FIXME: temp
 | 
					  //FIXME: temp
 | 
				
			||||||
 | 
					  var camera = Camera()
 | 
				
			||||||
 | 
					  var time: Float = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  func paint() throws {
 | 
					  func paint() throws {
 | 
				
			||||||
 | 
					  camera.update(deltaTime: 0.025)
 | 
				
			||||||
#if true
 | 
					#if true
 | 
				
			||||||
    let projection = matrix_float4x4.perspective(
 | 
					    let projection = matrix_float4x4.perspective(
 | 
				
			||||||
      verticalFov: Float(90.0).radians,
 | 
					      verticalFov: Float(60.0).radians,
 | 
				
			||||||
      aspect: aspectRatio,
 | 
					      aspect: aspectRatio,
 | 
				
			||||||
      near: 0.003,
 | 
					      near: 0.003,
 | 
				
			||||||
      far: 4)
 | 
					      far: 100)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    let projection = matrix_float4x4.orthographic(
 | 
					    let projection = matrix_float4x4.orthographic(
 | 
				
			||||||
      left: -aspectRatio, right: aspectRatio,
 | 
					      left: -aspectRatio, right: aspectRatio,
 | 
				
			||||||
      bottom: -1, top: 1,
 | 
					      bottom: -1, top: 1,
 | 
				
			||||||
      near: 0, far: -4)
 | 
					      near: 0, far: -4)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    let view = matrix_float4x4.identity
 | 
					    let view = camera.view
 | 
				
			||||||
    let model: matrix_float4x4 =
 | 
					    let model: matrix_float4x4 =
 | 
				
			||||||
      .translate(.init(0, sin(time * 0.5) * 0.75, -2)) *
 | 
					      .translate(.init(0, -1, 0)) * .scale(.init(10, 0.1, 10))
 | 
				
			||||||
      .scale(0.5) *
 | 
					      //.translate(.init(0, sin(time * 0.5) * 0.75, -2)) *
 | 
				
			||||||
      .rotate(y: time)
 | 
					      //.scale(0.5) *
 | 
				
			||||||
 | 
					      //.rotate(y: time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    time += 0.025
 | 
					    time += 0.025
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user