mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 02:59:37 +00:00 
			
		
		
		
	project mouse into view when clicking w/ mouse unlocked
This commit is contained in:
		@ -38,11 +38,16 @@ public class Application {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get window metrics
 | 
			
		||||
    var backBuffer = Size<Int32>.zero
 | 
			
		||||
    var backBuffer = Size<Int32>.zero, windowSize = Size<Int32>.zero
 | 
			
		||||
    guard SDL_GetWindowSizeInPixels(window, &backBuffer.w, &backBuffer.h) >= 0 else {
 | 
			
		||||
      printErr("SDL_GetWindowSizeInPixels() error: \(String(cString: SDL_GetError()))")
 | 
			
		||||
      return .exitFailure
 | 
			
		||||
    }
 | 
			
		||||
    guard SDL_GetWindowSize(window, &windowSize.w, &windowSize.h) >= 0 else {
 | 
			
		||||
      printErr("SDL_GetWindowSize() error: \(String(cString: SDL_GetError()))")
 | 
			
		||||
      return .exitFailure
 | 
			
		||||
    }
 | 
			
		||||
    Mouse.instance.setDPI(scale: SIMD2(Size<Float>(backBuffer) / Size<Float>(windowSize)))
 | 
			
		||||
 | 
			
		||||
    // Create Metal renderer
 | 
			
		||||
    view = SDL_Metal_CreateView(window)
 | 
			
		||||
 | 
			
		||||
@ -9,16 +9,18 @@ public final class Camera {
 | 
			
		||||
    static let viewProj   = Self(rawValue: 1 << 2)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private var _position: SIMD3<Float>
 | 
			
		||||
  private var _rotation: simd_quatf
 | 
			
		||||
  private var _position = SIMD3<Float>.zero
 | 
			
		||||
  private var _rotation = simd_quatf.identity
 | 
			
		||||
  private var _fieldOfView: Float
 | 
			
		||||
  private var _aspectRatio: Float
 | 
			
		||||
  private var _zNearFar: ClosedRange<Float>
 | 
			
		||||
  private var _viewport: Rect<Float>
 | 
			
		||||
 | 
			
		||||
  private var _dirty: Dirty
 | 
			
		||||
  private var _projection: matrix_float4x4
 | 
			
		||||
  private var _view: matrix_float4x4
 | 
			
		||||
  private var _viewProjection: matrix_float4x4
 | 
			
		||||
  private var _projection = matrix_identity_float4x4
 | 
			
		||||
  private var _view = matrix_identity_float4x4
 | 
			
		||||
  private var _viewProjection = matrix_identity_float4x4
 | 
			
		||||
  private var _invViewProjection = matrix_identity_float4x4
 | 
			
		||||
 | 
			
		||||
  public var position: SIMD3<Float> {
 | 
			
		||||
    get { self._position }
 | 
			
		||||
@ -59,8 +61,19 @@ public final class Camera {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public func setSize(_ size: Size<Int>) {
 | 
			
		||||
    self.aspectRatio = Float(size.w) / Float(size.h)
 | 
			
		||||
  public var viewport: Rect<Float> {
 | 
			
		||||
    get { self._viewport }
 | 
			
		||||
    set {
 | 
			
		||||
      self._viewport = newValue
 | 
			
		||||
      self.aspectRatio = Float(newValue.w) / Float(newValue.h)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  public var size: Size<Int> {
 | 
			
		||||
    get { Size<Int>(self._viewport.size) }
 | 
			
		||||
    set {
 | 
			
		||||
      self._viewport.size = Size<Float>(newValue)
 | 
			
		||||
      self.aspectRatio = Float(newValue.w) / Float(newValue.h)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public var range: ClosedRange<Float> {
 | 
			
		||||
@ -94,20 +107,64 @@ public final class Camera {
 | 
			
		||||
  public var viewProjection: matrix_float4x4 {
 | 
			
		||||
    if !self._dirty.isEmpty {
 | 
			
		||||
      self._viewProjection = self.projection * self.view
 | 
			
		||||
      self._invViewProjection = self._viewProjection.inverse
 | 
			
		||||
      self._dirty.remove(.viewProj)
 | 
			
		||||
    }
 | 
			
		||||
    return self._viewProjection
 | 
			
		||||
  }
 | 
			
		||||
  public var invViewProjection: matrix_float4x4 {
 | 
			
		||||
    if !self._dirty.isEmpty {
 | 
			
		||||
      self._viewProjection = self.projection * self.view
 | 
			
		||||
      self._invViewProjection = self._viewProjection.inverse
 | 
			
		||||
      self._dirty.remove(.viewProj)
 | 
			
		||||
    }
 | 
			
		||||
    return self._invViewProjection
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  init(fov: Float, size: Size<Int>, range: ClosedRange<Float>) {
 | 
			
		||||
    self._position = .zero
 | 
			
		||||
    self._rotation = .identity
 | 
			
		||||
    self._fieldOfView = fov.radians
 | 
			
		||||
    self._aspectRatio = Float(size.w) / Float(size.h)
 | 
			
		||||
    self._zNearFar = range
 | 
			
		||||
    self._viewport = .init(origin: .zero, size: Size<Float>(size))
 | 
			
		||||
    self._dirty = [ .projection, .view, .viewProj ]
 | 
			
		||||
    self._projection = .init()
 | 
			
		||||
    self._view = .init()
 | 
			
		||||
    self._viewProjection = .init()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public func screenRay(_ screen: SIMD2<Float>) -> SIMD3<Float> {
 | 
			
		||||
#if true
 | 
			
		||||
    simd_normalize(self.unproject(screen: SIMD3(screen, 1)) - self.unproject(screen: SIMD3(screen, 0)))
 | 
			
		||||
#else
 | 
			
		||||
    let inverse = self.projection.inverse
 | 
			
		||||
    var viewportCoord = screen - SIMD2(self._viewport.origin)
 | 
			
		||||
    viewportCoord = (viewportCoord * 2) / SIMD2(self.viewport.size) - 1
 | 
			
		||||
    return simd_normalize(self._rotation * inverse.project(SIMD3(viewportCoord.x, -viewportCoord.y, 1)))
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public func unproject(screen2D: SIMD2<Float>) -> SIMD3<Float> {
 | 
			
		||||
    self.unproject(screen: SIMD3(screen2D, self._zNearFar.lowerBound))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public func unproject(screen: SIMD3<Float>) -> SIMD3<Float> {
 | 
			
		||||
    let inverse = self.invViewProjection
 | 
			
		||||
 | 
			
		||||
    var viewportCoord = screen.xy - SIMD2(self._viewport.origin)
 | 
			
		||||
    viewportCoord = (viewportCoord * 2) / SIMD2(self._viewport.size) - 1
 | 
			
		||||
 | 
			
		||||
#if true
 | 
			
		||||
    return inverse.project(SIMD3(viewportCoord.x, -viewportCoord.y, screen.z))
 | 
			
		||||
#else
 | 
			
		||||
    let projected = inverse * SIMD4(viewportCoord.x, -viewportCoord.y, screen.z, 1)
 | 
			
		||||
    return projected.xyz * (1 / projected.w)
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public func project(world position: SIMD3<Float>) -> SIMD2<Float> {
 | 
			
		||||
    let viewport = self.viewProjection * SIMD4(position, 1)
 | 
			
		||||
    if viewport.w == 0 {
 | 
			
		||||
      // World point is exactly on focus point, screenpoint is undefined
 | 
			
		||||
      return .zero
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (viewport.xy + 1) * 0.5 * SIMD2(self._viewport.size)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ class Game: GameDelegate {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private func resetPlayer() {
 | 
			
		||||
    self.player.position = .init(repeating: 0.5) + .init(0, Float(Chunk.size), 0)
 | 
			
		||||
    self.player.position = .init(repeating: 0.5) + .up * Float(Chunk.size)
 | 
			
		||||
    self.player.velocity = .zero
 | 
			
		||||
    self.player.rotation = .init(.pi, 0)
 | 
			
		||||
  }
 | 
			
		||||
@ -85,9 +85,7 @@ class Game: GameDelegate {
 | 
			
		||||
      self.generateWorld()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    self.player.update(deltaTime: deltaTime, world: world)
 | 
			
		||||
    self.camera.position = player.eyePosition
 | 
			
		||||
    self.camera.rotation = player.eyeRotation
 | 
			
		||||
    self.player.update(deltaTime: deltaTime, world: world, camera: &camera)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  func draw(_ renderer: Renderer, _ time: GameTime) {
 | 
			
		||||
@ -103,21 +101,23 @@ class Game: GameDelegate {
 | 
			
		||||
      gloss: 75)
 | 
			
		||||
 | 
			
		||||
    var instances = world.instances
 | 
			
		||||
    if let position = player.rayhitPos {
 | 
			
		||||
      instances.append(
 | 
			
		||||
        Instance(
 | 
			
		||||
        position: player.rayhitPos,
 | 
			
		||||
          position: position,
 | 
			
		||||
          scale:    .init(repeating: 0.0725 * 0.5),
 | 
			
		||||
          rotation:
 | 
			
		||||
            .init(angle: totalTime * 3.0, axis: .init(0, 1, 0)) *
 | 
			
		||||
            .init(angle: totalTime * 1.5, axis: .init(1, 0, 0)) *
 | 
			
		||||
            .init(angle: totalTime * 0.7, axis: .init(0, 0, 1)),
 | 
			
		||||
          color:    .init(r: 0.5, g: 0.5, b: 1).linear))
 | 
			
		||||
    }
 | 
			
		||||
    if !instances.isEmpty {
 | 
			
		||||
      renderer.batch(instances: instances, material: material, environment: env, camera: self.camera)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  func resize(_ size: Size<Int>) {
 | 
			
		||||
    self.camera.setSize(size)
 | 
			
		||||
    self.camera.size = size
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,7 @@ public class Mouse {
 | 
			
		||||
 | 
			
		||||
  private var _window: OpaquePointer!
 | 
			
		||||
  private var _captured: Bool = false
 | 
			
		||||
  private var _dpiScale: SIMD2<Float> = .one
 | 
			
		||||
  private var _abs: SIMD2<Float> = .zero, _delta: SIMD2<Float> = .zero
 | 
			
		||||
  private var _btns: Buttons = [], _btnImpulse: Buttons = []
 | 
			
		||||
 | 
			
		||||
@ -48,7 +49,9 @@ public class Mouse {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private func getAbsolute() -> SIMD2<Float> { self._abs }
 | 
			
		||||
  internal func setDPI(scale: SIMD2<Float>) { self._dpiScale = scale }
 | 
			
		||||
 | 
			
		||||
  private func getAbsolute() -> SIMD2<Float> { self._abs * self._dpiScale }
 | 
			
		||||
  private func getDelta() -> SIMD2<Float> { self._delta }
 | 
			
		||||
 | 
			
		||||
  internal func buttonEvent(btn: UInt32, state: UInt8) {
 | 
			
		||||
 | 
			
		||||
@ -88,6 +88,19 @@ public extension simd_float4x4 {
 | 
			
		||||
      .init(0, 0, z, -1),
 | 
			
		||||
      .init(0, 0, w,  0))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  func project(_ v: SIMD3<Float>) -> SIMD3<Float> {
 | 
			
		||||
    let t = self.transpose, v = SIMD4(v, 1)
 | 
			
		||||
    return .init(simd_dot(v, t.columns.0), simd_dot(v, t.columns.1), simd_dot(v, t.columns.2))
 | 
			
		||||
      * (1 / simd_dot(v, t.columns.3))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static func * (lhs: Self, rhs: SIMD3<Float>) -> SIMD3<Float> {
 | 
			
		||||
    .init(
 | 
			
		||||
      simd_dot(lhs.columns.0.xyz, rhs),
 | 
			
		||||
      simd_dot(lhs.columns.1.xyz, rhs),
 | 
			
		||||
      simd_dot(lhs.columns.2.xyz, rhs))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension simd_quatf {
 | 
			
		||||
 | 
			
		||||
@ -43,12 +43,32 @@ public struct Size<T: AdditiveArithmetic>: Equatable {
 | 
			
		||||
extension Size where T: BinaryInteger {
 | 
			
		||||
  static var one: Self { .init(T(1), T(1)) }
 | 
			
		||||
 | 
			
		||||
  init<O>(_ other: Size<O>) where O: BinaryInteger {
 | 
			
		||||
  init<O: BinaryInteger>(_ other: Size<O>) {
 | 
			
		||||
    self.init(T(other.w), T(other.h))
 | 
			
		||||
  }
 | 
			
		||||
  init<O: BinaryFloatingPoint>(_ other: Size<O>) {
 | 
			
		||||
    self.init(T(other.w), T(other.h))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct Rect<T: AdditiveArithmetic>: Equatable {
 | 
			
		||||
extension Size where T: BinaryFloatingPoint {
 | 
			
		||||
  init<O: BinaryInteger>(_ other: Size<O>) {
 | 
			
		||||
    self.init(T(other.w), T(other.h))
 | 
			
		||||
  }
 | 
			
		||||
  init<O: BinaryFloatingPoint>(_ other: Size<O>) {
 | 
			
		||||
    self.init(T(other.w), T(other.h))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @inline(__always) public static func / (lhs: Self, rhs: Self) -> Self { .init(lhs.w / rhs.w, lhs.h / rhs.h) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension SIMD2 where Scalar: AdditiveArithmetic {
 | 
			
		||||
  init(_ size: Size<Scalar>) {
 | 
			
		||||
    self.init(size.w, size.h)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public struct Rect<T: AdditiveArithmetic>: Equatable {
 | 
			
		||||
  var x: T, y: T, w: T, h: T
 | 
			
		||||
 | 
			
		||||
  var origin: Point<T> {
 | 
			
		||||
@ -76,12 +96,12 @@ struct Rect<T: AdditiveArithmetic>: Equatable {
 | 
			
		||||
    self.h = size.h
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @inline(__always) static func == (lhs: Self, rhs: Self) -> Bool {
 | 
			
		||||
  @inline(__always) public static func == (lhs: Self, rhs: Self) -> Bool {
 | 
			
		||||
    lhs.x == rhs.x && lhs.y == rhs.y && lhs.w == rhs.w && lhs.h == rhs.h
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Rect where T: AdditiveArithmetic {
 | 
			
		||||
public extension Rect where T: AdditiveArithmetic {
 | 
			
		||||
  var left: T { x }
 | 
			
		||||
  var right: T { x + w }
 | 
			
		||||
  var up: T { y }
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,23 @@
 | 
			
		||||
import simd
 | 
			
		||||
 | 
			
		||||
extension SIMD3 {
 | 
			
		||||
  var xy: SIMD2<Scalar> {
 | 
			
		||||
    get { .init(self.x, self.y) }
 | 
			
		||||
    set {
 | 
			
		||||
      self.x = newValue.x
 | 
			
		||||
      self.y = newValue.y
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var xz: SIMD2<Scalar> {
 | 
			
		||||
    get { .init(self.x, self.z) }
 | 
			
		||||
    set {
 | 
			
		||||
      self.x = newValue.x
 | 
			
		||||
      self.z = newValue.y
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension SIMD3 where Scalar: FloatingPoint {
 | 
			
		||||
  @inline(__always) static var X: Self      { Self(1, 0, 0) }
 | 
			
		||||
  @inline(__always) static var Y: Self      { Self(0, 1, 0) }
 | 
			
		||||
@ -10,3 +30,42 @@ extension SIMD3 where Scalar: FloatingPoint {
 | 
			
		||||
  @inline(__always) static var forward: Self { -Z }
 | 
			
		||||
  @inline(__always) static var back: Self    {  Z }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension SIMD3 where Scalar == Float {
 | 
			
		||||
  static func * (q: simd_quatf, v: Self) -> Self {
 | 
			
		||||
#if true
 | 
			
		||||
    let q = simd_inverse(q)
 | 
			
		||||
#else
 | 
			
		||||
    let q = simd_quatf(real: q.real, imag: -q.imag)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if true
 | 
			
		||||
    var out = q.imag * 2 * simd_dot(q.imag, v)
 | 
			
		||||
    out += v * (q.real * q.real - simd_dot(q.imag, q.imag))
 | 
			
		||||
    return out + simd_cross(q.imag, v) * 2 * q.real
 | 
			
		||||
#else
 | 
			
		||||
    let uv = simd_cross(q.imag, v)
 | 
			
		||||
    let uuv = simd_cross(q.imag, uv)
 | 
			
		||||
    return v + ((uv * q.real) + uuv) * 2
 | 
			
		||||
#endif
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension SIMD4 {
 | 
			
		||||
  var xy: SIMD2<Scalar> {
 | 
			
		||||
    get { .init(self.x, self.y) }
 | 
			
		||||
    set {
 | 
			
		||||
      self.x = newValue.x
 | 
			
		||||
      self.y = newValue.y
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var xyz: SIMD3<Scalar> {
 | 
			
		||||
    get { .init(self.x, self.y, self.z) }
 | 
			
		||||
    set {
 | 
			
		||||
      self.x = newValue.x
 | 
			
		||||
      self.y = newValue.y
 | 
			
		||||
      self.z = newValue.z
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ struct Player {
 | 
			
		||||
  static let flySpeedCoeff: Float = 36
 | 
			
		||||
  static let jumpVelocity: Float = 7
 | 
			
		||||
  static let maxVelocity: Float = 160
 | 
			
		||||
  static let blockReach: Float = 3.8
 | 
			
		||||
 | 
			
		||||
  private var _position = SIMD3<Float>.zero
 | 
			
		||||
  private var _velocity = SIMD3<Float>.zero
 | 
			
		||||
@ -24,8 +25,9 @@ struct Player {
 | 
			
		||||
 | 
			
		||||
  private var _onGround: Bool = false
 | 
			
		||||
  private var _shouldJump: Optional<Float> = .none
 | 
			
		||||
  private var _useMouseDir: Bool = false
 | 
			
		||||
 | 
			
		||||
  public var rayhitPos = SIMD3<Float>.zero
 | 
			
		||||
  public var rayhitPos: Optional<SIMD3<Float>> = nil
 | 
			
		||||
  private var prevLeftTrigger: Float = 0, prevRightTrigger: Float = 0
 | 
			
		||||
 | 
			
		||||
  public var position: SIMD3<Float> { get { self._position } set { self._position = newValue } }
 | 
			
		||||
@ -40,7 +42,7 @@ struct Player {
 | 
			
		||||
 | 
			
		||||
  enum JumpInput { case off, press, held }
 | 
			
		||||
 | 
			
		||||
  mutating func update(deltaTime: Float, world: World) {
 | 
			
		||||
  mutating func update(deltaTime: Float, world: World, camera: inout Camera) {
 | 
			
		||||
    var turning: SIMD2<Float> = .zero
 | 
			
		||||
    var movement: SIMD2<Float> = .zero
 | 
			
		||||
    var flying: Int = .zero
 | 
			
		||||
@ -49,7 +51,11 @@ struct Player {
 | 
			
		||||
 | 
			
		||||
    // Read controller input (if one is plugged in)
 | 
			
		||||
    if let pad = GameController.current?.state {
 | 
			
		||||
      turning += pad.rightStick.radialDeadzone(min: 0.1, max: 1)
 | 
			
		||||
      let turn = pad.rightStick.radialDeadzone(min: 0.1, max: 1)
 | 
			
		||||
      if turn != .zero {
 | 
			
		||||
        turning += turn
 | 
			
		||||
        self._useMouseDir = false
 | 
			
		||||
      }
 | 
			
		||||
      movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
 | 
			
		||||
      flying += (pad.down(.rightBumper) ? 1 : 0) - (pad.down(.leftBumper) ? 1 : 0)
 | 
			
		||||
      if pad.pressed(.east) {
 | 
			
		||||
@ -86,6 +92,9 @@ struct Player {
 | 
			
		||||
    if Mouse.pressed(.right) { place   = true }
 | 
			
		||||
    if Mouse.capture {
 | 
			
		||||
      self._rotation += Mouse.relative / 2048 * Float.pi
 | 
			
		||||
      self._useMouseDir = false
 | 
			
		||||
    } else if simd_length_squared(Mouse.relative) > Float.ulpOfOne {
 | 
			
		||||
      self._useMouseDir = true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Turning input
 | 
			
		||||
@ -121,10 +130,10 @@ struct Player {
 | 
			
		||||
    if movementMagnitude > 1.0 {
 | 
			
		||||
      movement /= movementMagnitude
 | 
			
		||||
    }
 | 
			
		||||
    let rotc = cos(self._rotation.x), rots = sin(self._rotation.x)
 | 
			
		||||
    let right = SIMD2(cos(self._rotation.x), sin(self._rotation.x))
 | 
			
		||||
    let forward = SIMD2(-right.y, right.x)
 | 
			
		||||
    let coeff = self._onGround ? Self.accelerationCoeff : Self.airAccelCoeff
 | 
			
		||||
    self._velocity.x += (movement.x * rotc - movement.y * rots) * coeff * deltaTime
 | 
			
		||||
    self._velocity.z += (movement.y * rotc + movement.x * rots) * coeff * deltaTime
 | 
			
		||||
    self._velocity.xz += (right * movement.x + forward * movement.y) * coeff * deltaTime
 | 
			
		||||
 | 
			
		||||
    // Flying and unflying
 | 
			
		||||
    self._velocity.y += Float(flying).clamp(-1, 1) * Self.flySpeedCoeff * deltaTime
 | 
			
		||||
@ -198,25 +207,36 @@ struct Player {
 | 
			
		||||
      self._velocity.z = 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Update camera
 | 
			
		||||
    camera.position = self.eyePosition
 | 
			
		||||
    camera.rotation = self.eyeRotation
 | 
			
		||||
 | 
			
		||||
    // Block picking
 | 
			
		||||
    if let hit = raycast(
 | 
			
		||||
      world: world,
 | 
			
		||||
      origin: self.eyePosition,
 | 
			
		||||
      direction: .forward * simd_matrix3x3(self.eyeRotation),
 | 
			
		||||
      maxDistance: 3.8
 | 
			
		||||
    ) {
 | 
			
		||||
      self.rayhitPos = hit.position
 | 
			
		||||
    let dir = !Mouse.capture && self._useMouseDir
 | 
			
		||||
      ? camera.screenRay(Mouse.position)
 | 
			
		||||
      : self.eyeRotation * .forward
 | 
			
		||||
    if let hit = raycast(world: world, origin: self.eyePosition, direction: dir, maxDistance: Self.blockReach) {
 | 
			
		||||
      if destroy || place {
 | 
			
		||||
        if destroy {
 | 
			
		||||
          world.setBlock(at: hit.map, type: .air)
 | 
			
		||||
      } else if place {
 | 
			
		||||
        } else {
 | 
			
		||||
          world.setBlock(at: hit.map.offset(by: hit.side), type: .solid(.white))
 | 
			
		||||
        }
 | 
			
		||||
        if let hit = raycast(world: world, origin: self.eyePosition, direction: dir, maxDistance: Self.blockReach) {
 | 
			
		||||
          self.rayhitPos = hit.position
 | 
			
		||||
        } else {
 | 
			
		||||
          self.rayhitPos = nil
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        self.rayhitPos = hit.position
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      self.rayhitPos = nil
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Ground friction
 | 
			
		||||
    if self._onGround {
 | 
			
		||||
      self._velocity.x /= 1.0 + Self.frictionCoeff * deltaTime
 | 
			
		||||
      self._velocity.z /= 1.0 + Self.frictionCoeff * deltaTime
 | 
			
		||||
      self._velocity.xz /= 1.0 + Self.frictionCoeff * deltaTime
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Limit maximum velocity
 | 
			
		||||
 | 
			
		||||
@ -6,31 +6,22 @@ public func raycast(
 | 
			
		||||
  direction: SIMD3<Float>,
 | 
			
		||||
  maxDistance: Float
 | 
			
		||||
) -> Optional<RaycastHit> {
 | 
			
		||||
  let deltaDistance = abs(SIMD3(repeating: simd_length(direction)) / direction)
 | 
			
		||||
  let directionLen = simd_length(direction)
 | 
			
		||||
  let deltaDistance = SIMD3(direction.indices.map {
 | 
			
		||||
    direction[$0] != 0.0 ? abs(directionLen / direction[$0]) : Float.greatestFiniteMagnitude
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  var mapPosition = SIMD3<Int>(floor(rayPosition))
 | 
			
		||||
  var sideDistance: SIMD3<Float> = .zero
 | 
			
		||||
  var step: SIMD3<Int> = .zero
 | 
			
		||||
  if direction.x < 0 {
 | 
			
		||||
    step.x = -1
 | 
			
		||||
    sideDistance.x = (rayPosition.x - Float(mapPosition.x)) * deltaDistance.x
 | 
			
		||||
  for i in 0..<3 {
 | 
			
		||||
    if direction[i] < 0 {
 | 
			
		||||
      step[i] = -1
 | 
			
		||||
      sideDistance[i] = (rayPosition[i] - Float(mapPosition[i])) * deltaDistance[i]
 | 
			
		||||
    } else {
 | 
			
		||||
    step.x = 1
 | 
			
		||||
    sideDistance.x = (Float(mapPosition.x) + 1 - rayPosition.x) * deltaDistance.x
 | 
			
		||||
      step[i] = 1
 | 
			
		||||
      sideDistance[i] = (Float(mapPosition[i]) + 1 - rayPosition[i]) * deltaDistance[i]
 | 
			
		||||
    }
 | 
			
		||||
  if direction.y < 0 {
 | 
			
		||||
    step.y = -1
 | 
			
		||||
    sideDistance.y = (rayPosition.y - Float(mapPosition.y)) * deltaDistance.y
 | 
			
		||||
  } else {
 | 
			
		||||
    step.y = 1
 | 
			
		||||
    sideDistance.y = (Float(mapPosition.y) + 1 - rayPosition.y) * deltaDistance.y
 | 
			
		||||
  }
 | 
			
		||||
  if direction.z < 0 {
 | 
			
		||||
    step.z = -1
 | 
			
		||||
    sideDistance.z = (rayPosition.z - Float(mapPosition.z)) * deltaDistance.z
 | 
			
		||||
  } else {
 | 
			
		||||
    step.z = 1
 | 
			
		||||
    sideDistance.z = (Float(mapPosition.z) + 1 - rayPosition.z) * deltaDistance.z
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Run digital differential analysis (3DDDA)
 | 
			
		||||
@ -59,24 +50,23 @@ public func raycast(
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Compute distance
 | 
			
		||||
    var distance: Float = if side.isX {
 | 
			
		||||
      abs(Float(mapPosition.x) - rayPosition.x + Float(1 - step.x) / 2) / direction.x
 | 
			
		||||
    let distance: Float = if side.isX {
 | 
			
		||||
      abs(Float(mapPosition.x) - rayPosition.x + Float(1 - step.x) * 0.5) * deltaDistance.x
 | 
			
		||||
    } else if side.isVertical {
 | 
			
		||||
      abs(Float(mapPosition.y) - rayPosition.y + Float(1 - step.y) / 2) / direction.y
 | 
			
		||||
      abs(Float(mapPosition.y) - rayPosition.y + Float(1 - step.y) * 0.5) * deltaDistance.y
 | 
			
		||||
    } else {
 | 
			
		||||
      abs(Float(mapPosition.z) - rayPosition.z + Float(1 - step.z) / 2) / direction.z
 | 
			
		||||
      abs(Float(mapPosition.z) - rayPosition.z + Float(1 - step.z) * 0.5) * deltaDistance.z
 | 
			
		||||
    }
 | 
			
		||||
    distance = abs(distance)
 | 
			
		||||
 | 
			
		||||
    // Bail out if we've exeeded the max raycast distance
 | 
			
		||||
    if distance > maxDistance {
 | 
			
		||||
      return nil
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // return a result if we hit something solid
 | 
			
		||||
    // Return a result if we hit something solid
 | 
			
		||||
    if world.getBlock(at: mapPosition).type != .air {
 | 
			
		||||
      return .init(
 | 
			
		||||
        position: rayPosition + direction * distance,
 | 
			
		||||
        position: rayPosition + direction / directionLen * distance,
 | 
			
		||||
        distance: distance,
 | 
			
		||||
        map: mapPosition,
 | 
			
		||||
        side: side)
 | 
			
		||||
 | 
			
		||||
@ -5,30 +5,30 @@ import simd
 | 
			
		||||
import ShaderTypes
 | 
			
		||||
 | 
			
		||||
fileprivate let cubeVertices: [ShaderVertex] = [
 | 
			
		||||
  .init(position: .init(-1, -1,  1, 1), normal: .init(.back,    0), texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1, -1,  1, 1), normal: .init(.back,    0), texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1,  1,  1, 1), normal: .init(.back,    0), texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1,  1,  1, 1), normal: .init(.back,    0), texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init( 1, -1,  1, 1), normal: .init(.right,   0), texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1, -1, -1, 1), normal: .init(.right,   0), texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init( 1,  1,  1, 1), normal: .init(.right,   0), texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1,  1, -1, 1), normal: .init(.right,   0), texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init( 1, -1, -1, 1), normal: .init(.forward, 0), texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init(-1, -1, -1, 1), normal: .init(.forward, 0), texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init( 1,  1, -1, 1), normal: .init(.forward, 0), texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init(-1,  1, -1, 1), normal: .init(.forward, 0), texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init(-1, -1, -1, 1), normal: .init(.left,    0), texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init(-1, -1,  1, 1), normal: .init(.left,    0), texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1,  1, -1, 1), normal: .init(.left,    0), texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init(-1,  1,  1, 1), normal: .init(.left,    0), texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init(-1, -1, -1, 1), normal: .init(.down,    0), texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1, -1, -1, 1), normal: .init(.down,    0), texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1, -1,  1, 1), normal: .init(.down,    0), texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1, -1,  1, 1), normal: .init(.down,    0), texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init(-1,  1,  1, 1), normal: .init(.up,      0), texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1,  1,  1, 1), normal: .init(.up,      0), texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1,  1, -1, 1), normal: .init(.up,      0), texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1,  1, -1, 1), normal: .init(.up,      0), texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init(-1, -1,  1), normal: .back,    texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1, -1,  1), normal: .back,    texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1,  1,  1), normal: .back,    texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1,  1,  1), normal: .back,    texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init( 1, -1,  1), normal: .right,   texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1, -1, -1), normal: .right,   texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init( 1,  1,  1), normal: .right,   texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1,  1, -1), normal: .right,   texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init( 1, -1, -1), normal: .forward, texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init(-1, -1, -1), normal: .forward, texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init( 1,  1, -1), normal: .forward, texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init(-1,  1, -1), normal: .forward, texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init(-1, -1, -1), normal: .left,    texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init(-1, -1,  1), normal: .left,    texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1,  1, -1), normal: .left,    texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init(-1,  1,  1), normal: .left,    texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init(-1, -1, -1), normal: .down,    texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1, -1, -1), normal: .down,    texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1, -1,  1), normal: .down,    texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1, -1,  1), normal: .down,    texCoord: .init(1, 1)),
 | 
			
		||||
  .init(position: .init(-1,  1,  1), normal: .up,      texCoord: .init(0, 0)),
 | 
			
		||||
  .init(position: .init( 1,  1,  1), normal: .up,      texCoord: .init(1, 0)),
 | 
			
		||||
  .init(position: .init(-1,  1, -1), normal: .up,      texCoord: .init(0, 1)),
 | 
			
		||||
  .init(position: .init( 1,  1, -1), normal: .up,      texCoord: .init(1, 1)),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
fileprivate let cubeIndices: [UInt16] = [
 | 
			
		||||
 | 
			
		||||
@ -18,13 +18,13 @@ vertex FragmentInput vertexMain(
 | 
			
		||||
  constant VertexShaderUniforms& u [[buffer(VertexShaderInputIdxUniforms)]]
 | 
			
		||||
) {
 | 
			
		||||
  auto position = vtx[vertexID].position;
 | 
			
		||||
  auto world = i[instanceID].model * position;
 | 
			
		||||
  auto world = i[instanceID].model * float4(position, 1);
 | 
			
		||||
 | 
			
		||||
  FragmentInput out;
 | 
			
		||||
  out.position = u.projView * world;
 | 
			
		||||
  out.world    = world.xyz;
 | 
			
		||||
  out.color    = half4(i[instanceID].color);
 | 
			
		||||
  out.normal   = (i[instanceID].normalModel * vtx[vertexID].normal).xyz;
 | 
			
		||||
  out.normal   = (i[instanceID].normalModel * float4(vtx[vertexID].normal, 0)).xyz;
 | 
			
		||||
  out.texCoord = vtx[vertexID].texCoord;
 | 
			
		||||
  return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -24,8 +24,8 @@ typedef NS_ENUM(NSInteger, VertexShaderInputIdx) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
  vector_float4 position;
 | 
			
		||||
  vector_float4 normal;
 | 
			
		||||
  vector_float3 position;
 | 
			
		||||
  vector_float3 normal;
 | 
			
		||||
  vector_float2 texCoord;
 | 
			
		||||
} ShaderVertex;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user