mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 21:21:34 +00:00
implement parallel chunk mesh building
This commit is contained in:
@ -54,6 +54,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
|
|||||||
# Game logic classes
|
# Game logic classes
|
||||||
Chunk.swift
|
Chunk.swift
|
||||||
ChunkGeneration.swift
|
ChunkGeneration.swift
|
||||||
|
ChunkMeshGeneration.swift
|
||||||
WorldGenerator.swift
|
WorldGenerator.swift
|
||||||
CubeMeshBuilder.swift
|
CubeMeshBuilder.swift
|
||||||
ChunkMeshBuilder.swift
|
ChunkMeshBuilder.swift
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
struct ChunkMeshBuilder {
|
struct ChunkMeshBuilder {
|
||||||
public static func build(world: World, chunkID: SIMD3<Int>) -> Mesh<VertexPositionNormalColorTexcoord, UInt16> {
|
public static func build(world: World, chunk: Chunk) -> Mesh<VertexPositionNormalColorTexcoord, UInt16> {
|
||||||
guard let chunk = world.getChunk(id: chunkID) else { return .empty }
|
|
||||||
|
|
||||||
var vertices = [VertexPositionNormalColorTexcoord]()
|
var vertices = [VertexPositionNormalColorTexcoord]()
|
||||||
var indices = [UInt16]()
|
var indices = [UInt16]()
|
||||||
chunk.forEach { block, position in
|
chunk.forEach { block, position in
|
||||||
|
47
Sources/Voxelotl/ChunkMeshGeneration.swift
Normal file
47
Sources/Voxelotl/ChunkMeshGeneration.swift
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public struct ChunkMeshGeneration {
|
||||||
|
private let queue: OperationQueue
|
||||||
|
private let localReadyMeshes = ConcurrentDictionary<SIMD3<Int>, RendererMesh>()
|
||||||
|
|
||||||
|
weak var game: Game?
|
||||||
|
weak var renderer: Renderer?
|
||||||
|
|
||||||
|
init(queue: DispatchQueue) {
|
||||||
|
self.queue = OperationQueue()
|
||||||
|
self.queue.underlyingQueue = queue
|
||||||
|
self.queue.maxConcurrentOperationCount = 8
|
||||||
|
self.queue.qualityOfService = .userInitiated
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func generate(chunkID: SIMD3<Int>, chunk: Chunk) {
|
||||||
|
self.queueGenerateJob(chunkID: chunkID, chunk: chunk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func queueGenerateJob(chunkID: SIMD3<Int>, chunk: Chunk) {
|
||||||
|
self.queue.addOperation {
|
||||||
|
guard let game = self.game else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let renderer = self.renderer else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let mesh = ChunkMeshBuilder.build(world: game.world, chunk: chunk)
|
||||||
|
self.localReadyMeshes[chunkID] = renderer.createMesh(mesh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public mutating func acceptReadyMeshes() {
|
||||||
|
guard let game = self.game else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.waitUntilAllOperationsAreFinished()
|
||||||
|
|
||||||
|
for (chunkID, mesh) in self.localReadyMeshes.take() {
|
||||||
|
game.renderChunks.updateValue(mesh, forKey: chunkID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ class Game: GameDelegate {
|
|||||||
var world = World()
|
var world = World()
|
||||||
var cubeMesh: RendererMesh?
|
var cubeMesh: RendererMesh?
|
||||||
var renderChunks = [SIMD3<Int>: RendererMesh]()
|
var renderChunks = [SIMD3<Int>: RendererMesh]()
|
||||||
|
var chunkMeshGeneration: ChunkMeshGeneration!
|
||||||
|
|
||||||
func create(_ renderer: Renderer) {
|
func create(_ renderer: Renderer) {
|
||||||
self.resetPlayer()
|
self.resetPlayer()
|
||||||
@ -35,6 +36,9 @@ class Game: GameDelegate {
|
|||||||
self.cubeMesh = renderer.createMesh(CubeMeshBuilder.build(bound: .fromUnitCube(position: .zero, scale: .one)))
|
self.cubeMesh = renderer.createMesh(CubeMeshBuilder.build(bound: .fromUnitCube(position: .zero, scale: .one)))
|
||||||
|
|
||||||
renderer.clearColor = Color<Double>.black.mix(.white, 0.1).linear
|
renderer.clearColor = Color<Double>.black.mix(.white, 0.1).linear
|
||||||
|
self.chunkMeshGeneration = .init(queue: .global(qos: .userInitiated))
|
||||||
|
self.chunkMeshGeneration.game = self
|
||||||
|
self.chunkMeshGeneration.renderer = renderer
|
||||||
}
|
}
|
||||||
|
|
||||||
private func resetPlayer() {
|
private func resetPlayer() {
|
||||||
@ -110,13 +114,9 @@ class Game: GameDelegate {
|
|||||||
|
|
||||||
// Update chunk meshes if needed
|
// Update chunk meshes if needed
|
||||||
self.world.handleRenderDamagedChunks { id, chunk in
|
self.world.handleRenderDamagedChunks { id, chunk in
|
||||||
let mesh = ChunkMeshBuilder.build(world: self.world, chunkID: id)
|
self.chunkMeshGeneration.generate(chunkID: id, chunk: chunk)
|
||||||
if let renderMesh = renderer.createMesh(mesh) {
|
|
||||||
self.renderChunks[id] = renderMesh
|
|
||||||
} else {
|
|
||||||
self.renderChunks.removeValue(forKey: id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
self.chunkMeshGeneration.acceptReadyMeshes()
|
||||||
|
|
||||||
for (id, chunk) in self.renderChunks {
|
for (id, chunk) in self.renderChunks {
|
||||||
let drawPos = SIMD3<Float>(id &<< Chunk.shift)
|
let drawPos = SIMD3<Float>(id &<< Chunk.shift)
|
||||||
|
Reference in New Issue
Block a user