mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 13:11:33 +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