This commit is contained in:
Alex Zenla 2024-09-01 04:17:22 -04:00
parent 64fcebf4e5
commit 09ae09511d
No known key found for this signature in database
GPG Key ID: 067B238899B51269
4 changed files with 114 additions and 26 deletions

View File

@ -1,3 +1,5 @@
import simd
public struct Chunk {
public static let shift = 4 // 16
public static let size: Int = 1 << shift
@ -11,6 +13,10 @@ public struct Chunk {
public let origin: SIMD3<Int>
private var blocks: [Block]
static func getID(position: SIMD3<Float>) -> SIMD3<Int> {
SIMD3(floor(position)) &>> Chunk.shift
}
init(position: SIMD3<Int>, blocks: [Block]) {
assert(blocks.count == Self.blockCount)
self.origin = position

View File

@ -78,6 +78,13 @@ class Game: GameDelegate {
}
self.player.update(deltaTime: deltaTime, world: world, camera: &camera)
let centerChunkID = Chunk.getID(position: self.player.position)
chunkGenerateNeighbors.forEach { offset in
let chunkID = centerChunkID &+ offset
if self.world.getChunk(id: chunkID) == nil {
self.world.generate(chunkID: chunkID)
}
}
}
func draw(_ renderer: Renderer, _ time: GameTime) {
@ -112,4 +119,34 @@ class Game: GameDelegate {
func resize(_ size: Size<Int>) {
self.camera.size = size
}
private let chunkGenerateNeighbors: [SIMD3<Int>] = [
SIMD3<Int>(-1, -1, -1),
SIMD3<Int>(0, -1, -1),
SIMD3<Int>(1, -1, -1),
SIMD3<Int>(-1, 0, -1),
SIMD3<Int>(0, 0, -1),
SIMD3<Int>(1, 0, -1),
SIMD3<Int>(-1, 1, -1),
SIMD3<Int>(0, 1, -1),
SIMD3<Int>(1, 1, -1),
SIMD3<Int>(-1, -1, 0),
SIMD3<Int>(0, -1, 0),
SIMD3<Int>(1, -1, 0),
SIMD3<Int>(-1, 0, 0),
SIMD3<Int>(0, 0, 0),
SIMD3<Int>(1, 0, 0),
SIMD3<Int>(-1, 1, 0),
SIMD3<Int>(0, 1, 0),
SIMD3<Int>(1, 1, 0),
SIMD3<Int>(-1, -1, 1),
SIMD3<Int>(0, -1, 1),
SIMD3<Int>(1, -1, 1),
SIMD3<Int>(-1, 0, 1),
SIMD3<Int>(0, 0, 1),
SIMD3<Int>(1, 0, 1),
SIMD3<Int>(-1, 1, 1),
SIMD3<Int>(0, 1, 1),
SIMD3<Int>(1, 1, 1),
]
}

View File

@ -4,6 +4,8 @@ public class World {
private var _chunks: Dictionary<SIMD3<Int>, Chunk>
private var _generator: WorldGenerator
let _chunkLock: NSLock = .init()
public init() {
self._chunks = [:]
self._generator = WorldGenerator()
@ -19,6 +21,13 @@ public class World {
self._chunks[position &>> Chunk.shift]?.setBlock(at: position, type: type)
}
func getChunk(id chunkID: SIMD3<Int>) -> Chunk? {
self._chunkLock.lock()
let chunk = self._chunks[chunkID]
self._chunkLock.unlock()
return chunk
}
func generate(width: Int, height: Int, depth: Int, seed: UInt64) {
self._generator.reset(seed: seed)
let orig = SIMD3(width, height, depth) / 2
@ -26,18 +35,27 @@ public class World {
for y in 0..<height {
for x in 0..<width {
let chunkID = SIMD3(x, y, z) &- orig
self._chunks[chunkID] = self._generator.makeChunk(id: chunkID)
self.generate(chunkID: chunkID)
}
}
}
}
func generate(chunkID: SIMD3<Int>) {
self._chunks[chunkID] = self._generator.makeChunk(id: chunkID)
if self._generator.isCurrentlyGenerating(id: chunkID) { return }
self._generator.makeChunk(id: chunkID) { chunk in
self._chunkLock.lock()
self._chunks[chunkID] = chunk
self._chunkLock.unlock()
}
}
var instances: [Instance] {
self._chunks.values.flatMap { chunk in
self._chunkLock.lock()
defer {
self._chunkLock.unlock()
}
return self._chunks.values.flatMap { chunk in
chunk.compactMap { block, position in
if case let .solid(color) = block.type {
Instance(

View File

@ -1,7 +1,11 @@
struct WorldGenerator {
var noise: ImprovedPerlin<Float>!, noise2: SimplexNoise<Float>!
import Foundation
public mutating func reset(seed: UInt64) {
class WorldGenerator: NSObject {
var noise: ImprovedPerlin<Float>!, noise2: SimplexNoise<Float>!
var generating: Set<SIMD3<Int>> = .init()
let generatingLock: NSLock = .init()
public func reset(seed: UInt64) {
var random: any RandomProvider
let initialState = SplitMix64.createState(seed: seed)
#if true
@ -14,26 +18,49 @@ struct WorldGenerator {
self.noise2 = SimplexNoise<Float>(random: &random)
}
public func makeChunk(id chunkID: SIMD3<Int>) -> Chunk {
let chunkOrigin = chunkID &<< Chunk.shift
var chunk = Chunk(position: chunkOrigin)
chunk.fill(allBy: { position in
let fpos = SIMD3<Float>(position)
let threshold: Float = 0.6
let value = fpos.y / Float(Chunk.size)
+ self.noise.get(fpos * 0.05) * 1.1
+ self.noise.get(fpos * 0.10) * 0.5
+ self.noise.get(fpos * 0.30) * 0.23
return if value < threshold {
.solid(.init(
hue: Float16(180 + self.noise2.get(fpos * 0.05) * 180),
saturation: Float16(0.5 + self.noise2.get(SIMD4(fpos * 0.05, 4)) * 0.5),
value: Float16(0.5 + self.noise2.get(SIMD4(fpos * 0.05, 9)) * 0.5).lerp(0.5, 1)).linear)
} else {
.air
public func isCurrentlyGenerating(id chunkID: SIMD3<Int>) -> Bool {
self.generatingLock.lock()
defer {
self.generatingLock.unlock()
}
return self.generating.contains(chunkID)
}
public func makeChunk(id chunkID: SIMD3<Int>, completion: @escaping (Chunk) -> Void) {
self.generatingLock.lock()
print(self.generating)
if !self.generating.insert(chunkID).inserted {
self.generatingLock.unlock()
return
}
self.generatingLock.unlock()
DispatchQueue.global(qos: .userInteractive).async {
defer {
self.generatingLock.lock()
self.generating.remove(chunkID)
self.generatingLock.unlock()
}
})
return chunk
let chunkOrigin = chunkID &<< Chunk.shift
var chunk = Chunk(position: chunkOrigin)
chunk.fill(allBy: { position in
let fpos = SIMD3<Float>(position)
let threshold: Float = 0.6
let value = fpos.y / Float(Chunk.size)
+ self.noise.get(fpos * 0.05) * 1.1
+ self.noise.get(fpos * 0.10) * 0.5
+ self.noise.get(fpos * 0.30) * 0.23
return if value < threshold {
.solid(.init(
hue: Float16(180 + self.noise2.get(fpos * 0.05) * 180),
saturation: Float16(0.5 + self.noise2.get(SIMD4(fpos * 0.05, 4)) * 0.5),
value: Float16(0.5 + self.noise2.get(SIMD4(fpos * 0.05, 9)) * 0.5).lerp(0.5, 1)).linear)
} else {
.air
}
})
completion(chunk)
}
}
}