mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 13:11:33 +00:00
organise maths helpers
This commit is contained in:
58
Sources/Voxelotl/Math/AABB.swift
Normal file
58
Sources/Voxelotl/Math/AABB.swift
Normal file
@ -0,0 +1,58 @@
|
||||
import simd
|
||||
|
||||
struct AABB {
|
||||
private var _bounds: simd_float2x3
|
||||
|
||||
var lower: SIMD3<Float> {
|
||||
get { _bounds[0] }
|
||||
set(row) { self._bounds[0] = row }
|
||||
}
|
||||
var upper: SIMD3<Float> {
|
||||
get { _bounds[1] }
|
||||
set(row) { self._bounds[1] = row }
|
||||
}
|
||||
var center: SIMD3<Float> {
|
||||
get { (self._bounds[0] + self._bounds[1]) / 2 }
|
||||
}
|
||||
var size: SIMD3<Float> {
|
||||
get { self._bounds[1] - self._bounds[0] }
|
||||
}
|
||||
|
||||
var left: Float { self._bounds[0].x }
|
||||
var bottom: Float { self._bounds[0].y }
|
||||
var far: Float { self._bounds[0].z }
|
||||
var right: Float { self._bounds[1].x }
|
||||
var top: Float { self._bounds[1].y }
|
||||
var near: Float { self._bounds[1].z }
|
||||
|
||||
private init(bounds: simd_float2x3) {
|
||||
self._bounds = bounds
|
||||
}
|
||||
|
||||
init(from: SIMD3<Float>, to: SIMD3<Float>) {
|
||||
self.init(bounds: .init(from, to))
|
||||
}
|
||||
|
||||
static func fromUnitCube(position: SIMD3<Float> = .zero, scale: SIMD3<Float> = .one) -> Self {
|
||||
self.init(
|
||||
from: position - scale,
|
||||
to: position + scale)
|
||||
}
|
||||
|
||||
func touching(_ other: Self) -> Bool{
|
||||
let distLower = other._bounds[0] - self._bounds[1] // x: left, y: bottom, z: far
|
||||
let distUpper = self._bounds[0] - other._bounds[1] // x: right, y: top, z: near
|
||||
|
||||
if distLower.x > 0 || distUpper.x > 0 { return false }
|
||||
if distLower.y > 0 || distUpper.y > 0 { return false }
|
||||
if distLower.z > 0 || distUpper.z > 0 { return false }
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension AABB {
|
||||
static func + (lhs: Self, rhs: SIMD3<Float>) -> Self {
|
||||
.init(bounds: lhs._bounds + .init(rhs, rhs))
|
||||
}
|
||||
}
|
10
Sources/Voxelotl/Math/FloatExtensions.swift
Normal file
10
Sources/Voxelotl/Math/FloatExtensions.swift
Normal file
@ -0,0 +1,10 @@
|
||||
public extension FloatingPoint {
|
||||
@inline(__always) var degrees: Self { self * (180 / Self.pi) }
|
||||
@inline(__always) var radians: Self { self * (Self.pi / 180) }
|
||||
|
||||
@inline(__always) func lerp(_ a: Self, _ b: Self) -> Self { b * self + a * (1 - self) }
|
||||
@inline(__always) func mlerp(_ a: Self, _ b: Self) -> Self { a + (b - a) * self }
|
||||
|
||||
@inline(__always) func clamp(_ a: Self, _ b: Self) -> Self { min(max(self, a), b) }
|
||||
@inline(__always) var saturated: Self { self.clamp(0, 1) }
|
||||
}
|
95
Sources/Voxelotl/Math/Matrix4x4.swift
Normal file
95
Sources/Voxelotl/Math/Matrix4x4.swift
Normal file
@ -0,0 +1,95 @@
|
||||
import simd
|
||||
|
||||
public extension simd_float4x4 {
|
||||
typealias T = Float
|
||||
|
||||
@inline(__always) static var identity: Self { matrix_identity_float4x4 }
|
||||
|
||||
@inline(__always) static func translate(_ v: SIMD3<T>) -> Self {
|
||||
Self(
|
||||
.init( 1, 0, 0, 0),
|
||||
.init( 0, 1, 0, 0),
|
||||
.init( 0, 0, 1, 0),
|
||||
.init(v.x, v.y, v.z, 1))
|
||||
}
|
||||
|
||||
@inline(__always) static func scale(_ v: SIMD3<T>) -> Self { Self(diagonal: .init(v.x, v.y, v.z, 1)) }
|
||||
@inline(__always) static func scale(_ s: T) -> Self { Self(diagonal: .init(s, s, s, 1)) }
|
||||
|
||||
static func rotate(x theta: T) -> Self {
|
||||
let c = cos(theta), s = sin(theta)
|
||||
return Self(
|
||||
.init(1, 0, 0, 0),
|
||||
.init(0, c, s, 0),
|
||||
.init(0, -s, c, 0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
static func rotate(y theta: T) -> Self {
|
||||
let c = cos(theta), s = sin(theta)
|
||||
return Self(
|
||||
.init(c, 0, -s, 0),
|
||||
.init(0, 1, 0, 0),
|
||||
.init(s, 0, c, 0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
static func rotate(z theta: T) -> Self {
|
||||
let c = cos(theta), s = sin(theta)
|
||||
return Self(
|
||||
.init(c, -s, 0, 0),
|
||||
.init(s, c, 0, 0),
|
||||
.init(0, 0, 1, 0),
|
||||
.init(0, 0, 0, 1))
|
||||
}
|
||||
|
||||
@inline(__always) static func rotate(yawPitch yp: SIMD2<T>) -> Self { rotate(yaw: yp.x, pitch: yp.y) }
|
||||
|
||||
static func rotate(yaw ytheta: T, pitch xtheta: T) -> Self {
|
||||
let xc = cos(xtheta), xs = sin(xtheta)
|
||||
let yc = cos(ytheta), ys = sin(ytheta)
|
||||
|
||||
return .init(
|
||||
.init(yc, ys * xs, -ys * xc, 0),
|
||||
.init( 0, xc, xs, 0),
|
||||
.init(ys, yc * -xs, yc * xc, 0),
|
||||
.init( 0, 0, 0, 1))
|
||||
}
|
||||
|
||||
static func orthographic(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self {
|
||||
let
|
||||
invWidth = 1 / (right - left),
|
||||
invHeight = 1 / (top - bottom),
|
||||
invDepth = 1 / (far - near)
|
||||
let
|
||||
tx = -(right + left) * invWidth,
|
||||
ty = -(top + bottom) * invHeight,
|
||||
tz = -near * invDepth
|
||||
let x = 2 * invWidth, y = 2 * invHeight, z = invDepth
|
||||
|
||||
return .init(
|
||||
.init( x, 0, 0, 0),
|
||||
.init( 0, y, 0, 0),
|
||||
.init( 0, 0, z, 0),
|
||||
.init(tx, ty, tz, 1))
|
||||
}
|
||||
|
||||
static func perspective(verticalFov fovY: T, aspect: T, near zNear: T, far zFar: T) -> Self {
|
||||
let tanHalfFovY = tan(fovY * T(0.5))
|
||||
let invClipRange = 1 / (zNear - zFar)
|
||||
|
||||
let y = 1 / tanHalfFovY
|
||||
let x = y / aspect
|
||||
let z = zFar * invClipRange
|
||||
let w = zNear * z
|
||||
return .init(
|
||||
.init(x, 0, 0, 0),
|
||||
.init(0, y, 0, 0),
|
||||
.init(0, 0, z, -1),
|
||||
.init(0, 0, w, 0))
|
||||
}
|
||||
}
|
||||
|
||||
extension simd_quatf {
|
||||
static var identity: Self { .init(real: 1, imag: .zero) }
|
||||
}
|
103
Sources/Voxelotl/Math/Rectangle.swift
Normal file
103
Sources/Voxelotl/Math/Rectangle.swift
Normal file
@ -0,0 +1,103 @@
|
||||
struct Point<T: AdditiveArithmetic>: Equatable {
|
||||
var x: T, y: T
|
||||
|
||||
static var zero: Self { .init(.zero, .zero) }
|
||||
|
||||
init(_ x: T, _ y: T) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
}
|
||||
|
||||
@inline(__always) static func == (lhs: Self, rhs: Self) -> Bool { lhs.x == rhs.x && lhs.y == rhs.y }
|
||||
@inline(__always) static func != (lhs: Self, rhs: Self) -> Bool { lhs.x != rhs.x || lhs.y != rhs.y }
|
||||
}
|
||||
|
||||
extension Point where T: AdditiveArithmetic {
|
||||
@inline(__always) static func + (lhs: Self, rhs: Self) -> Self { Self(lhs.x + rhs.x, lhs.y + rhs.y) }
|
||||
@inline(__always) static func - (lhs: Self, rhs: Self) -> Self { Self(lhs.x - rhs.x, lhs.y - rhs.y) }
|
||||
|
||||
@inline(__always) static func += (lhs: inout Self, rhs: Self) { lhs.x += rhs.x; lhs.y += rhs.y }
|
||||
@inline(__always) static func -= (lhs: inout Self, rhs: Self) { lhs.x -= rhs.x; lhs.y -= rhs.y }
|
||||
}
|
||||
|
||||
extension SIMD2 where Scalar: AdditiveArithmetic {
|
||||
init(_ point: Point<Scalar>) {
|
||||
self.init(point.x, point.y)
|
||||
}
|
||||
}
|
||||
|
||||
public struct Size<T: AdditiveArithmetic>: Equatable {
|
||||
var w: T, h: T
|
||||
|
||||
static var zero: Self { .init(.zero, .zero) }
|
||||
|
||||
init(_ w: T, _ h: T) {
|
||||
self.w = w
|
||||
self.h = h
|
||||
}
|
||||
|
||||
@inline(__always) public static func == (lhs: Self, rhs: Self) -> Bool { lhs.w == rhs.w && lhs.h == rhs.h }
|
||||
@inline(__always) public static func != (lhs: Self, rhs: Self) -> Bool { lhs.w != rhs.w || lhs.h != rhs.h }
|
||||
}
|
||||
|
||||
extension Size where T: BinaryInteger {
|
||||
static var one: Self { .init(T(1), T(1)) }
|
||||
|
||||
init<O>(_ other: Size<O>) where O: BinaryInteger {
|
||||
self.init(T(other.w), T(other.h))
|
||||
}
|
||||
}
|
||||
|
||||
struct Rect<T: AdditiveArithmetic>: Equatable {
|
||||
var x: T, y: T, w: T, h: T
|
||||
|
||||
var origin: Point<T> {
|
||||
get { .init(self.x, self.y) }
|
||||
set(point) { self.x = point.x; self.y = point.y }
|
||||
}
|
||||
var size: Size<T> {
|
||||
get { .init(self.w, self.h) }
|
||||
set(size) { self.w = size.w; self.h = size.h }
|
||||
}
|
||||
|
||||
static var zero: Self { .init(origin: .zero, size: .zero) }
|
||||
|
||||
init(x: T, y: T, width: T, height: T) {
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.w = width
|
||||
self.h = height
|
||||
}
|
||||
|
||||
init(origin: Point<T>, size: Size<T>) {
|
||||
self.x = origin.x
|
||||
self.y = origin.y
|
||||
self.w = size.w
|
||||
self.h = size.h
|
||||
}
|
||||
|
||||
@inline(__always) 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 {
|
||||
var left: T { x }
|
||||
var right: T { x + w }
|
||||
var up: T { y }
|
||||
var down: T { y + h }
|
||||
}
|
||||
|
||||
struct Extent<T: AdditiveArithmetic>: Equatable {
|
||||
var top: T, bottom: T, left: T, right: T
|
||||
|
||||
@inline(__always) static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
lhs.left == rhs.left && lhs.right == rhs.right && lhs.top == rhs.top && lhs.bottom == rhs.bottom
|
||||
}
|
||||
}
|
||||
|
||||
extension Extent where T: Comparable {
|
||||
var size: Size<T> { .init(
|
||||
right > left ? right - left : left - right,
|
||||
bottom > top ? bottom - top : top - bottom) }
|
||||
}
|
12
Sources/Voxelotl/Math/VectorExtensions.swift
Normal file
12
Sources/Voxelotl/Math/VectorExtensions.swift
Normal file
@ -0,0 +1,12 @@
|
||||
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) }
|
||||
@inline(__always) static var Z: Self { Self(0, 0, 1) }
|
||||
|
||||
@inline(__always) static var up: Self { Y }
|
||||
@inline(__always) static var down: Self { -Y }
|
||||
@inline(__always) static var left: Self { -X }
|
||||
@inline(__always) static var right: Self { X }
|
||||
@inline(__always) static var forward: Self { -Z }
|
||||
@inline(__always) static var back: Self { Z }
|
||||
}
|
Reference in New Issue
Block a user