// OrbitalCamera.swift // Blender-style orbit camera with pan, orbit, and dolly zoom. import Observation import simd @Observable final class OrbitalCamera { var azimuth: Float = Float.pi * 0.25 var elevation: Float = Float.pi * 0.2 var target: SIMD3 = .zero var distance: Float = 7.0 private let minDistance: Float = 0.5 private let maxDistance: Float = 200.0 private let minElevation: Float = -Float.pi * 0.49 private let maxElevation: Float = Float.pi * 0.49 var eye: SIMD3 { let x = distance * cosf(elevation) * sinf(azimuth) let y = distance * sinf(elevation) let z = distance * cosf(elevation) * cosf(azimuth) return target + SIMD3(x, y, z) } var viewMatrix: simd_float4x4 { MathUtils.lookAt(eye: eye, center: target, up: SIMD3(0, 1, 0)) } func orbit(deltaX: Float, deltaY: Float) { azimuth -= deltaX * 0.008 elevation += deltaY * 0.008 elevation = min(max(elevation, minElevation), maxElevation) } func pan(deltaX: Float, deltaY: Float) { let right = SIMD3(cosf(azimuth), 0, -sinf(azimuth)) let up = SIMD3(0, 1, 0) let speed: Float = distance * 0.002 target += right * (-deltaX * speed) + up * (deltaY * speed) } func zoom(scale: Float) { distance /= scale distance = min(max(distance, minDistance), maxDistance) } func reset() { azimuth = Float.pi * 0.25 elevation = Float.pi * 0.2 distance = 7.0 target = .zero } }