From dc88042a367a88b2d04595e6aa9be283734a8ea0 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Tue, 13 Aug 2024 08:38:21 +1000 Subject: [PATCH] break up gameplay stuff --- Sources/Voxelotl/Application.swift | 43 ++++---- Sources/Voxelotl/CMakeLists.txt | 6 ++ Sources/Voxelotl/Camera.swift | 128 ++++++++++++++++++----- Sources/Voxelotl/FPSCalculator.swift | 18 ++-- Sources/Voxelotl/Game.swift | 53 ++++++++++ Sources/Voxelotl/GameDelegate.swift | 23 +++++ Sources/Voxelotl/Matrix4x4.swift | 20 ++-- Sources/Voxelotl/Player.swift | 36 +++++++ Sources/Voxelotl/Rectangle.swift | 99 ++++++++++++++++++ Sources/Voxelotl/Renderer.swift | 146 ++++++++++++++------------- Sources/Voxelotl/main.swift | 4 + Sources/Voxelotl/shader.metal | 2 +- Sources/Voxelotl/shadertypes.h | 2 +- 13 files changed, 448 insertions(+), 132 deletions(-) create mode 100644 Sources/Voxelotl/Game.swift create mode 100644 Sources/Voxelotl/GameDelegate.swift create mode 100644 Sources/Voxelotl/Player.swift create mode 100644 Sources/Voxelotl/Rectangle.swift diff --git a/Sources/Voxelotl/Application.swift b/Sources/Voxelotl/Application.swift index 05aee21..6d51cca 100644 --- a/Sources/Voxelotl/Application.swift +++ b/Sources/Voxelotl/Application.swift @@ -4,16 +4,17 @@ import QuartzCore.CAMetalLayer public class Application { private let cfg: ApplicationConfiguration + private let del: GameDelegate private var window: OpaquePointer? = nil private var view: SDL_MetalView? = nil private var renderer: Renderer? = nil private var lastCounter: UInt64 = 0 - private var fpsCalculator = FPSCalculator() + private var time: Duration = .zero - - public init(configuration: ApplicationConfiguration) { + public init(delegate: GameDelegate, configuration: ApplicationConfiguration) { self.cfg = configuration + self.del = delegate } private func initialize() -> ApplicationExecutionState { @@ -48,7 +49,7 @@ public class Application { do { let layer = unsafeBitCast(SDL_Metal_GetLayer(view), to: CAMetalLayer.self) layer.displaySyncEnabled = cfg.vsyncMode == .off ? false : true - self.renderer = try Renderer(layer: layer, size: SIMD2(Int(backBufferWidth), Int(backBufferHeight))) + self.renderer = try Renderer(layer: layer, size: .init(Int(backBufferWidth), Int(backBufferHeight))) } catch RendererError.initFailure(let message) { printErr("Renderer init error: \(message)") return .exitFailure @@ -103,8 +104,9 @@ public class Application { return .running case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: - let backBufferSize = SIMD2(Int(event.window.data1), Int(event.window.data2)) - renderer!.resize(size: backBufferSize) + let backBufferSize = Size(Int(event.window.data1), Int(event.window.data2)) + self.renderer!.resize(size: backBufferSize) + self.del.resize(backBufferSize) return .running default: @@ -112,13 +114,17 @@ public class Application { } } - private func update(_ deltaTime: Double) -> ApplicationExecutionState { - fpsCalculator.frame(deltaTime: deltaTime) { fps in - print("FPS: \(fps)") - } + private func update() -> ApplicationExecutionState { + let deltaTime = getDeltaTime() + time += deltaTime + let gameTime = GameTime(total: time, delta: deltaTime) + + del.update(gameTime) do { - try renderer!.paint() + try renderer!.beginFrame() + del.draw(renderer!, gameTime) + renderer!.endFrame() } catch RendererError.drawFailure(let message) { printErr("Renderer draw error: \(message)") return .exitFailure @@ -130,11 +136,14 @@ public class Application { return .running } - private func getDeltaTime() -> Double { + private func getDeltaTime() -> Duration { let counter = SDL_GetPerformanceCounter() - let divisor = 1.0 / Double(SDL_GetPerformanceFrequency()) - defer { lastCounter = counter } - return Double(counter &- lastCounter) * divisor + defer { + lastCounter = counter + } + let difference = Double(counter &- lastCounter) + let divisor = Double(SDL_GetPerformanceFrequency()) + return Duration.seconds(difference / divisor) } func run() -> Int32 { @@ -149,9 +158,7 @@ public class Application { break quit } } - - let deltaTime = getDeltaTime() - res = update(deltaTime) + res = update() } return res == .exitSuccess ? 0 : 1 diff --git a/Sources/Voxelotl/CMakeLists.txt b/Sources/Voxelotl/CMakeLists.txt index 309ba6d..a412f1a 100644 --- a/Sources/Voxelotl/CMakeLists.txt +++ b/Sources/Voxelotl/CMakeLists.txt @@ -8,13 +8,19 @@ add_executable(Voxelotl MACOSX_BUNDLE FloatExtensions.swift Matrix4x4.swift + Rectangle.swift NSImageLoader.swift Renderer.swift GameController.swift Camera.swift + Player.swift FPSCalculator.swift + GameDelegate.swift Application.swift + + Game.swift + main.swift) set_source_files_properties( diff --git a/Sources/Voxelotl/Camera.swift b/Sources/Voxelotl/Camera.swift index 08467e1..d3c3c86 100644 --- a/Sources/Voxelotl/Camera.swift +++ b/Sources/Voxelotl/Camera.swift @@ -1,37 +1,113 @@ import simd -struct Camera { - private var position = SIMD3.zero - private var rotation = SIMD2.zero +public final class Camera { + private struct Dirty: OptionSet { + let rawValue: UInt8 - var view: matrix_float4x4 { - .rotate(yawPitch: rotation) * .translate(-position) + static let projection = Self(rawValue: 1 << 0) + static let view = Self(rawValue: 1 << 1) + static let viewProj = Self(rawValue: 1 << 2) } - mutating func update(deltaTime: Float) { - if let pad = GameController.current?.state { - let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1) - rotation += turning * deltaTime - if rotation.x < 0.0 { - rotation.x += .pi * 2 - } else if rotation.x > .pi * 2 { - rotation.x -= .pi * 2 - } - rotation.y = rotation.y.clamp(-.pi * 0.5, .pi * 0.5) + private var _position: SIMD3 + private var _rotation: simd_quatf + private var _fieldOfView: Float + private var _aspectRatio: Float + private var _zNearFar: ClosedRange - let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1) + private var _dirty: Dirty + private var _projection: matrix_float4x4 + private var _view: matrix_float4x4 + private var _viewProjection: matrix_float4x4 - let rotc = cos(rotation.x), rots = sin(rotation.x) - position += .init( - movement.x * rotc - movement.y * rots, - 0, - movement.y * rotc + movement.x * rots - ) * deltaTime - - if pad.pressed(.back) { - position = .zero - rotation = .zero + public var position: SIMD3 { + get { self._position } + set(position) { + if self._position != position { + self._position = position + self._dirty.insert(.view) } } } + + public var rotation: simd_quatf { + get { self._rotation } + set(rotation) { + if self._rotation != rotation { + self._rotation = rotation + self._dirty.insert(.view) + } + } + } + + public var fieldOfView: Float { + get { self._fieldOfView.degrees } + set(fov) { + let fovRad = fov.radians + self._fieldOfView = fovRad + self._dirty.insert(.projection) + } + } + + public var aspectRatio: Float { + get { self._aspectRatio } + set(aspect) { + if self._aspectRatio != aspect { + self._aspectRatio = aspect + self._dirty.insert(.projection) + } + } + } + + public func setSize(_ size: Size) { + self.aspectRatio = Float(size.w) / Float(size.h) + } + + public var range: ClosedRange { + get { self._zNearFar } + set(range) { + self._zNearFar = range + self._dirty.insert(.projection) + } + } + + public var projection: matrix_float4x4 { + if self._dirty.contains(.projection) { + self._projection = .perspective( + verticalFov: self._fieldOfView, + aspect: self._aspectRatio, + near: self._zNearFar.lowerBound, + far: self._zNearFar.upperBound) + self._dirty.remove(.projection) + self._dirty.insert(.viewProj) + } + return self._projection + } + public var view: matrix_float4x4 { + if self._dirty.contains(.view) { + self._view = matrix_float4x4(rotation) * .translate(-position) + self._dirty.remove(.view) + self._dirty.insert(.viewProj) + } + return self._view + } + public var viewProjection: matrix_float4x4 { + if !self._dirty.isEmpty { + self._viewProjection = self.projection * self.view + self._dirty.remove(.viewProj) + } + return self._viewProjection + } + + init(fov: Float, size: Size, range: ClosedRange) { + self._position = .zero + self._rotation = .identity + self._fieldOfView = fov.radians + self._aspectRatio = Float(size.w) / Float(size.h) + self._zNearFar = range + self._dirty = [ .projection, .view, .viewProj ] + self._projection = .init() + self._view = .init() + self._viewProjection = .init() + } } diff --git a/Sources/Voxelotl/FPSCalculator.swift b/Sources/Voxelotl/FPSCalculator.swift index 5b188a8..b84b54b 100644 --- a/Sources/Voxelotl/FPSCalculator.swift +++ b/Sources/Voxelotl/FPSCalculator.swift @@ -1,18 +1,20 @@ import Foundation public struct FPSCalculator { - private var _accumulator = 0.0 + private var _accumulator = Duration.zero private var _framesCount = 0 - public mutating func frame(deltaTime: Double, result: (_ fps: Int) -> Void) { - _framesCount += 1 - _accumulator += deltaTime + public mutating func frame(deltaTime: Duration, result: (_ fps: Int) -> Void) { + self._framesCount += 1 + self._accumulator += deltaTime - if _accumulator >= 1.0 { - result(_framesCount) + if self._accumulator >= Duration.seconds(1) { + result(self._framesCount) - _framesCount = 0 - _accumulator = fmod(_accumulator, 1.0) + self._framesCount = 0 + self._accumulator = .init( + secondsComponent: 0, + attosecondsComponent: self._accumulator.components.attoseconds) } } } diff --git a/Sources/Voxelotl/Game.swift b/Sources/Voxelotl/Game.swift new file mode 100644 index 0000000..b144983 --- /dev/null +++ b/Sources/Voxelotl/Game.swift @@ -0,0 +1,53 @@ +import simd + +struct Instance { + var position: SIMD3 = .zero + var scale: SIMD3 = .one + var rotation: simd_quatf = .identity + var color: SIMD4 = .one +} + +class Game: GameDelegate { + + private var fpsCalculator = FPSCalculator() + var camera = Camera(fov: 60, size: .one, range: 0.03...25) + var player = Player() + var projection: matrix_float4x4 = .identity + + func fixedUpdate(_ time: GameTime) { + + } + + func update(_ time: GameTime) { + let deltaTime = Float(time.delta.asFloat) + fpsCalculator.frame(deltaTime: time.delta) { fps in + print("FPS: \(fps)") + } + + player.update(deltaTime: deltaTime) + camera.position = player.position + camera.rotation = + simd_quatf(angle: player.rotation.y, axis: .init(1, 0, 0)) * + simd_quatf(angle: player.rotation.x, axis: .init(0, 1, 0)) + } + + func draw(_ renderer: Renderer, _ time: GameTime) { + let totalTime = Float(time.total.asFloat) + + let instances: [Instance] = [ + Instance( + position: .init(0, sin(totalTime * 1.5) * 0.5, -2), + scale: .init(repeating: 0.25), + rotation: .init(angle: totalTime * 3.0, axis: .init(0, 1, 0)), + color: .init(0.5, 0.5, 1, 1)), + Instance(position: .init(0, -1, 0), scale: .init(10, 0.1, 10)), + Instance(position: .init(-2.5, 0, -3), color: .init(1, 0.5, 0.75, 1)), + Instance(position: .init(-2.5, -0.5, -5), color: .init(0.75, 1, 1, 1)) + ] + renderer.batch(instances: instances, camera: self.camera) + } + + func resize(_ size: Size) { + self.camera.setSize(size) + } +} diff --git a/Sources/Voxelotl/GameDelegate.swift b/Sources/Voxelotl/GameDelegate.swift new file mode 100644 index 0000000..eece92a --- /dev/null +++ b/Sources/Voxelotl/GameDelegate.swift @@ -0,0 +1,23 @@ +public protocol GameDelegate { + func fixedUpdate(_ time: GameTime) + func update(_ time: GameTime) + func draw(_ renderer: Renderer, _ time: GameTime) + func resize(_ size: Size) +} + +public extension GameDelegate { + func fixedUpdate(_ time: GameTime) {} + func update(_ time: GameTime) {} + func resize(_ size: Size) {} +} + +public struct GameTime { + let total: Duration + let delta: Duration +} + +extension Duration { + var asFloat: Double { + Double(components.seconds) + Double(components.attoseconds) * 1e-18 + } +} diff --git a/Sources/Voxelotl/Matrix4x4.swift b/Sources/Voxelotl/Matrix4x4.swift index 975dc1c..d03612e 100644 --- a/Sources/Voxelotl/Matrix4x4.swift +++ b/Sources/Voxelotl/Matrix4x4.swift @@ -68,20 +68,20 @@ public extension simd_float4x4 { 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)) + .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: T, far: T) -> Self { + 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 / (near - far) + let invClipRange = 1 / (zNear - zFar) let y = 1 / tanHalfFovY let x = y / aspect - let z = far * invClipRange - let w = near * z // (far * near) * invClipRange + let z = zFar * invClipRange + let w = zNear * z return .init( .init(x, 0, 0, 0), .init(0, y, 0, 0), @@ -89,3 +89,7 @@ public extension simd_float4x4 { .init(0, 0, w, 0)) } } + +extension simd_quatf { + static var identity: Self { .init(real: 1, imag: .zero) } +} diff --git a/Sources/Voxelotl/Player.swift b/Sources/Voxelotl/Player.swift new file mode 100644 index 0000000..4eee088 --- /dev/null +++ b/Sources/Voxelotl/Player.swift @@ -0,0 +1,36 @@ +import simd + +struct Player { + private var _position = SIMD3.zero + private var _rotation = SIMD2.zero + + public var position: SIMD3 { self._position } + public var rotation: SIMD2 { self._rotation } + + mutating func update(deltaTime: Float) { + if let pad = GameController.current?.state { + let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1) + _rotation += turning * deltaTime * 3.0 + if _rotation.x < 0.0 { + _rotation.x += .pi * 2 + } else if _rotation.x > .pi * 2 { + _rotation.x -= .pi * 2 + } + _rotation.y = _rotation.y.clamp(-.pi * 0.5, .pi * 0.5) + + let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1) + + let rotc = cos(_rotation.x), rots = sin(_rotation.x) + _position += .init( + movement.x * rotc - movement.y * rots, + 0, + movement.y * rotc + movement.x * rots + ) * deltaTime * 3.0 + + if pad.pressed(.back) { + _position = .zero + _rotation = .zero + } + } + } +} diff --git a/Sources/Voxelotl/Rectangle.swift b/Sources/Voxelotl/Rectangle.swift new file mode 100644 index 0000000..481ec32 --- /dev/null +++ b/Sources/Voxelotl/Rectangle.swift @@ -0,0 +1,99 @@ +struct Point: 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) { + self.init(point.x, point.y) + } +} + +public struct Size: 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)) } +} + +struct Rect: Equatable { + var x: T, y: T, w: T, h: T + + var origin: Point { + get { .init(self.x, self.y) } + set(point) { self.x = point.x; self.y = point.y } + } + var size: Size { + 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, size: Size) { + 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: 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 { .init( + right > left ? right - left : left - right, + bottom > top ? bottom - top : top - bottom) } +} diff --git a/Sources/Voxelotl/Renderer.swift b/Sources/Voxelotl/Renderer.swift index ee6020e..e56fc4e 100644 --- a/Sources/Voxelotl/Renderer.swift +++ b/Sources/Voxelotl/Renderer.swift @@ -43,11 +43,11 @@ fileprivate let cubeIndices: [UInt16] = [ fileprivate let numFramesInFlight: Int = 3 fileprivate let depthFormat: MTLPixelFormat = .depth16Unorm -class Renderer { +public class Renderer { private var device: MTLDevice private var layer: CAMetalLayer - private var viewport: MTLViewport - private var aspectRatio: Float + private var backBufferSize: Size + private var _aspectRatio: Float private var queue: MTLCommandQueue private var lib: MTLLibrary private let passDescription = MTLRenderPassDescriptor() @@ -55,12 +55,19 @@ class Renderer { private var depthStencilState: MTLDepthStencilState private var depthTextures: [MTLTexture] + private var _commandBuf: MTLCommandBuffer! + private var _encoder: MTLRenderCommandEncoder! + private var _rt: (any CAMetalDrawable)! + private var vtxBuffer: MTLBuffer, idxBuffer: MTLBuffer private var defaultTexture: MTLTexture private var cubeTexture: MTLTexture? = nil private let inFlightSemaphore = DispatchSemaphore(value: numFramesInFlight) - private var frame = 0 + private var currentFrame = 0 + + var frame: Rect { .init(origin: .zero, size: self.backBufferSize) } + var aspectRatio: Float { self._aspectRatio } fileprivate static func createMetalDevice() -> MTLDevice? { MTLCopyAllDevices().reduce(nil, { best, dev in @@ -71,7 +78,7 @@ class Renderer { }) } - init(layer metalLayer: CAMetalLayer, size: SIMD2) throws { + internal init(layer metalLayer: CAMetalLayer, size: Size) throws { self.layer = metalLayer // Select best Metal device @@ -89,8 +96,8 @@ class Renderer { } self.queue = queue - self.viewport = Self.makeViewport(size: size) - self.aspectRatio = Float(size.x) / Float(size.y) + self.backBufferSize = size + self._aspectRatio = Float(self.backBufferSize.w) / Float(self.backBufferSize.w) passDescription.colorAttachments[0].loadAction = .clear passDescription.colorAttachments[0].storeAction = .store @@ -239,12 +246,12 @@ class Renderer { return newTexture } - private static func createDepthTexture(_ device: MTLDevice, _ size: SIMD2, format: MTLPixelFormat + private static func createDepthTexture(_ device: MTLDevice, _ size: Size, format: MTLPixelFormat ) -> MTLTexture? { let texDescriptor = MTLTextureDescriptor.texture2DDescriptor( pixelFormat: format, - width: size.x, - height: size.y, + width: size.w, + height: size.h, mipmapped: false) texDescriptor.depth = 1 texDescriptor.sampleCount = 1 @@ -261,111 +268,110 @@ class Renderer { return depthStencilTexture } - static func makeViewport(size: SIMD2) -> MTLViewport { + static func makeViewport(rect: Rect, znear: Double = 0.0, zfar: Double = 1.0) -> MTLViewport { MTLViewport( - originX: 0.0, originY: 0.0, - width: Double(size.x), - height: Double(size.y), - znear: 0, zfar: 1) + originX: Double(rect.x), + originY: Double(rect.y), + width: Double(rect.w), + height: Double(rect.h), + znear: znear, zfar: zfar) } - func resize(size: SIMD2) { - if Int(self.viewport.width) != size.x || Int(self.viewport.height) != size.y { + func resize(size: Size) { + if self.backBufferSize.w != size.w || self.backBufferSize.h != size.h { self.depthTextures = (0.. ShaderInstance in + ShaderInstance( + model: + .translate(instance.position) * + matrix_float4x4(instance.rotation) * + .scale(instance.scale), + color: .init( + UInt8(instance.color.x * 0xFF), + UInt8(instance.color.y * 0xFF), + UInt8(instance.color.z * 0xFF), + UInt8(instance.color.w * 0xFF))) + } + + self._encoder.setVertexBuffer(vtxBuffer, offset: 0, index: ShaderInputIdx.vertices.rawValue) // Ideal as long as our uniforms total 4 KB or less - encoder.setVertexBytes(instances, + self._encoder.setVertexBytes(instances, length: instances.count * MemoryLayout.stride, index: ShaderInputIdx.instance.rawValue) - encoder.setVertexBytes(&uniforms, + self._encoder.setVertexBytes(&uniforms, length: MemoryLayout.stride, index: ShaderInputIdx.uniforms.rawValue) - encoder.drawIndexedPrimitives( + self._encoder.drawIndexedPrimitives( type: .triangle, indexCount: cubeIndices.count, indexType: .uint16, indexBuffer: idxBuffer, indexBufferOffset: 0, instanceCount: instances.count) + } - encoder.endEncoding() - commandBuf.present(rt) - commandBuf.commit() + func endFrame() { + self._encoder.endEncoding() + self._commandBuf.present(self._rt) + self._commandBuf.commit() - self.frame &+= 1 - if self.frame == numFramesInFlight { - self.frame = 0 + self._rt = nil + self._encoder = nil + self._commandBuf = nil + + self.currentFrame &+= 1 + if self.currentFrame == numFramesInFlight { + self.currentFrame = 0 } } } diff --git a/Sources/Voxelotl/main.swift b/Sources/Voxelotl/main.swift index 20d4cd8..ee43e88 100644 --- a/Sources/Voxelotl/main.swift +++ b/Sources/Voxelotl/main.swift @@ -1,6 +1,10 @@ import Darwin +var rect = Rect(origin: .init(0, 0), size: .init(32, 32)) +rect.origin += Point(10, 10) + let app = Application( + delegate: Game(), configuration: ApplicationConfiguration( width: 1280, height: 720, diff --git a/Sources/Voxelotl/shader.metal b/Sources/Voxelotl/shader.metal index 14ff1a8..5c98a81 100644 --- a/Sources/Voxelotl/shader.metal +++ b/Sources/Voxelotl/shader.metal @@ -22,7 +22,7 @@ vertex FragmentInput vertexMain( FragmentInput out; out.position = ndc; - out.color = half4(i[instanceID].color); + out.color = half4(i[instanceID].color) / 255.0; out.normal = vtx[vertexID].normal; out.texCoord = vtx[vertexID].texCoord; return out; diff --git a/Sources/Voxelotl/shadertypes.h b/Sources/Voxelotl/shadertypes.h index 054b7fc..8ba0cf3 100644 --- a/Sources/Voxelotl/shadertypes.h +++ b/Sources/Voxelotl/shadertypes.h @@ -24,7 +24,7 @@ typedef struct { typedef struct { matrix_float4x4 model; - vector_float4 color; + vector_uchar4 color; } ShaderInstance; typedef struct {