From 9640a4042f10bf01aaa8e9f60501f5ce79e9ac42 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Sat, 21 Sep 2024 10:21:26 +1000 Subject: [PATCH] rotating flip flags --- Sources/Voxelotl/Math/Extent.swift | 66 +++++++++ Sources/Voxelotl/Renderer/Sprite.swift | 16 ++- Sources/Voxelotl/Renderer/SpriteBatch.swift | 22 +-- Sources/Voxelotl/SpriteTestGame.swift | 148 ++++++++++++++++---- 4 files changed, 216 insertions(+), 36 deletions(-) diff --git a/Sources/Voxelotl/Math/Extent.swift b/Sources/Voxelotl/Math/Extent.swift index a3feba8..2e680ee 100644 --- a/Sources/Voxelotl/Math/Extent.swift +++ b/Sources/Voxelotl/Math/Extent.swift @@ -1,6 +1,15 @@ +import Foundation + public struct Extent: Hashable { public var left: T, top: T, right: T, bottom: T + public init(left: T, top: T, right: T, bottom: T) { + self.left = left + self.top = top + self.right = right + self.bottom = bottom + } + @inline(__always) public var topLeft: Point { .init(left, top) } @inline(__always) public var topRight: Point { .init(right, top) } @inline(__always) public var bottomLeft: Point { .init(left, bottom) } @@ -24,6 +33,14 @@ public extension Extent where T: SIMDScalar { self.right = to.x self.bottom = to.y } + + @inline(__always) static func + (lhs: Self, rhs: SIMD2) -> Self { + .init( + left: lhs.left + rhs.x, + top: lhs.top + rhs.y, + right: lhs.right + rhs.x, + bottom: lhs.bottom + rhs.y) + } } public extension Extent where T: AdditiveArithmetic { @@ -44,3 +61,52 @@ public extension Extent where T: Numeric { bottom: lhs.bottom * rhs.h) } } + +public extension Extent where T: BinaryInteger { + init(_ other: Extent) { + self.left = T(other.left) + self.top = T(other.top) + self.right = T(other.right) + self.bottom = T(other.bottom) + } + init(_ other: Extent) { + self.left = T(other.left) + self.top = T(other.top) + self.right = T(other.right) + self.bottom = T(other.bottom) + } +} + +public extension Extent where T: FloatingPoint { + init(_ other: Extent) { + self.left = T(other.left) + self.top = T(other.top) + self.right = T(other.right) + self.bottom = T(other.bottom) + } + + @inline(__always) static func / (lhs: Self, rhs: T) -> Self { + .init( + left: lhs.left / rhs, + top: lhs.top / rhs, + right: lhs.right / rhs, + bottom: lhs.bottom / rhs) + } +} + +public extension Extent where T: BinaryFloatingPoint { + init(_ other: Extent) { + self.left = T(other.left) + self.top = T(other.top) + self.right = T(other.right) + self.bottom = T(other.bottom) + } +} + +@inlinable public func floor(_ extent: Extent) -> Extent { + .init( + left: floor(extent.left), + top: floor(extent.top), + right: floor(extent.right), + bottom: floor(extent.bottom)) +} diff --git a/Sources/Voxelotl/Renderer/Sprite.swift b/Sources/Voxelotl/Renderer/Sprite.swift index 6686fda..342c8dd 100644 --- a/Sources/Voxelotl/Renderer/Sprite.swift +++ b/Sources/Voxelotl/Renderer/Sprite.swift @@ -6,8 +6,8 @@ public struct Sprite { } public static let none: Self = Self(rawValue: 0) - public static let x: Self = Self(rawValue: 1 << 0) - public static let y: Self = Self(rawValue: 1 << 1) + public static let horz: Self = Self(rawValue: 1 << 0) + public static let vert: Self = Self(rawValue: 1 << 1) public static let diag: Self = Self(rawValue: 1 << 2) } @@ -21,3 +21,15 @@ public struct Sprite { var flip: Flip var color: Color } + +public extension Sprite.Flip { + var clockwise: Self { + [Self](arrayLiteral: [ .vert, .diag ], [ .horz, .vert, .diag ], .diag, + [ .horz, .diag ], .horz, .none, [ .horz, .vert ], .vert)[Int(self.rawValue)] + } + + var counterClockwise: Self { + [Self](arrayLiteral: [ .horz, .diag ], .diag, [ .horz, .vert, .diag ], + [ .vert, .diag ], .vert, [ .horz, .vert ], .none, .horz)[Int(self.rawValue)] + } +} diff --git a/Sources/Voxelotl/Renderer/SpriteBatch.swift b/Sources/Voxelotl/Renderer/SpriteBatch.swift index 3c72966..83dc348 100644 --- a/Sources/Voxelotl/Renderer/SpriteBatch.swift +++ b/Sources/Voxelotl/Renderer/SpriteBatch.swift @@ -64,7 +64,7 @@ public struct SpriteBatch { let bias = origin / SIMD2(size) self.drawQuad(texture, .positions(position, size * scale, angle, bias), texCoord, color: color) } else { - self.drawQuad(texture, .positions(position - origin, size * scale), texCoord, color: color) + self.drawQuad(texture, .positions(position - origin * scale, size * scale), texCoord, color: color) } } @@ -83,7 +83,7 @@ public struct SpriteBatch { let bias = SIMD2(origin) / SIMD2(size) self.drawQuad(texture, .positions(position, size * scale, angle, bias), texCoord, color: color) } else { - self.drawQuad(texture, .positions(position, size * scale), texCoord, color: color) + self.drawQuad(texture, .positions(position - origin * scale, size * scale), texCoord, color: color) } } @@ -345,12 +345,11 @@ fileprivate extension SpriteBatch.Quad { v10: SIMD2(0, 0), v11: SIMD2(1, 0)) static func texcoords(_ flip: Sprite.Flip) -> Self { - let flipX = flip.contains(.x), flipY = flip.contains(.y) - //TODO: diag + let flipX = flip.contains(.horz), flipY = flip.contains(.vert), flipD = flip.contains(.diag) return .init( v00: .init(flipX ? 1 : 0, flipY ? 0 : 1), - v01: .init(flipX ? 0 : 1, flipY ? 0 : 1), - v10: .init(flipX ? 1 : 0, flipY ? 1 : 0), + v01: flipD ? .init(flipX ? 1 : 0, flipY ? 1 : 0) : .init(flipX ? 0 : 1, flipY ? 0 : 1), + v10: flipD ? .init(flipX ? 0 : 1, flipY ? 0 : 1) : .init(flipX ? 1 : 0, flipY ? 1 : 0), v11: .init(flipX ? 0 : 1, flipY ? 1 : 0)) } @@ -363,14 +362,17 @@ fileprivate extension SpriteBatch.Quad { } static func texcoords(_ texSize: Size, _ source: Rect, _ flip: Sprite.Flip) -> Self { - let flipX = flip.contains(.x), flipY = flip.contains(.y) + let flipX = flip.contains(.horz), flipY = flip.contains(.vert), flipD = flip.contains(.diag) let invSize = 1 / Size(texSize) let st = Extent(source) * invSize - //TODO: diag return .init( v00: .init(flipX ? st.right : st.left, flipY ? st.bottom : st.top), - v01: .init(flipX ? st.left : st.right, flipY ? st.bottom : st.top), - v10: .init(flipX ? st.right : st.left, flipY ? st.top : st.bottom), + v01: flipD + ? .init(flipX ? st.right : st.left, flipY ? st.top : st.bottom) + : .init(flipX ? st.left : st.right, flipY ? st.bottom : st.top), + v10: flipD + ? .init(flipX ? st.left : st.right, flipY ? st.bottom : st.top) + : .init(flipX ? st.right : st.left, flipY ? st.top : st.bottom), v11: .init(flipX ? st.left : st.right, flipY ? st.top : st.bottom)) } } diff --git a/Sources/Voxelotl/SpriteTestGame.swift b/Sources/Voxelotl/SpriteTestGame.swift index f781b46..bcea8e3 100644 --- a/Sources/Voxelotl/SpriteTestGame.swift +++ b/Sources/Voxelotl/SpriteTestGame.swift @@ -6,18 +6,7 @@ internal class SpriteTestGame: GameDelegate { private var player = TestPlayer(position: .one * 10) private var texture: RendererTexture2D! private var wireShark: RendererTexture2D! - - private static let levelWidth = 40, levelHeight = 23 - private var level: [UInt8] - - init() { - self.level = .init(repeating: 0, count: Self.levelWidth * Self.levelHeight) - for i in 0..(renderer.frame.size)) * 0.01), destination: nil, - angle: sin(player.rotate) * .pi * 0.1, center: nil, color: .init(renderer.clearColor).setAlpha(0.7)) // Draw level let scale: Float = 64 - for y in 0.. 0 { + shorkFlip = self.player.velocity.x < 0 ? shorkFlip.counterClockwise : shorkFlip.clockwise + } self.spriteBatch.draw(self.wireShark, - position: player.position, - scale: .init(sin(player.rotate * 5), cos(player.rotate * 3)), - angle: player.rotate, origin: .init(250, 275)) + position: self.player.position, scale: 0.2, origin: .init(250, 275 * 2), flip: shorkFlip) // Sliding door test let doorAngle = max(sin(player.rotate * 2.6) - 0.75, 0) * .pi @@ -78,7 +96,7 @@ internal class SpriteTestGame: GameDelegate { mpos /= SIMD2(Size(renderer.frame.size)) mpos *= SIMD2(self.spriteBatch.viewport!.size) } - let inter = 0.5 + sin(player.rotate * 10) * 0.5 + let inter = 0.5 + sin(Float(time.total) * 10) * 0.5 let color = Color.green.mix(.white, 0.3) let mesh = Mesh.init(vertices: [ .init(position: mpos, texCoord: .zero, color: .one), @@ -91,13 +109,95 @@ internal class SpriteTestGame: GameDelegate { } else { self.spriteBatch.draw(self.texture, vertices: mesh.vertices[..<3]) } + self.spriteBatch.end() } } +fileprivate struct TestLevel { + public static let levelWidth = 40, levelHeight = 23 + public static let cellScale: Float = 64 + + private var data: [UInt8] + + init() { + self.data = .init(repeating: 0, count: Self.levelWidth * Self.levelHeight) + for i in 0.., _ to: Bool) { + if p.x >= 0 && p.y >= 0 && p.x < Self.levelWidth && p.y < Self.levelHeight { + self.data[p.x + Self.levelWidth * p.y] = to ? 1 : 0 + } + } + + func get(_ p: Point) -> Bool { + if p.x < 0 || p.y < 0 || p.x >= Self.levelWidth || p.y >= Self.levelHeight { + return false + } + return self.data[p.x + Self.levelWidth * p.y] == 1 + } + + func check(_ p: SIMD2) -> Bool { + let p = SIMD2(p / TestLevel.cellScale, rounding: .down) + return self.get(Point(p.x, p.y)) + } + + func check(_ rect: Extent) -> Bool { + let p = Extent(floor(rect / Self.cellScale)) + return self.get(p.topLeft) || self.get(p.topRight) || self.get(p.bottomLeft) || self.get(p.bottomRight) + } +} + fileprivate struct TestPlayer { + static private let rect = Extent(left: -30, top: -63, right: 30, bottom: 0) + var position: SIMD2 + var velocity: SIMD2 = .zero var rotate: Float = 0 + + private var _onGround = false + var onGround: Bool { self._onGround } + + init(position: SIMD2) { + self.position = position + } + + mutating func update(deltaTime dt: Float, level: TestLevel) { + self.velocity.y += 1500 * dt + if abs(self.velocity.y) > 3000 { + self.velocity.y = .init(signOf: self.velocity.y, magnitudeOf: 3000) + } + + if self.velocity.x != 0 { + self.position.x += self.velocity.x * dt + if level.check(Self.rect + self.position) { + let offset = self.velocity.x < 0 ? Self.rect.left : Self.rect.right + self.position.x = round((self.position.x + offset) / TestLevel.cellScale) * TestLevel.cellScale - offset + self.position.x -= .init(signOf: self.velocity.x, magnitudeOf: .ulpOfOne * 12000); + self.velocity.x = 0 + } + } + if self.velocity.y != 0 { + self.position.y += self.velocity.y * dt + if level.check(Self.rect + (self.position)) { + let offset = self.velocity.y < 0 ? Self.rect.top : Self.rect.bottom + self.position.y = round((self.position.y + offset) / TestLevel.cellScale) * TestLevel.cellScale - offset + self.position.y -= .init(signOf: self.velocity.y, magnitudeOf: .ulpOfOne * 12000); + self.velocity.y = 0 + } + } + + if self.velocity.x != 0 { + self.rotate += abs(self.velocity.x) / 1000 * dt + } + + self._onGround = level.check(Self.rect + self.position + .init(0, .ulpOfOne * 24000)) + } } fileprivate extension Color {