turn the ChunkID type alias into a real type

This commit is contained in:
a dinosaur 2024-09-08 03:47:26 +10:00
parent 76b61c49ae
commit b804030594
9 changed files with 78 additions and 39 deletions

View File

@ -64,6 +64,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
ChunkMeshGeneration.swift
CubeMeshBuilder.swift
ChunkMeshBuilder.swift
ChunkID.swift
World.swift
Raycast.swift
Camera.swift

View File

@ -2,8 +2,8 @@ import Foundation
public struct ChunkGeneration {
private let queue: OperationQueue
private let localReadyChunks = ConcurrentDictionary<SIMD3<Int>, Chunk>()
private var generatingChunkSet = Set<SIMD3<Int>>()
private let localReadyChunks = ConcurrentDictionary<ChunkID, Chunk>()
private var generatingChunkSet = Set<ChunkID>()
weak var world: World?
@ -21,18 +21,18 @@ public struct ChunkGeneration {
self.generatingChunkSet.removeAll()
}
public mutating func generate(chunkID: SIMD3<Int>) {
public mutating func generate(chunkID: ChunkID) {
if generatingChunkSet.insert(chunkID).inserted {
self.queueGenerateJob(chunkID: chunkID)
}
}
func queueGenerateJob(chunkID: SIMD3<Int>) {
func queueGenerateJob(chunkID: ChunkID) {
self.queue.addOperation {
guard let world = self.world else {
return
}
let chunk = world.generateSingleChunkUncommitted(chunkID: chunkID)
let chunk = world.generateSingleChunkUncommitted(id: chunkID)
self.localReadyChunks[chunkID] = chunk
}
}
@ -41,12 +41,12 @@ public struct ChunkGeneration {
guard let world = self.world else {
return
}
let centerChunkID = World.makeID(position: position)
let centerChunkID = ChunkID(fromPosition: position)
let range = -2...2
for z in range {
for y in range {
for x in range {
let chunkID = centerChunkID &+ SIMD3(x, y, z)
let chunkID = centerChunkID &+ ChunkID(x, y, z)
if world.getChunk(id: chunkID) == nil {
self.generate(chunkID: chunkID)
}
@ -69,7 +69,7 @@ public struct ChunkGeneration {
}
for (chunkID, chunk) in self.localReadyChunks.take() {
world.addChunk(chunkID: chunkID, chunk: chunk)
world.addChunk(id: chunkID, chunk: chunk)
self.generatingChunkSet.remove(chunkID)
}
}

View File

@ -0,0 +1,44 @@
import simd
public struct ChunkID: Hashable {
public let id: SIMD3<Int>
init(id: SIMD3<Int>) {
self.id = id
}
}
public extension ChunkID {
@inline(__always) init(_ x: Int, _ y: Int, _ z: Int) { self.id = SIMD3(x, y, z) }
@inline(__always) init<F: BinaryFloatingPoint>(fromPosition position: SIMD3<F>) {
self.init(fromPosition: SIMD3(Int(floor(position.x)), Int(floor(position.y)), Int(floor(position.z))))
}
@inline(__always) init(fromPosition position: SIMD3<Int>) { self.id = position &>> Chunk.shift }
@inline(__always) func getPosition() -> SIMD3<Int> { self.id &<< Chunk.shift }
@inline(__always) func getPosition(offset: SIMD3<Int>) -> SIMD3<Int> { self.id &<< Chunk.shift &+ offset }
@inline(__always) func getFloatPosition() -> SIMD3<Float> { SIMD3<Float>(self.id) * Float(Chunk.size) }
@inline(__always) func getFloatPosition(offset: SIMD3<Float>) -> SIMD3<Float> {
SIMD3<Float>(self.id) * Float(Chunk.size) + offset
}
@inline(__always) static func &+ (lhs: Self, rhs: Self) -> Self { .init(id: lhs.id &+ rhs.id) }
@inline(__always) static func &- (lhs: Self, rhs: Self) -> Self { .init(id: lhs.id &- rhs.id) }
}
public extension ChunkID {
@inlinable func distance(_ other: Self) -> Float {
simd_distance(SIMD3<Float>(self.id), SIMD3<Float>(other.id))
}
}
public extension ChunkID {
static var axes: [Self] {
[ .X, .Y, .Z ].map { (j: SIMD3<Int>) -> Self in .init(id: j) }
}
}
public extension SIMD3 where Scalar == Int {
init(_ chunkID: ChunkID) { self = chunkID.id }
}

View File

@ -2,7 +2,7 @@ import Foundation
public struct ChunkMeshGeneration {
private let queue: OperationQueue
private let localReadyMeshes = ConcurrentDictionary<SIMD3<Int>, RendererMesh?>()
private let localReadyMeshes = ConcurrentDictionary<ChunkID, RendererMesh?>()
weak var game: Game?
weak var renderer: Renderer?
@ -14,11 +14,11 @@ public struct ChunkMeshGeneration {
self.queue.qualityOfService = .userInitiated
}
public mutating func generate(chunkID: SIMD3<Int>, chunk: Chunk) {
self.queueGenerateJob(chunkID: chunkID, chunk: chunk)
public mutating func generate(id chunkID: ChunkID, chunk: Chunk) {
self.queueGenerateJob(id: chunkID, chunk: chunk)
}
func queueGenerateJob(chunkID: SIMD3<Int>, chunk: Chunk) {
func queueGenerateJob(id chunkID: ChunkID, chunk: Chunk) {
self.queue.addOperation {
guard let game = self.game else {
return

View File

@ -7,7 +7,7 @@ class Game: GameDelegate {
var projection: matrix_float4x4 = .identity
var world = World(generator: StandardWorldGenerator())
var cubeMesh: RendererMesh?
var renderChunks = [SIMD3<Int>: RendererMesh?]()
var renderChunks = [ChunkID: RendererMesh?]()
var chunkMeshGeneration: ChunkMeshGeneration!
var modelBatch: ModelBatch!
@ -77,9 +77,9 @@ class Game: GameDelegate {
// Regenerate current chunk
if regenChunk {
let chunkID = World.makeID(position: self.player.position)
let chunk = self.world.generateSingleChunkUncommitted(chunkID: chunkID)
self.world.addChunk(chunkID: chunkID, chunk: chunk)
let chunkID = ChunkID(fromPosition: self.player.position)
let chunk = self.world.generateSingleChunkUncommitted(id: chunkID)
self.world.addChunk(id: chunkID, chunk: chunk)
}
self.world.generateAdjacentChunksIfNeeded(position: self.player.position)
@ -101,7 +101,7 @@ class Game: GameDelegate {
// Update chunk meshes if needed
self.world.handleRenderDamagedChunks { id, chunk in
self.chunkMeshGeneration.generate(chunkID: id, chunk: chunk)
self.chunkMeshGeneration.generate(id: id, chunk: chunk)
}
self.chunkMeshGeneration.acceptReadyMeshes()
@ -111,7 +111,7 @@ class Game: GameDelegate {
if chunk == nil {
continue
}
let drawPos = SIMD3<Float>(id &<< Chunk.shift)
let drawPos = id.getFloatPosition()
self.modelBatch.draw(.init(mesh: chunk!, material: Self.material), position: drawPos)
}

View File

@ -15,7 +15,7 @@ struct StandardWorldGenerator: WorldGenerator {
self.colorNoise = .init(random: &random, octaves: 15, frequency: 0.006667, amplitude: 3)
}
public func makeChunk(id chunkID: SIMD3<Int>) -> Chunk {
public func makeChunk(id chunkID: ChunkID) -> Chunk {
let blockFunc = { (height: Float, position: SIMD3<Float>) -> BlockType in
#if true
let value = height + self.terrainNoise.get(position * SIMD3(1, 2, 1))
@ -37,7 +37,7 @@ struct StandardWorldGenerator: WorldGenerator {
saturation: 0.47, value: 0.9).linear)
}
let chunkOrigin = chunkID &<< Chunk.shift
let chunkOrigin = chunkID.getPosition()
var chunk = Chunk(position: chunkOrigin)
for z in 0..<Chunk.size {
for x in 0..<Chunk.size {

View File

@ -10,8 +10,8 @@ struct TerrorTowerGenerator: WorldGenerator {
self.noise2 = LayeredNoise(random: &random, octaves: 3, frequency: 0.1)
}
public func makeChunk(id chunkID: SIMD3<Int>) -> Chunk {
let chunkOrigin = chunkID &<< Chunk.shift
public func makeChunk(id chunkID: ChunkID) -> Chunk {
let chunkOrigin = chunkID.getPosition()
var chunk = Chunk(position: chunkOrigin)
chunk.fill(allBy: { position in
let fpos = SIMD3<Float>(chunkOrigin &+ position)

View File

@ -1,6 +1,6 @@
public protocol WorldGenerator {
mutating func reset(seed: UInt64)
func makeChunk(id: SIMD3<Int>) -> Chunk
func makeChunk(id: ChunkID) -> Chunk
}
internal extension RandomProvider where Output == UInt64, Self: RandomSeedable, SeedType == UInt64 {

View File

@ -1,12 +1,6 @@
import Foundation
public class World {
public typealias ChunkID = SIMD3<Int>
@inline(__always) public static func makeID<F: BinaryFloatingPoint>(position: SIMD3<F>) -> ChunkID {
makeID(position: SIMD3(Int(floor(position.x)), Int(floor(position.y)), Int(floor(position.z))))
}
@inline(__always) public static func makeID(position: SIMD3<Int>) -> ChunkID { position &>> Chunk.shift }
private var _chunks: Dictionary<ChunkID, Chunk>
private var _chunkDamage: Set<ChunkID>
private var _generator: any WorldGenerator
@ -21,24 +15,24 @@ public class World {
}
func getBlock(at position: SIMD3<Int>) -> Block {
return if let chunk = self._chunks[position &>> Chunk.shift] {
if let chunk = self._chunks[ChunkID(fromPosition: position)] {
chunk.getBlock(at: position)
} else { Block(.air) }
}
func setBlock(at position: SIMD3<Int>, type: BlockType) {
// Find the chunk containing the block position
let chunkID = position &>> Chunk.shift
let chunkID = ChunkID(fromPosition: position)
if let idx = self._chunks.index(forKey: chunkID) {
// Set the block and mark the containing chunk for render update
self._chunks.values[idx].setBlock(at: position, type: type)
self._chunkDamage.insert(chunkID)
// Mark adjacent chunks for render update when placing along the chunk border
let internalPos = position &- chunkID &<< Chunk.shift
let internalPos = position &- chunkID.getPosition()
for (i, ofs) in zip(internalPos.indices, [ SIMD3<Int>.X, .Y, .Z ]) {
if internalPos[i] == 0 {
let id = chunkID &- ofs
let id = chunkID &- ChunkID(id: ofs)
if let other = self._chunks[id],
// optim: Damage adjacent chunk only if block is touching a solid
case .solid = other.getBlock(internal: (internalPos &- ofs) & Chunk.mask).type
@ -46,7 +40,7 @@ public class World {
self._chunkDamage.insert(id)
}
} else if internalPos[i] == Chunk.size - 1 {
let id = chunkID &+ ofs
let id = chunkID &+ ChunkID(id: ofs)
if let other = self._chunks[id],
// optim: Damage adjacent chunk only if block is touching a solid
case .solid = other.getBlock(internal: (internalPos &+ ofs) & Chunk.mask).type
@ -62,7 +56,7 @@ public class World {
self._chunks[chunkID]
}
public func forEachChunk(_ body: @escaping (_ id: SIMD3<Int>, _ chunk: Chunk) throws -> Void) rethrows {
public func forEachChunk(_ body: @escaping (_ id: ChunkID, _ chunk: Chunk) throws -> Void) rethrows {
for i in self._chunks {
try body(i.key, i.value)
}
@ -80,14 +74,14 @@ public class World {
for z in 0..<depth {
for y in 0..<height {
for x in 0..<width {
let chunkID = SIMD3(x, y, z) &- orig
let chunkID = ChunkID(id: SIMD3(x, y, z) &- orig)
self._chunkGeneration.generate(chunkID: chunkID)
}
}
}
}
func generateSingleChunkUncommitted(chunkID: SIMD3<Int>) -> Chunk {
func generateSingleChunkUncommitted(id chunkID: ChunkID) -> Chunk {
self._generator.makeChunk(id: chunkID)
}
@ -95,10 +89,10 @@ public class World {
self._chunkGeneration.generateAdjacentIfNeeded(position: position)
}
public func addChunk(chunkID: ChunkID, chunk: Chunk) {
public func addChunk(id chunkID: ChunkID, chunk: Chunk) {
self._chunks[chunkID] = chunk
self._chunkDamage.insert(chunkID)
for i: ChunkID in [ .X, .Y, .Z ] {
for i in ChunkID.axes {
for otherID in [ chunkID &- i, chunkID &+ i ] {
if self._chunks.keys.contains(otherID) {
self._chunkDamage.insert(otherID)