diff --git a/Sources/Voxelotl/CMakeLists.txt b/Sources/Voxelotl/CMakeLists.txt index 0bcf9c0..c9d2e17 100644 --- a/Sources/Voxelotl/CMakeLists.txt +++ b/Sources/Voxelotl/CMakeLists.txt @@ -6,6 +6,9 @@ add_executable(Voxelotl MACOSX_BUNDLE shadertypes.h shader.metal + FloatExtensions.swift + Matrix4x4.swift + NSImageLoader.swift Renderer.swift FPSCalculator.swift diff --git a/Sources/Voxelotl/FloatExtensions.swift b/Sources/Voxelotl/FloatExtensions.swift new file mode 100644 index 0000000..7f09d78 --- /dev/null +++ b/Sources/Voxelotl/FloatExtensions.swift @@ -0,0 +1,4 @@ +public extension FloatingPoint { + @inline(__always) var degrees: Self { self * (180 / Self.pi) } + @inline(__always) var radians: Self { self * (Self.pi / 180) } +} diff --git a/Sources/Voxelotl/Matrix4x4.swift b/Sources/Voxelotl/Matrix4x4.swift new file mode 100644 index 0000000..cdd57ee --- /dev/null +++ b/Sources/Voxelotl/Matrix4x4.swift @@ -0,0 +1,60 @@ +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) -> 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) -> 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)) + } + + static func perspective(verticalFov: T, aspect: T, near: T, far: T) -> Self { + let h = 1 / tan(verticalFov * T(0.5)) + let w = h / aspect + + let invClipRange = 1 / (far - near) + let z = -(far + near) * invClipRange + let z2 = -(2 * far * near) * invClipRange + + return .init( + .init(w, 0, 0, 0), + .init(0, h, 0, 0), + .init(0, 0, z, -1), + .init(0, 0, z2, 0)) + } +} diff --git a/Sources/Voxelotl/Renderer.swift b/Sources/Voxelotl/Renderer.swift index 7ff7d63..ba54bcb 100644 --- a/Sources/Voxelotl/Renderer.swift +++ b/Sources/Voxelotl/Renderer.swift @@ -217,10 +217,23 @@ class Renderer { zfar: -1.0) } + var time: Float = 0 //FIXME: temp + func paint() throws { - var uniforms = ShaderUniforms( - model: .init(diagonal: .init(0.5, 0.5, 0.5, 1.0)), - projView: matrix_identity_float4x4) + let projection = matrix_float4x4.perspective( + verticalFov: Float(90.0).radians, + aspect: Float(self.viewport.width / self.viewport.height), + near: 0.1, + far: 10) + let view = matrix_float4x4.identity + let model: matrix_float4x4 = + .translate(.init(0, sin(time * 0.5) * 0.5, -2)) * + .scale(0.5) * + .rotate(y: time) + + time += 0.025 + + var uniforms = ShaderUniforms(model: model, projView: projection * view) guard let rt = layer.nextDrawable() else { throw RendererError.drawFailure("Failed to get next drawable render target")