112 lines
3 KiB
Swift
112 lines
3 KiB
Swift
// MathUtils.swift
|
|
// Matrix and vector utilities for the viewport camera.
|
|
|
|
import simd
|
|
|
|
enum MathUtils {
|
|
|
|
// MARK: - Projection
|
|
|
|
static func perspective(
|
|
fovYRadians fov: Float,
|
|
aspect: Float,
|
|
near: Float,
|
|
far: Float
|
|
) -> simd_float4x4 {
|
|
let y = 1.0 / tanf(fov * 0.5)
|
|
let x = y / aspect
|
|
let z = far / (near - far)
|
|
|
|
return simd_float4x4(columns: (
|
|
SIMD4<Float>(x, 0, 0, 0),
|
|
SIMD4<Float>(0, y, 0, 0),
|
|
SIMD4<Float>(0, 0, z, -1),
|
|
SIMD4<Float>(0, 0, z * near, 0)
|
|
))
|
|
}
|
|
|
|
// MARK: - View
|
|
|
|
static func lookAt(
|
|
eye: SIMD3<Float>,
|
|
center: SIMD3<Float>,
|
|
up: SIMD3<Float>
|
|
) -> simd_float4x4 {
|
|
let f = normalize(center - eye)
|
|
let s = normalize(cross(f, up))
|
|
let u = cross(s, f)
|
|
|
|
return simd_float4x4(columns: (
|
|
SIMD4<Float>(s.x, u.x, -f.x, 0),
|
|
SIMD4<Float>(s.y, u.y, -f.y, 0),
|
|
SIMD4<Float>(s.z, u.z, -f.z, 0),
|
|
SIMD4<Float>(-dot(s, eye), -dot(u, eye), dot(f, eye), 1)
|
|
))
|
|
}
|
|
|
|
// MARK: - Model transforms
|
|
|
|
static func translation(_ t: SIMD3<Float>) -> simd_float4x4 {
|
|
var m = matrix_identity_float4x4
|
|
m.columns.3 = SIMD4<Float>(t.x, t.y, t.z, 1)
|
|
return m
|
|
}
|
|
|
|
static func scale(_ s: SIMD3<Float>) -> simd_float4x4 {
|
|
return simd_float4x4(diagonal: SIMD4<Float>(s.x, s.y, s.z, 1))
|
|
}
|
|
|
|
static func rotationX(_ angle: Float) -> simd_float4x4 {
|
|
let c = cosf(angle)
|
|
let s = sinf(angle)
|
|
return simd_float4x4(columns: (
|
|
SIMD4<Float>(1, 0, 0, 0),
|
|
SIMD4<Float>(0, c, s, 0),
|
|
SIMD4<Float>(0, -s, c, 0),
|
|
SIMD4<Float>(0, 0, 0, 1)
|
|
))
|
|
}
|
|
|
|
static func rotationY(_ angle: Float) -> simd_float4x4 {
|
|
let c = cosf(angle)
|
|
let s = sinf(angle)
|
|
return simd_float4x4(columns: (
|
|
SIMD4<Float>( c, 0, s, 0),
|
|
SIMD4<Float>( 0, 1, 0, 0),
|
|
SIMD4<Float>(-s, 0, c, 0),
|
|
SIMD4<Float>( 0, 0, 0, 1)
|
|
))
|
|
}
|
|
|
|
static func rotationZ(_ angle: Float) -> simd_float4x4 {
|
|
let c = cosf(angle)
|
|
let s = sinf(angle)
|
|
return simd_float4x4(columns: (
|
|
SIMD4<Float>( c, s, 0, 0),
|
|
SIMD4<Float>(-s, c, 0, 0),
|
|
SIMD4<Float>( 0, 0, 1, 0),
|
|
SIMD4<Float>( 0, 0, 0, 1)
|
|
))
|
|
}
|
|
|
|
static func normalMatrix(from model: simd_float4x4) -> simd_float4x4 {
|
|
let upper3x3 = simd_float3x3(
|
|
model.columns.0.xyz,
|
|
model.columns.1.xyz,
|
|
model.columns.2.xyz
|
|
)
|
|
let inv = upper3x3.inverse.transpose
|
|
return simd_float4x4(columns: (
|
|
SIMD4<Float>(inv.columns.0, 0),
|
|
SIMD4<Float>(inv.columns.1, 0),
|
|
SIMD4<Float>(inv.columns.2, 0),
|
|
SIMD4<Float>(0, 0, 0, 1)
|
|
))
|
|
}
|
|
}
|
|
|
|
// MARK: - SIMD helpers
|
|
|
|
extension SIMD4 where Scalar == Float {
|
|
var xyz: SIMD3<Float> { .init(x, y, z) }
|
|
}
|