// 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(x, 0, 0, 0), SIMD4(0, y, 0, 0), SIMD4(0, 0, z, -1), SIMD4(0, 0, z * near, 0) )) } // MARK: - View static func lookAt( eye: SIMD3, center: SIMD3, up: SIMD3 ) -> simd_float4x4 { let f = normalize(center - eye) let s = normalize(cross(f, up)) let u = cross(s, f) return simd_float4x4(columns: ( SIMD4(s.x, u.x, -f.x, 0), SIMD4(s.y, u.y, -f.y, 0), SIMD4(s.z, u.z, -f.z, 0), SIMD4(-dot(s, eye), -dot(u, eye), dot(f, eye), 1) )) } // MARK: - Model transforms static func translation(_ t: SIMD3) -> simd_float4x4 { var m = matrix_identity_float4x4 m.columns.3 = SIMD4(t.x, t.y, t.z, 1) return m } static func scale(_ s: SIMD3) -> simd_float4x4 { return simd_float4x4(diagonal: SIMD4(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(1, 0, 0, 0), SIMD4(0, c, s, 0), SIMD4(0, -s, c, 0), SIMD4(0, 0, 0, 1) )) } static func rotationY(_ angle: Float) -> simd_float4x4 { let c = cosf(angle) let s = sinf(angle) return simd_float4x4(columns: ( SIMD4( c, 0, s, 0), SIMD4( 0, 1, 0, 0), SIMD4(-s, 0, c, 0), SIMD4( 0, 0, 0, 1) )) } static func rotationZ(_ angle: Float) -> simd_float4x4 { let c = cosf(angle) let s = sinf(angle) return simd_float4x4(columns: ( SIMD4( c, s, 0, 0), SIMD4(-s, c, 0, 0), SIMD4( 0, 0, 1, 0), SIMD4( 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(inv.columns.0, 0), SIMD4(inv.columns.1, 0), SIMD4(inv.columns.2, 0), SIMD4(0, 0, 0, 1) )) } } // MARK: - SIMD helpers extension SIMD4 where Scalar == Float { var xyz: SIMD3 { .init(x, y, z) } }