Spatial Chunk Generation

This commit is contained in:
Alex Zenla 2024-09-07 02:38:54 -04:00
parent 76b61c49ae
commit f2b3b07832
No known key found for this signature in database
GPG Key ID: 067B238899B51269
4 changed files with 101 additions and 9 deletions

View File

@ -1,8 +1,10 @@
import Foundation import Foundation
import Spatial
public struct ChunkGeneration { public struct ChunkGeneration {
private let queue: OperationQueue private let queue: OperationQueue
private let localReadyChunks = ConcurrentDictionary<SIMD3<Int>, Chunk>() private let localReadyChunks = ConcurrentDictionary<SIMD3<Int>, Chunk>()
private var chunkGenerationOperations = ConcurrentDictionary<SIMD3<Int>, WeakChunkGenerationOperationHolder>()
private var generatingChunkSet = Set<SIMD3<Int>>() private var generatingChunkSet = Set<SIMD3<Int>>()
weak var world: World? weak var world: World?
@ -28,12 +30,51 @@ public struct ChunkGeneration {
} }
func queueGenerateJob(chunkID: SIMD3<Int>) { func queueGenerateJob(chunkID: SIMD3<Int>) {
self.queue.addOperation { let operation = ChunkGenerationOperation(chunkID: chunkID)
guard let world = self.world else { operation.world = self.world
return operation.localReadyChunks = self.localReadyChunks
operation.chunkGenerationOperations = self.chunkGenerationOperations
self.queue.addOperation(operation)
let holder = WeakChunkGenerationOperationHolder()
holder.operation = operation
self.chunkGenerationOperations[chunkID] = holder
}
public mutating func updatePriorityPosition(position: SIMD3<Float>) {
let centerChunkID = World.makeID(position: position)
let centerChunkPoint = Point3D(x: Double(centerChunkID.x), y: Double(centerChunkID.y), z: Double(centerChunkID.z))
self.chunkGenerationOperations.with { operations in
var remove: [SIMD3<Int>] = []
for (chunkID, operation) in operations {
if operation.operation == nil {
remove.append(chunkID)
continue
}
let chunkPoint = Point3D(x: Double(chunkID.x), y: Double(chunkID.y), z: Double(chunkID.z))
let distance = abs(chunkPoint.distance(to: centerChunkPoint))
let priority: Operation.QueuePriority
if distance < 3 {
priority = .veryHigh
} else if distance < 6 {
priority = .normal
} else if distance < 10 {
priority = .low
} else {
priority = .veryLow
}
if priority == .veryLow {
operation.operation?.cancel()
self.generatingChunkSet.remove(chunkID)
} else {
operation.operation?.queuePriority = priority
}
}
for item in remove {
operations.removeValue(forKey: item)
} }
let chunk = world.generateSingleChunkUncommitted(chunkID: chunkID)
self.localReadyChunks[chunkID] = chunk
} }
} }
@ -74,3 +115,41 @@ public struct ChunkGeneration {
} }
} }
} }
class WeakChunkGenerationOperationHolder {
weak var operation: ChunkGenerationOperation?
}
class ChunkGenerationOperation: Operation, @unchecked Sendable {
let chunkID: SIMD3<Int>
weak var world: World?
weak var localReadyChunks: ConcurrentDictionary<SIMD3<Int>, Chunk>?
weak var chunkGenerationOperations: ConcurrentDictionary<SIMD3<Int>, WeakChunkGenerationOperationHolder>?
init(chunkID: SIMD3<Int>) {
self.chunkID = chunkID
}
override func main() {
if isCancelled {
return
}
guard let world = self.world else {
return
}
guard let localReadyChunks = self.localReadyChunks else {
return
}
let chunk = world.generateSingleChunkUncommitted(chunkID: self.chunkID)
localReadyChunks[chunkID] = chunk
guard let chunkGenerationOperations = self.chunkGenerationOperations else {
return
}
chunkGenerationOperations.remove(key: chunkID)
}
}

View File

@ -76,6 +76,18 @@ public class ConcurrentDictionary<V: Hashable, T>: Collection {
} }
} }
@discardableResult public func remove(key: V) -> T? {
self.locked {
self.inner.removeValue(forKey: key)
}
}
public func with(_ perform: (inout [V : T]) -> Void) {
self.locked {
perform(&self.inner)
}
}
fileprivate func locked<X>(_ perform: () -> X) -> X { fileprivate func locked<X>(_ perform: () -> X) -> X {
self.lock.lock() self.lock.lock()
defer { defer {

View File

@ -83,7 +83,7 @@ class Game: GameDelegate {
} }
self.world.generateAdjacentChunksIfNeeded(position: self.player.position) self.world.generateAdjacentChunksIfNeeded(position: self.player.position)
self.world.update() self.world.update(priorityPosition: self.player.position)
} }
public static let material = Material( public static let material = Material(

View File

@ -107,7 +107,8 @@ public class World {
} }
} }
public func update() { public func update(priorityPosition: SIMD3<Float>) {
self._chunkGeneration.updatePriorityPosition(position: priorityPosition)
self._chunkGeneration.acceptReadyChunks() self._chunkGeneration.acceptReadyChunks()
} }