mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-02 13:00:53 +00:00
turn the ChunkID type alias into a real type
This commit is contained in:
parent
76b61c49ae
commit
b804030594
@ -64,6 +64,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
|
||||
ChunkMeshGeneration.swift
|
||||
CubeMeshBuilder.swift
|
||||
ChunkMeshBuilder.swift
|
||||
ChunkID.swift
|
||||
World.swift
|
||||
Raycast.swift
|
||||
Camera.swift
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
44
Sources/Voxelotl/ChunkID.swift
Normal file
44
Sources/Voxelotl/ChunkID.swift
Normal 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 }
|
||||
}
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user