From 9ef60faa86d1dd0facc5d1d1db28526e916a0e32 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Fri, 23 Aug 2024 16:55:59 +1000 Subject: [PATCH] improved perlin basic blockgen --- Sources/Voxelotl/CMakeLists.txt | 2 + Sources/Voxelotl/Chunk.swift | 12 +++- Sources/Voxelotl/Game.swift | 22 +++++--- Sources/Voxelotl/Math/FloatExtensions.swift | 5 +- .../Random/PerlinNoiseGenerator.swift | 56 +++++++++++++++++++ .../Random/RandomCollectionExtensions.swift | 19 +++++++ 6 files changed, 105 insertions(+), 11 deletions(-) create mode 100644 Sources/Voxelotl/Random/PerlinNoiseGenerator.swift create mode 100644 Sources/Voxelotl/Random/RandomCollectionExtensions.swift diff --git a/Sources/Voxelotl/CMakeLists.txt b/Sources/Voxelotl/CMakeLists.txt index 3e8a53a..6e9f848 100644 --- a/Sources/Voxelotl/CMakeLists.txt +++ b/Sources/Voxelotl/CMakeLists.txt @@ -20,6 +20,8 @@ add_executable(Voxelotl MACOSX_BUNDLE Random/Arc4Random.swift Random/PCG32Random.swift Random/Xoroshiro128.swift + Random/PerlinNoiseGenerator.swift + Random/RandomCollectionExtensions.swift # Resource classes NSImageLoader.swift diff --git a/Sources/Voxelotl/Chunk.swift b/Sources/Voxelotl/Chunk.swift index 543b67a..673b560 100644 --- a/Sources/Voxelotl/Chunk.swift +++ b/Sources/Voxelotl/Chunk.swift @@ -43,9 +43,15 @@ public struct Chunk { blocks[position.x + position.y * Self.yStride + position.z * Self.zStride].type = type } - mutating func fill(allBy calculation: () -> BlockType) { - blocks.indices.forEach { i in - blocks[i].type = calculation() + mutating func fill(allBy calculation: (_ position: SIMD3) -> BlockType) { + var i = 0 + for x in 0..(random: &random) + self.chunk.fill(allBy: { position in + let fpos = SIMD3(position) + return if fpos.y / Float(Chunk.chunkSize) + + noise.get(fpos * 0.07) * 0.7 + + noise.get(fpos * 0.321 + 100) * 0.3 < 0.6 { + .solid(.init( + r: Float16(noise.get(fpos * 0.1)), + g: Float16(noise.get(fpos * 0.1 + 10)), + b: Float16(noise.get(fpos * 0.1 + 100))).mix(.white, 0.4).linear) } else { .air } diff --git a/Sources/Voxelotl/Math/FloatExtensions.swift b/Sources/Voxelotl/Math/FloatExtensions.swift index 25d9e82..b65783b 100644 --- a/Sources/Voxelotl/Math/FloatExtensions.swift +++ b/Sources/Voxelotl/Math/FloatExtensions.swift @@ -3,8 +3,11 @@ public extension FloatingPoint { @inline(__always) var radians: Self { self * (Self.pi / 180) } @inline(__always) func lerp(_ a: Self, _ b: Self) -> Self { b * self + a * (1 - self) } - @inline(__always) func mlerp(_ a: Self, _ b: Self) -> Self { a + (b - a) * self } + @inline(__always) func mlerp(_ a: Self, _ b: Self) -> Self { a + self * (b - a) } @inline(__always) func clamp(_ a: Self, _ b: Self) -> Self { min(max(self, a), b) } @inline(__always) var saturated: Self { self.clamp(0, 1) } + + @inline(__always) func smoothStep() -> Self { self * self * (3 - 2 * self) } + @inline(__always) func smootherStep() -> Self { self * self * self * (self * (self * 6 - 15) + 10) } } diff --git a/Sources/Voxelotl/Random/PerlinNoiseGenerator.swift b/Sources/Voxelotl/Random/PerlinNoiseGenerator.swift new file mode 100644 index 0000000..d3eeb0f --- /dev/null +++ b/Sources/Voxelotl/Random/PerlinNoiseGenerator.swift @@ -0,0 +1,56 @@ +import Foundation + +public struct ImprovedPerlin { + private let p: [Int32] + + public init(random: inout any RandomProvider) { + self.p = (0..<0x100).map { Int32($0) }.shuffled(using: &random) + } + + public func get(_ point: SIMD3) -> T { + // Find unit cube containg point + let idx = SIMD3(Int(floor(point.x)), Int(floor(point.y)), Int(floor(point.z))) & 0xFF + // Find relative point in cube + let inner = point - SIMD3(floor(point.x), floor(point.y), floor(point.z)) + + // Compute fade curves for each axis + let u = inner.x.smootherStep() + let v = inner.y.smootherStep() + let w = inner.z.smootherStep() + + // Compute hash of the coordinates of the 8 cube corners + let a = idx.y + perm(idx.x) + let aa = idx.z + perm(a) + let ab = idx.z + perm(a + 1) + let b = idx.y + perm(idx.x + 1) + let ba = idx.z + perm(b) + let bb = idx.z + perm(b + 1) + + // Add blended results + return w.mlerp(v.mlerp( + u.mlerp( + grad(perm(aa), inner), + grad(perm(ba), .init(inner.x - 1, inner.y, inner.z))), + u.mlerp( + grad(perm(ab), .init(inner.x, inner.y - 1, inner.z)), + grad(perm(bb), .init(inner.x - 1, inner.y - 1, inner.z)))), + v.mlerp(u.mlerp( + grad(perm(aa + 1), .init(inner.x, inner.y, inner.z - 1)), + grad(perm(ba + 1), .init(inner.x - 1, inner.y, inner.z - 1))), + u.mlerp( + grad(perm(ab + 1), .init(inner.x, inner.y - 1, inner.z - 1)), + grad(perm(bb + 1), inner - .init(repeating: 1))))) + + @inline(__always) func perm(_ x: Int) -> Int { Int(self.p[x & 0xFF]) } + + func grad(_ hash: Int, _ point: SIMD3) -> T { + // Convert low 4 bits of hash code into 12 gradient directions + let low4 = hash & 0xF + var u = low4 < 8 ? point.x : point.y + var v = low4 < 4 ? point.y : (low4 == 0b1100 || low4 == 0b1110 ? point.x : point.z) + u = (low4 & 0x1) == 0 ? u : -u + v = (low4 & 0x2) == 0 ? v : -v + return u + v + } + } +} diff --git a/Sources/Voxelotl/Random/RandomCollectionExtensions.swift b/Sources/Voxelotl/Random/RandomCollectionExtensions.swift new file mode 100644 index 0000000..4e9cb5a --- /dev/null +++ b/Sources/Voxelotl/Random/RandomCollectionExtensions.swift @@ -0,0 +1,19 @@ +public extension MutableCollection { + mutating func shuffle(using provider: inout T) { + guard self.count > 1 else { + return + } + for (first, remaining) in zip(self.indices, stride(from: 0x100, to: 1, by: -1)) { + let i = self.index(first, offsetBy: provider.next(in: remaining)) + self.swapAt(first, i) + } + } +} + +public extension Sequence { + func shuffled(using provider: inout T) -> [Self.Element] { + var copy = Array(self) + copy.shuffle(using: &provider) + return copy + } +}