diff --git a/Sources/Voxelotl/Random/PerlinNoiseGenerator.swift b/Sources/Voxelotl/Random/PerlinNoiseGenerator.swift index f45e103..f96657b 100644 --- a/Sources/Voxelotl/Random/PerlinNoiseGenerator.swift +++ b/Sources/Voxelotl/Random/PerlinNoiseGenerator.swift @@ -1,6 +1,6 @@ import Foundation -public struct ImprovedPerlin: CoherentNoise3D { +public struct ImprovedPerlin: CoherentNoise2D, CoherentNoise3D { private let p: [Int16] public init(permutation: [Int16]) { @@ -12,6 +12,31 @@ public struct ImprovedPerlin: CoherentNoise self.p = (0..<0x100).map { Int16($0) }.shuffled(using: &random) } + public func get(_ point: SIMD2) -> T { + // Find unit square + let idx = SIMD2(Int(floor(point.x)), Int(floor(point.y))) & 0xFF + // Find relative point in square + let inner = point - SIMD2(floor(point.x), floor(point.y)) + + // Compute fade curves for each axis + let u = inner.x.smootherStep() + let v = inner.y.smootherStep() + + // Compute hash of the coordinates of the 4 square corners + let a = idx.y + perm(idx.x), b = idx.y + perm(idx.x + 1) + let aa = perm(a), ab = perm(a + 1) + let ba = perm(b), bb = perm(b + 1) + + // Add blended results + return v.mlerp( + u.mlerp( + grad(perm(aa), inner), + grad(perm(ba), .init(inner.x - 1, inner.y))), + u.mlerp( + grad(perm(ab), .init(inner.x, inner.y - 1)), + grad(perm(bb), inner - .init(repeating: 1)))) + } + 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 @@ -45,17 +70,19 @@ public struct ImprovedPerlin: CoherentNoise 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]) } + @inline(__always) fileprivate 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 - } + @inline(__always) fileprivate func grad(_ hash: Int, _ point: SIMD2) -> T { grad(hash, SIMD3(point, 0)) } + + fileprivate 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 } }