initial voxel system revamp

This commit is contained in:
2024-08-20 03:21:55 +10:00
parent 2e8f1de0a7
commit 9dd56faa4e
3 changed files with 102 additions and 74 deletions

View File

@ -1,10 +1,15 @@
public struct Chunk { public struct Chunk {
public static let chunkSize: Int = 16 public static let chunkSize: Int = 16
public static let blockCount = chunkSize * chunkSize * chunkSize
private static let yStride = chunkSize
private static let zStride = chunkSize * chunkSize
public let position: SIMD3<Int> public let position: SIMD3<Int>
private var blocks: [Block] private var blocks: [Block]
init(position: SIMD3<Int>, blocks: [Block]) { init(position: SIMD3<Int>, blocks: [Block]) {
assert(blocks.count == Self.blockCount)
self.position = position self.position = position
self.blocks = blocks self.blocks = blocks
} }
@ -13,24 +18,29 @@ public struct Chunk {
self.position = position self.position = position
self.blocks = Array( self.blocks = Array(
repeating: BlockType.air, repeating: BlockType.air,
count: Chunk.chunkSize * Chunk.chunkSize * Chunk.chunkSize count: Self.blockCount
).map { type in Block(type) } ).map { type in Block(type) }
} }
func getBlockInternally(at position: SIMD3<Int>) -> Block { func getBlock(at position: SIMD3<Int>) -> Block {
blocks[position.x + position.y * Chunk.chunkSize + position.z * Chunk.chunkSize * Chunk.chunkSize] if position.x < 0 || position.y < 0 || position.z < 0 {
Block(.air)
} else if position.x >= Self.chunkSize || position.y >= Self.chunkSize || position.z >= Self.chunkSize {
Block(.air)
} else {
blocks[position.x + position.y * Self.yStride + position.z * Self.zStride]
}
} }
mutating func setBlockInternally(at position: SIMD3<Int>, type: BlockType) { mutating func setBlock(at position: SIMD3<Int>, type: BlockType) {
if position.x >= Chunk.chunkSize || position.y >= Chunk.chunkSize || position.z >= Chunk.chunkSize {
return
}
if position.x < 0 || position.y < 0 || position.z < 0 { if position.x < 0 || position.y < 0 || position.z < 0 {
return return
} }
if position.x >= Self.chunkSize || position.y >= Self.chunkSize || position.z >= Self.chunkSize {
return
}
blocks[position.x + position.y * Chunk.chunkSize + position.z * Chunk.chunkSize * Chunk.chunkSize].type = type blocks[position.x + position.y * Self.yStride + position.z * Self.zStride].type = type
} }
mutating func fill(allBy calculation: () -> BlockType) { mutating func fill(allBy calculation: () -> BlockType) {
@ -39,26 +49,75 @@ public struct Chunk {
} }
} }
func forEach(block perform: (SIMD3<Int>, Block) -> Void) { func forEach(block perform: (Block, SIMD3<Int>) -> Void) {
for x in 0..<Chunk.chunkSize { for x in 0..<Self.chunkSize {
for y in 0..<Chunk.chunkSize { for y in 0..<Self.chunkSize {
for z in 0..<Chunk.chunkSize { for z in 0..<Self.chunkSize {
perform(SIMD3(x, y, z), blocks[x + y * Chunk.chunkSize + z * Chunk.chunkSize * Chunk.chunkSize]) let idx = x + y * Self.yStride + z * Self.zStride
let position = SIMD3(x, y, z)
perform(blocks[idx], position)
} }
} }
} }
} }
public func map<T>(block transform: (Block, SIMD3<Int>) throws -> T) rethrows -> [T] {
assert(self.blocks.count == Self.blockCount)
var out = [T]()
out.reserveCapacity(Self.blockCount)
var position = SIMD3<Int>()
for i in self.blocks.indices {
out.append(try transform(blocks[i], position))
position.x += 1
if position.x == Self.chunkSize {
position.x = 0
position.y += 1
if position.y == Self.chunkSize {
position.y = 0
position.z += 1
}
}
}
return out
}
public func compactMap<T>(block transform: (Block, SIMD3<Int>) throws -> T?) rethrows -> [T] {
assert(self.blocks.count == Self.blockCount)
var out = [T]()
out.reserveCapacity(Self.blockCount >> 1)
var position = SIMD3<Int>()
for i in self.blocks.indices {
if let element = try transform(blocks[i], position) {
out.append(element)
}
position.x += 1
if position.x == Self.chunkSize {
position.x = 0
position.y += 1
if position.y == Self.chunkSize {
position.y = 0
position.z += 1
}
}
}
return out
}
} }
public enum BlockType: Equatable { public enum BlockType: Equatable {
case air case air
case solid(Color<Float16>) case solid(_ color: Color<UInt8>)
} }
public struct Block { public struct Block {
public var type: BlockType public var type: BlockType
public init(_ type: BlockType) { public init(_ type: BlockType) {
self.type = type self.type = type
} }

View File

@ -1,11 +1,6 @@
import simd import simd
import Foundation import Foundation
struct Box {
var geometry: AABB
var color: Color<Float16> = .white
}
struct Instance { struct Instance {
let position: SIMD3<Float> let position: SIMD3<Float>
let scale: SIMD3<Float> let scale: SIMD3<Float>
@ -30,32 +25,24 @@ class Game: GameDelegate {
var camera = Camera(fov: 60, size: .one, range: 0.06...900) var camera = Camera(fov: 60, size: .one, range: 0.06...900)
var player = Player() var player = Player()
var projection: matrix_float4x4 = .identity var projection: matrix_float4x4 = .identity
var boxes: [Box] = []
var chunk = Chunk(position: .zero) var chunk = Chunk(position: .zero)
init() { init() {
player.position = SIMD3(0.5, Float(Chunk.chunkSize) + 0.5, 0.5) player.position = SIMD3(0.5, Float(Chunk.chunkSize) + 0.5, 0.5)
player.rotation = .init(.pi, 0) player.rotation = .init(.pi, 0)
let options: [BlockType] = [ let colors: [Color<UInt8>] = [
.air, .white,
.solid(.blue), .red, .blue, .green,
.air, .magenta, .yellow, .cyan
.solid(.red),
.air,
.solid(.green),
.air,
.solid(.white),
.air,
.solid(.cyan),
.air,
.solid(.yellow),
.air,
.solid(.magenta),
.air,
] ]
chunk.fill(allBy: { options[Int(arc4random_uniform(UInt32(options.count)))] }) chunk.fill(allBy: {
if (arc4random() & 0x1) == 0x1 {
.solid(colors[Int(arc4random_uniform(UInt32(colors.count)))])
} else {
.air
}
})
} }
func fixedUpdate(_ time: GameTime) { func fixedUpdate(_ time: GameTime) {
@ -72,29 +59,17 @@ class Game: GameDelegate {
if let pad = GameController.current?.state { if let pad = GameController.current?.state {
// Delete block underneath player // Delete block underneath player
if pad.pressed(.south) { if pad.pressed(.south) {
chunk.setBlockInternally(at: SIMD3(player.position + .down * 0.2), type: .air) chunk.setBlock(at: SIMD3(player.position + .down * 0.2), type: .air)
} }
// Player reset // Player reset
if pad.pressed(.back) { if pad.pressed(.back) {
player.position = SIMD3(0.5, Float(Chunk.chunkSize) + 0.5, 0.5) player.position = .init(repeating: 0.5) + .init(0, Float(Chunk.chunkSize), 0)
player.velocity = .zero player.velocity = .zero
player.rotation = .init(.pi, 0) player.rotation = .init(.pi, 0)
} }
} }
boxes = []
chunk.forEach { position, block in
if block.type == .air {
return
}
if case let .solid(color) = block.type { player.update(deltaTime: deltaTime, chunk: chunk)
boxes.append(Box(
geometry: .fromUnitCube(position: SIMD3<Float>(position) + 0.5, scale: .init(repeating: 0.5)),
color: color))
}
}
player.update(deltaTime: deltaTime, boxes: boxes)
camera.position = player.eyePosition camera.position = player.eyePosition
camera.rotation = player.eyeRotation camera.rotation = player.eyeRotation
} }
@ -103,18 +78,14 @@ class Game: GameDelegate {
let totalTime = Float(time.total.asFloat) let totalTime = Float(time.total.asFloat)
let cubeSpeedMul: Float = 0.1 let cubeSpeedMul: Float = 0.1
var instances: [Instance] = boxes.map { let instances = chunk.compactMap { block, position in
Instance( if case let .solid(color) = block.type {
position: $0.geometry.center, Instance(
scale: $0.geometry.size * 0.5, position: SIMD3<Float>(position) + 0.5,
color: $0.color) scale: .init(repeating: 0.5),
color: Color<Float16>(color).linear)
} else { nil }
} }
instances.append(
Instance(
position: .init(0, sin(totalTime * 1.5 * cubeSpeedMul) * 0.5, 0) * 2,
scale: .init(repeating: 0.5),
rotation: .init(angle: totalTime * 3.0 * cubeSpeedMul, axis: .init(0, 1, 0)),
color: .init(r: 0.5, g: 0.5, b: 1).linear))
renderer.batch(instances: instances, camera: self.camera) renderer.batch(instances: instances, camera: self.camera)
} }

View File

@ -33,7 +33,7 @@ struct Player {
.init(angle: self._rotation.x, axis: .up) .init(angle: self._rotation.x, axis: .up)
} }
mutating func update(deltaTime: Float, boxes: [Box]) { mutating func update(deltaTime: Float, chunk: Chunk) {
if let pad = GameController.current?.state { if let pad = GameController.current?.state {
// Turning input // Turning input
let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1) let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1)
@ -69,12 +69,10 @@ struct Player {
// Move & handle collision // Move & handle collision
let checkCollision = { (position: SIMD3<Float>) -> Optional<AABB> in let checkCollision = { (position: SIMD3<Float>) -> Optional<AABB> in
for box in boxes { let bounds = Self.bounds + position
let bounds = Self.bounds + position //if bounds.touching(blockGeometry) {
if bounds.touching(box.geometry) { // return box.geometry
return box.geometry //}
}
}
return nil return nil
} }
self._position.x += self._velocity.x * deltaTime self._position.x += self._velocity.x * deltaTime