Files
CavesOfSwift/Sources/JolkEngine/Input/GamePad.swift

199 lines
5.2 KiB
Swift

import Foundation
import SDL2
import Maths
public class GamePad
{
public enum Axes
{
case leftStickX, leftStickY
case rightStickX, rightStickY
case leftTrigger, rightTrigger
}
public enum Buttons
{
case east, south, north, west
case select, start, guide, mic, touchPad
case leftStick, rightStick
case leftBumper, rightBumper
case dpadLeft, dpadRight, dpadUp, dpadDown
case paddle1, paddle2, paddle3, paddle4
}
public struct State
{
public func down(_ btn: Buttons) -> Bool { btnState(btn) & GamePad._DOWN == GamePad._DOWN }
public func pressed(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._PRESS }
public func released(_ btn: Buttons) -> Bool { btnState(btn) == GamePad._RELEASE }
public func axis(_ axis: Axes) -> Float
{
let axisRescale = 1.0 / Float(Int16.max)
return Float(rawAxis(axis)) * axisRescale
}
@inline(__always) func rawAxis(_ axis: Axes) -> Int16
{
_axes[Int(axis.sdlEnum.rawValue)]
}
@inline(__always) private func btnState(_ btn: Buttons) -> UInt8
{
_btns[Int(btn.sdlEnum.rawValue)]
}
private let _btns: [UInt8], _axes: [Int16]
internal init(btns: [UInt8], axes: [Int16])
{
self._btns = btns
self._axes = axes
}
}
public var state: State
{
.init(btns: _btns, axes: _axes)
}
@inline(__always) public static var current: GamePad?
{
let input = Input.instance
return if let id = input.firstPadID { input.getPad(id: id) } else { nil }
}
private static let _UP = UInt8(0b00), _DOWN = UInt8(0b10), _IMPULSE = UInt8(0b01)
private static let _PRESS = _DOWN | _IMPULSE
private static let _RELEASE = _UP | _IMPULSE
private var _btns = [UInt8](repeating: 0, count: Int(SDL_CONTROLLER_BUTTON_MAX.rawValue))
private var _axes = [Int16](repeating: 0, count: Int(SDL_CONTROLLER_AXIS_MAX.rawValue))
private var _joyID: SDL_JoystickID, _sdlPad: OpaquePointer
internal var instanceID: SDL_JoystickID { _joyID }
public var name: String { String(cString: SDL_GameControllerName(_sdlPad)) }
internal static func open(device: Int32) -> GamePad?
{
guard let sdlPad = SDL_GameControllerOpen(device) else { return nil }
let joyID = SDL_JoystickGetDeviceInstanceID(device)
return GamePad(instance: joyID, pad: sdlPad)
}
private init(instance: SDL_JoystickID, pad: OpaquePointer)
{
self._joyID = instance
self._sdlPad = pad
}
internal func newTick()
{
_btns = _btns.map({ $0 & ~Self._IMPULSE })
}
internal func close()
{
SDL_GameControllerClose(_sdlPad)
}
internal func buttonPressEvent(_ btn: UInt8)
{
_btns[Int(btn)] = Self._PRESS
}
internal func buttonReleaseEvent(_ btn: UInt8)
{
_btns[Int(btn)] = Self._RELEASE
}
internal func axisEvent(_ axis: UInt8, _ value: Int16)
{
_axes[Int(axis)] = value
}
}
public extension GamePad.State
{
var leftStick: Vec2f { Vec2f(axis(.leftStickX), axis(.leftStickY)) }
var rightStick: Vec2f { Vec2f(axis(.rightStickX), axis(.rightStickY)) }
}
internal extension GamePad.Axes
{
var sdlEnum: SDL_GameControllerAxis
{
switch self
{
case .leftStickX: SDL_CONTROLLER_AXIS_LEFTX
case .leftStickY: SDL_CONTROLLER_AXIS_LEFTY
case .rightStickX: SDL_CONTROLLER_AXIS_RIGHTX
case .rightStickY: SDL_CONTROLLER_AXIS_RIGHTY
case .leftTrigger: SDL_CONTROLLER_AXIS_TRIGGERLEFT
case .rightTrigger: SDL_CONTROLLER_AXIS_TRIGGERRIGHT
}
}
}
internal extension GamePad.Buttons
{
var sdlEnum: SDL_GameControllerButton
{
switch self
{
case .east: SDL_CONTROLLER_BUTTON_B
case .south: SDL_CONTROLLER_BUTTON_A
case .north: SDL_CONTROLLER_BUTTON_Y
case .west: SDL_CONTROLLER_BUTTON_X
case .start: SDL_CONTROLLER_BUTTON_START
case .select: SDL_CONTROLLER_BUTTON_BACK
case .guide: SDL_CONTROLLER_BUTTON_GUIDE
case .mic: SDL_CONTROLLER_BUTTON_MISC1
case .touchPad: SDL_CONTROLLER_BUTTON_TOUCHPAD
case .leftStick: SDL_CONTROLLER_BUTTON_LEFTSTICK
case .rightStick: SDL_CONTROLLER_BUTTON_RIGHTSTICK
case .leftBumper: SDL_CONTROLLER_BUTTON_LEFTSHOULDER
case .rightBumper: SDL_CONTROLLER_BUTTON_RIGHTSHOULDER
case .dpadUp: SDL_CONTROLLER_BUTTON_DPAD_UP
case .dpadDown: SDL_CONTROLLER_BUTTON_DPAD_DOWN
case .dpadLeft: SDL_CONTROLLER_BUTTON_DPAD_LEFT
case .dpadRight: SDL_CONTROLLER_BUTTON_DPAD_RIGHT
case .paddle1: SDL_CONTROLLER_BUTTON_PADDLE1
case .paddle2: SDL_CONTROLLER_BUTTON_PADDLE2
case .paddle3: SDL_CONTROLLER_BUTTON_PADDLE3
case .paddle4: SDL_CONTROLLER_BUTTON_PADDLE4
}
}
}
public extension Vector2 where Scalar: FloatingPoint
{
func cardinalDeadzone(min: Scalar, max: Scalar) -> Self
{
Self(self.x.axisDeadzone(min, max), self.y.axisDeadzone(min, max))
}
func radialDeadzone(min: Scalar, max: Scalar) -> Self
{
let magnitude = self.len
if magnitude == .zero || magnitude < min { return Self.zero }
if magnitude > max { return self / magnitude }
let rescale = (magnitude - min) / (max - min)
return self / magnitude * rescale
}
}
fileprivate extension FloatingPoint
{
@inline(__always) internal func axisDeadzone(_ min: Self, _ max: Self) -> Self
{
let xabs = abs(self)
return if xabs <= min { 0 }
else if xabs >= max { Self(signOf: self, magnitudeOf: 1) }
else { Self(signOf: self, magnitudeOf: xabs - min) / (max - min) }
}
}