mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 10:59:39 +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
 | 
					    // 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 {
 | 
					    guard SDL_GetWindowSizeInPixels(window, &backBuffer.w, &backBuffer.h) >= 0 else {
 | 
				
			||||||
      printErr("SDL_GetWindowSizeInPixels() error: \(String(cString: SDL_GetError()))")
 | 
					      printErr("SDL_GetWindowSizeInPixels() error: \(String(cString: SDL_GetError()))")
 | 
				
			||||||
      return .exitFailure
 | 
					      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
 | 
					    // Create Metal renderer
 | 
				
			||||||
    view = SDL_Metal_CreateView(window)
 | 
					    view = SDL_Metal_CreateView(window)
 | 
				
			||||||
 | 
				
			|||||||
@ -9,16 +9,18 @@ public final class Camera {
 | 
				
			|||||||
    static let viewProj   = Self(rawValue: 1 << 2)
 | 
					    static let viewProj   = Self(rawValue: 1 << 2)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private var _position: SIMD3<Float>
 | 
					  private var _position = SIMD3<Float>.zero
 | 
				
			||||||
  private var _rotation: simd_quatf
 | 
					  private var _rotation = simd_quatf.identity
 | 
				
			||||||
  private var _fieldOfView: Float
 | 
					  private var _fieldOfView: Float
 | 
				
			||||||
  private var _aspectRatio: Float
 | 
					  private var _aspectRatio: Float
 | 
				
			||||||
  private var _zNearFar: ClosedRange<Float>
 | 
					  private var _zNearFar: ClosedRange<Float>
 | 
				
			||||||
 | 
					  private var _viewport: Rect<Float>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private var _dirty: Dirty
 | 
					  private var _dirty: Dirty
 | 
				
			||||||
  private var _projection: matrix_float4x4
 | 
					  private var _projection = matrix_identity_float4x4
 | 
				
			||||||
  private var _view: matrix_float4x4
 | 
					  private var _view = matrix_identity_float4x4
 | 
				
			||||||
  private var _viewProjection: matrix_float4x4
 | 
					  private var _viewProjection = matrix_identity_float4x4
 | 
				
			||||||
 | 
					  private var _invViewProjection = matrix_identity_float4x4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public var position: SIMD3<Float> {
 | 
					  public var position: SIMD3<Float> {
 | 
				
			||||||
    get { self._position }
 | 
					    get { self._position }
 | 
				
			||||||
@ -59,8 +61,19 @@ public final class Camera {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public func setSize(_ size: Size<Int>) {
 | 
					  public var viewport: Rect<Float> {
 | 
				
			||||||
    self.aspectRatio = Float(size.w) / Float(size.h)
 | 
					    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> {
 | 
					  public var range: ClosedRange<Float> {
 | 
				
			||||||
@ -94,20 +107,64 @@ public final class Camera {
 | 
				
			|||||||
  public var viewProjection: matrix_float4x4 {
 | 
					  public var viewProjection: matrix_float4x4 {
 | 
				
			||||||
    if !self._dirty.isEmpty {
 | 
					    if !self._dirty.isEmpty {
 | 
				
			||||||
      self._viewProjection = self.projection * self.view
 | 
					      self._viewProjection = self.projection * self.view
 | 
				
			||||||
 | 
					      self._invViewProjection = self._viewProjection.inverse
 | 
				
			||||||
      self._dirty.remove(.viewProj)
 | 
					      self._dirty.remove(.viewProj)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return self._viewProjection
 | 
					    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>) {
 | 
					  init(fov: Float, size: Size<Int>, range: ClosedRange<Float>) {
 | 
				
			||||||
    self._position = .zero
 | 
					 | 
				
			||||||
    self._rotation = .identity
 | 
					 | 
				
			||||||
    self._fieldOfView = fov.radians
 | 
					    self._fieldOfView = fov.radians
 | 
				
			||||||
    self._aspectRatio = Float(size.w) / Float(size.h)
 | 
					    self._aspectRatio = Float(size.w) / Float(size.h)
 | 
				
			||||||
    self._zNearFar = range
 | 
					    self._zNearFar = range
 | 
				
			||||||
 | 
					    self._viewport = .init(origin: .zero, size: Size<Float>(size))
 | 
				
			||||||
    self._dirty = [ .projection, .view, .viewProj ]
 | 
					    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() {
 | 
					  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.velocity = .zero
 | 
				
			||||||
    self.player.rotation = .init(.pi, 0)
 | 
					    self.player.rotation = .init(.pi, 0)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -47,13 +47,13 @@ class Game: GameDelegate {
 | 
				
			|||||||
    random = Xoroshiro128PlusPlus(seed: newSeed)
 | 
					    random = Xoroshiro128PlusPlus(seed: newSeed)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    random = PCG32Random(state: (
 | 
					    random = PCG32Random(state: (
 | 
				
			||||||
        UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32,
 | 
					      UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32,
 | 
				
			||||||
        UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32))
 | 
					      UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32))
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
#if DEBUG
 | 
					#if DEBUG
 | 
				
			||||||
      self.world.generate(width: 2, height: 1, depth: 1, random: &random)
 | 
					    self.world.generate(width: 2, height: 1, depth: 1, random: &random)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
      self.world.generate(width: 5, height: 3, depth: 5, random: &random)
 | 
					    self.world.generate(width: 5, height: 3, depth: 5, random: &random)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -85,9 +85,7 @@ class Game: GameDelegate {
 | 
				
			|||||||
      self.generateWorld()
 | 
					      self.generateWorld()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self.player.update(deltaTime: deltaTime, world: world)
 | 
					    self.player.update(deltaTime: deltaTime, world: world, camera: &camera)
 | 
				
			||||||
    self.camera.position = player.eyePosition
 | 
					 | 
				
			||||||
    self.camera.rotation = player.eyeRotation
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  func draw(_ renderer: Renderer, _ time: GameTime) {
 | 
					  func draw(_ renderer: Renderer, _ time: GameTime) {
 | 
				
			||||||
@ -103,21 +101,23 @@ class Game: GameDelegate {
 | 
				
			|||||||
      gloss: 75)
 | 
					      gloss: 75)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var instances = world.instances
 | 
					    var instances = world.instances
 | 
				
			||||||
    instances.append(
 | 
					    if let position = player.rayhitPos {
 | 
				
			||||||
      Instance(
 | 
					      instances.append(
 | 
				
			||||||
        position: player.rayhitPos,
 | 
					        Instance(
 | 
				
			||||||
        scale:    .init(repeating: 0.0725 * 0.5),
 | 
					          position: position,
 | 
				
			||||||
        rotation:
 | 
					          scale:    .init(repeating: 0.0725 * 0.5),
 | 
				
			||||||
          .init(angle: totalTime * 3.0, axis: .init(0, 1, 0)) *
 | 
					          rotation:
 | 
				
			||||||
          .init(angle: totalTime * 1.5, axis: .init(1, 0, 0)) *
 | 
					            .init(angle: totalTime * 3.0, axis: .init(0, 1, 0)) *
 | 
				
			||||||
          .init(angle: totalTime * 0.7, axis: .init(0, 0, 1)),
 | 
					            .init(angle: totalTime * 1.5, axis: .init(1, 0, 0)) *
 | 
				
			||||||
        color:    .init(r: 0.5, g: 0.5, b: 1).linear))
 | 
					            .init(angle: totalTime * 0.7, axis: .init(0, 0, 1)),
 | 
				
			||||||
 | 
					          color:    .init(r: 0.5, g: 0.5, b: 1).linear))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    if !instances.isEmpty {
 | 
					    if !instances.isEmpty {
 | 
				
			||||||
      renderer.batch(instances: instances, material: material, environment: env, camera: self.camera)
 | 
					      renderer.batch(instances: instances, material: material, environment: env, camera: self.camera)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  func resize(_ size: Size<Int>) {
 | 
					  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 _window: OpaquePointer!
 | 
				
			||||||
  private var _captured: Bool = false
 | 
					  private var _captured: Bool = false
 | 
				
			||||||
 | 
					  private var _dpiScale: SIMD2<Float> = .one
 | 
				
			||||||
  private var _abs: SIMD2<Float> = .zero, _delta: SIMD2<Float> = .zero
 | 
					  private var _abs: SIMD2<Float> = .zero, _delta: SIMD2<Float> = .zero
 | 
				
			||||||
  private var _btns: Buttons = [], _btnImpulse: Buttons = []
 | 
					  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 }
 | 
					  private func getDelta() -> SIMD2<Float> { self._delta }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  internal func buttonEvent(btn: UInt32, state: UInt8) {
 | 
					  internal func buttonEvent(btn: UInt32, state: UInt8) {
 | 
				
			||||||
 | 
				
			|||||||
@ -88,6 +88,19 @@ public extension simd_float4x4 {
 | 
				
			|||||||
      .init(0, 0, z, -1),
 | 
					      .init(0, 0, z, -1),
 | 
				
			||||||
      .init(0, 0, w,  0))
 | 
					      .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 {
 | 
					extension simd_quatf {
 | 
				
			||||||
 | 
				
			|||||||
@ -43,12 +43,32 @@ public struct Size<T: AdditiveArithmetic>: Equatable {
 | 
				
			|||||||
extension Size where T: BinaryInteger {
 | 
					extension Size where T: BinaryInteger {
 | 
				
			||||||
  static var one: Self { .init(T(1), T(1)) }
 | 
					  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))
 | 
					    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 x: T, y: T, w: T, h: T
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var origin: Point<T> {
 | 
					  var origin: Point<T> {
 | 
				
			||||||
@ -76,12 +96,12 @@ struct Rect<T: AdditiveArithmetic>: Equatable {
 | 
				
			|||||||
    self.h = size.h
 | 
					    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
 | 
					    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 left: T { x }
 | 
				
			||||||
  var right: T { x + w }
 | 
					  var right: T { x + w }
 | 
				
			||||||
  var up: T { y }
 | 
					  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 {
 | 
					extension SIMD3 where Scalar: FloatingPoint {
 | 
				
			||||||
  @inline(__always) static var X: Self      { Self(1, 0, 0) }
 | 
					  @inline(__always) static var X: Self      { Self(1, 0, 0) }
 | 
				
			||||||
  @inline(__always) static var Y: Self      { Self(0, 1, 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 forward: Self { -Z }
 | 
				
			||||||
  @inline(__always) static var back: 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 flySpeedCoeff: Float = 36
 | 
				
			||||||
  static let jumpVelocity: Float = 7
 | 
					  static let jumpVelocity: Float = 7
 | 
				
			||||||
  static let maxVelocity: Float = 160
 | 
					  static let maxVelocity: Float = 160
 | 
				
			||||||
 | 
					  static let blockReach: Float = 3.8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private var _position = SIMD3<Float>.zero
 | 
					  private var _position = SIMD3<Float>.zero
 | 
				
			||||||
  private var _velocity = SIMD3<Float>.zero
 | 
					  private var _velocity = SIMD3<Float>.zero
 | 
				
			||||||
@ -24,8 +25,9 @@ struct Player {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private var _onGround: Bool = false
 | 
					  private var _onGround: Bool = false
 | 
				
			||||||
  private var _shouldJump: Optional<Float> = .none
 | 
					  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
 | 
					  private var prevLeftTrigger: Float = 0, prevRightTrigger: Float = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public var position: SIMD3<Float> { get { self._position } set { self._position = newValue } }
 | 
					  public var position: SIMD3<Float> { get { self._position } set { self._position = newValue } }
 | 
				
			||||||
@ -40,7 +42,7 @@ struct Player {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  enum JumpInput { case off, press, held }
 | 
					  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 turning: SIMD2<Float> = .zero
 | 
				
			||||||
    var movement: SIMD2<Float> = .zero
 | 
					    var movement: SIMD2<Float> = .zero
 | 
				
			||||||
    var flying: Int = .zero
 | 
					    var flying: Int = .zero
 | 
				
			||||||
@ -49,7 +51,11 @@ 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)
 | 
					      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)
 | 
					      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) {
 | 
				
			||||||
@ -86,6 +92,9 @@ struct Player {
 | 
				
			|||||||
    if Mouse.pressed(.right) { place   = true }
 | 
					    if Mouse.pressed(.right) { place   = true }
 | 
				
			||||||
    if Mouse.capture {
 | 
					    if Mouse.capture {
 | 
				
			||||||
      self._rotation += Mouse.relative / 2048 * Float.pi
 | 
					      self._rotation += Mouse.relative / 2048 * Float.pi
 | 
				
			||||||
 | 
					      self._useMouseDir = false
 | 
				
			||||||
 | 
					    } else if simd_length_squared(Mouse.relative) > Float.ulpOfOne {
 | 
				
			||||||
 | 
					      self._useMouseDir = true
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Turning input
 | 
					    // Turning input
 | 
				
			||||||
@ -121,10 +130,10 @@ struct Player {
 | 
				
			|||||||
    if movementMagnitude > 1.0 {
 | 
					    if movementMagnitude > 1.0 {
 | 
				
			||||||
      movement /= movementMagnitude
 | 
					      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
 | 
					    let coeff = self._onGround ? Self.accelerationCoeff : Self.airAccelCoeff
 | 
				
			||||||
    self._velocity.x += (movement.x * rotc - movement.y * rots) * coeff * deltaTime
 | 
					    self._velocity.xz += (right * movement.x + forward * movement.y) * coeff * deltaTime
 | 
				
			||||||
    self._velocity.z += (movement.y * rotc + movement.x * rots) * coeff * deltaTime
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Flying and unflying
 | 
					    // Flying and unflying
 | 
				
			||||||
    self._velocity.y += Float(flying).clamp(-1, 1) * Self.flySpeedCoeff * deltaTime
 | 
					    self._velocity.y += Float(flying).clamp(-1, 1) * Self.flySpeedCoeff * deltaTime
 | 
				
			||||||
@ -198,25 +207,36 @@ struct Player {
 | 
				
			|||||||
      self._velocity.z = 0
 | 
					      self._velocity.z = 0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update camera
 | 
				
			||||||
 | 
					    camera.position = self.eyePosition
 | 
				
			||||||
 | 
					    camera.rotation = self.eyeRotation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Block picking
 | 
					    // Block picking
 | 
				
			||||||
    if let hit = raycast(
 | 
					    let dir = !Mouse.capture && self._useMouseDir
 | 
				
			||||||
      world: world,
 | 
					      ? camera.screenRay(Mouse.position)
 | 
				
			||||||
      origin: self.eyePosition,
 | 
					      : self.eyeRotation * .forward
 | 
				
			||||||
      direction: .forward * simd_matrix3x3(self.eyeRotation),
 | 
					    if let hit = raycast(world: world, origin: self.eyePosition, direction: dir, maxDistance: Self.blockReach) {
 | 
				
			||||||
      maxDistance: 3.8
 | 
					      if destroy || place {
 | 
				
			||||||
    ) {
 | 
					        if destroy {
 | 
				
			||||||
      self.rayhitPos = hit.position
 | 
					          world.setBlock(at: hit.map, type: .air)
 | 
				
			||||||
      if destroy {
 | 
					        } else {
 | 
				
			||||||
        world.setBlock(at: hit.map, type: .air)
 | 
					          world.setBlock(at: hit.map.offset(by: hit.side), type: .solid(.white))
 | 
				
			||||||
      } else if place {
 | 
					        }
 | 
				
			||||||
        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
 | 
					    // Ground friction
 | 
				
			||||||
    if self._onGround {
 | 
					    if self._onGround {
 | 
				
			||||||
      self._velocity.x /= 1.0 + Self.frictionCoeff * deltaTime
 | 
					      self._velocity.xz /= 1.0 + Self.frictionCoeff * deltaTime
 | 
				
			||||||
      self._velocity.z /= 1.0 + Self.frictionCoeff * deltaTime
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Limit maximum velocity
 | 
					    // Limit maximum velocity
 | 
				
			||||||
 | 
				
			|||||||
@ -6,31 +6,22 @@ public func raycast(
 | 
				
			|||||||
  direction: SIMD3<Float>,
 | 
					  direction: SIMD3<Float>,
 | 
				
			||||||
  maxDistance: Float
 | 
					  maxDistance: Float
 | 
				
			||||||
) -> Optional<RaycastHit> {
 | 
					) -> 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 mapPosition = SIMD3<Int>(floor(rayPosition))
 | 
				
			||||||
  var sideDistance: SIMD3<Float> = .zero
 | 
					  var sideDistance: SIMD3<Float> = .zero
 | 
				
			||||||
  var step: SIMD3<Int> = .zero
 | 
					  var step: SIMD3<Int> = .zero
 | 
				
			||||||
  if direction.x < 0 {
 | 
					  for i in 0..<3 {
 | 
				
			||||||
    step.x = -1
 | 
					    if direction[i] < 0 {
 | 
				
			||||||
    sideDistance.x = (rayPosition.x - Float(mapPosition.x)) * deltaDistance.x
 | 
					      step[i] = -1
 | 
				
			||||||
  } else {
 | 
					      sideDistance[i] = (rayPosition[i] - Float(mapPosition[i])) * deltaDistance[i]
 | 
				
			||||||
    step.x = 1
 | 
					    } else {
 | 
				
			||||||
    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)
 | 
					  // Run digital differential analysis (3DDDA)
 | 
				
			||||||
@ -59,24 +50,23 @@ public func raycast(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Compute distance
 | 
					    // Compute distance
 | 
				
			||||||
    var distance: Float = if side.isX {
 | 
					    let distance: Float = if side.isX {
 | 
				
			||||||
      abs(Float(mapPosition.x) - rayPosition.x + Float(1 - step.x) / 2) / direction.x
 | 
					      abs(Float(mapPosition.x) - rayPosition.x + Float(1 - step.x) * 0.5) * deltaDistance.x
 | 
				
			||||||
    } else if side.isVertical {
 | 
					    } 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 {
 | 
					    } 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
 | 
					    // Bail out if we've exeeded the max raycast distance
 | 
				
			||||||
    if distance > maxDistance {
 | 
					    if distance > maxDistance {
 | 
				
			||||||
      return nil
 | 
					      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 {
 | 
					    if world.getBlock(at: mapPosition).type != .air {
 | 
				
			||||||
      return .init(
 | 
					      return .init(
 | 
				
			||||||
        position: rayPosition + direction * distance,
 | 
					        position: rayPosition + direction / directionLen * distance,
 | 
				
			||||||
        distance: distance,
 | 
					        distance: distance,
 | 
				
			||||||
        map: mapPosition,
 | 
					        map: mapPosition,
 | 
				
			||||||
        side: side)
 | 
					        side: side)
 | 
				
			||||||
 | 
				
			|||||||
@ -5,30 +5,30 @@ import simd
 | 
				
			|||||||
import ShaderTypes
 | 
					import ShaderTypes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fileprivate let cubeVertices: [ShaderVertex] = [
 | 
					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), normal: .back,    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), normal: .back,    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), normal: .back,    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), normal: .back,    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), normal: .right,   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), normal: .right,   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), normal: .right,   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), normal: .right,   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), normal: .forward, 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), normal: .forward, 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), normal: .forward, 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), normal: .forward, 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), normal: .left,    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), normal: .left,    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), normal: .left,    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), normal: .left,    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), normal: .down,    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), normal: .down,    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), normal: .down,    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), normal: .down,    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), normal: .up,      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), normal: .up,      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), normal: .up,      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: .up,      texCoord: .init(1, 1)),
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fileprivate let cubeIndices: [UInt16] = [
 | 
					fileprivate let cubeIndices: [UInt16] = [
 | 
				
			||||||
 | 
				
			|||||||
@ -18,13 +18,13 @@ vertex FragmentInput vertexMain(
 | 
				
			|||||||
  constant VertexShaderUniforms& u [[buffer(VertexShaderInputIdxUniforms)]]
 | 
					  constant VertexShaderUniforms& u [[buffer(VertexShaderInputIdxUniforms)]]
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
  auto position = vtx[vertexID].position;
 | 
					  auto position = vtx[vertexID].position;
 | 
				
			||||||
  auto world = i[instanceID].model * position;
 | 
					  auto world = i[instanceID].model * float4(position, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  FragmentInput out;
 | 
					  FragmentInput out;
 | 
				
			||||||
  out.position = u.projView * world;
 | 
					  out.position = u.projView * world;
 | 
				
			||||||
  out.world    = world.xyz;
 | 
					  out.world    = world.xyz;
 | 
				
			||||||
  out.color    = half4(i[instanceID].color);
 | 
					  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;
 | 
					  out.texCoord = vtx[vertexID].texCoord;
 | 
				
			||||||
  return out;
 | 
					  return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,8 +24,8 @@ typedef NS_ENUM(NSInteger, VertexShaderInputIdx) {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct {
 | 
					typedef struct {
 | 
				
			||||||
  vector_float4 position;
 | 
					  vector_float3 position;
 | 
				
			||||||
  vector_float4 normal;
 | 
					  vector_float3 normal;
 | 
				
			||||||
  vector_float2 texCoord;
 | 
					  vector_float2 texCoord;
 | 
				
			||||||
} ShaderVertex;
 | 
					} ShaderVertex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user