initial voxel system revamp

This commit is contained in:
a dinosaur 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 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>
private var blocks: [Block]
init(position: SIMD3<Int>, blocks: [Block]) {
assert(blocks.count == Self.blockCount)
self.position = position
self.blocks = blocks
}
@ -13,24 +18,29 @@ public struct Chunk {
self.position = position
self.blocks = Array(
repeating: BlockType.air,
count: Chunk.chunkSize * Chunk.chunkSize * Chunk.chunkSize
count: Self.blockCount
).map { type in Block(type) }
}
func getBlockInternally(at position: SIMD3<Int>) -> Block {
blocks[position.x + position.y * Chunk.chunkSize + position.z * Chunk.chunkSize * Chunk.chunkSize]
func getBlock(at position: SIMD3<Int>) -> Block {
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) {
if position.x >= Chunk.chunkSize || position.y >= Chunk.chunkSize || position.z >= Chunk.chunkSize {
return
}
mutating func setBlock(at position: SIMD3<Int>, type: BlockType) {
if position.x < 0 || position.y < 0 || position.z < 0 {
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) {
@ -39,26 +49,75 @@ public struct Chunk {
}
}
func forEach(block perform: (SIMD3<Int>, Block) -> Void) {
for x in 0..<Chunk.chunkSize {
for y in 0..<Chunk.chunkSize {
for z in 0..<Chunk.chunkSize {
perform(SIMD3(x, y, z), blocks[x + y * Chunk.chunkSize + z * Chunk.chunkSize * Chunk.chunkSize])
func forEach(block perform: (Block, SIMD3<Int>) -> Void) {
for x in 0..<Self.chunkSize {
for y in 0..<Self.chunkSize {
for z in 0..<Self.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 {
case air
case solid(Color<Float16>)
case solid(_ color: Color<UInt8>)
}
public struct Block {
public var type: BlockType
public init(_ type: BlockType) {
self.type = type
}

View File

@ -1,11 +1,6 @@
import simd
import Foundation
struct Box {
var geometry: AABB
var color: Color<Float16> = .white
}
struct Instance {
let position: 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 player = Player()
var projection: matrix_float4x4 = .identity
var boxes: [Box] = []
var chunk = Chunk(position: .zero)
init() {
player.position = SIMD3(0.5, Float(Chunk.chunkSize) + 0.5, 0.5)
player.rotation = .init(.pi, 0)
let options: [BlockType] = [
.air,
.solid(.blue),
.air,
.solid(.red),
.air,
.solid(.green),
.air,
.solid(.white),
.air,
.solid(.cyan),
.air,
.solid(.yellow),
.air,
.solid(.magenta),
.air,
let colors: [Color<UInt8>] = [
.white,
.red, .blue, .green,
.magenta, .yellow, .cyan
]
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) {
@ -72,29 +59,17 @@ class Game: GameDelegate {
if let pad = GameController.current?.state {
// Delete block underneath player
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
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.rotation = .init(.pi, 0)
}
}
boxes = []
chunk.forEach { position, block in
if block.type == .air {
return
}
if case let .solid(color) = block.type {
boxes.append(Box(
geometry: .fromUnitCube(position: SIMD3<Float>(position) + 0.5, scale: .init(repeating: 0.5)),
color: color))
}
}
player.update(deltaTime: deltaTime, boxes: boxes)
player.update(deltaTime: deltaTime, chunk: chunk)
camera.position = player.eyePosition
camera.rotation = player.eyeRotation
}
@ -103,18 +78,14 @@ class Game: GameDelegate {
let totalTime = Float(time.total.asFloat)
let cubeSpeedMul: Float = 0.1
var instances: [Instance] = boxes.map {
Instance(
position: $0.geometry.center,
scale: $0.geometry.size * 0.5,
color: $0.color)
let instances = chunk.compactMap { block, position in
if case let .solid(color) = block.type {
Instance(
position: SIMD3<Float>(position) + 0.5,
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)
}

View File

@ -33,7 +33,7 @@ struct Player {
.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 {
// Turning input
let turning = pad.rightStick.radialDeadzone(min: 0.1, max: 1)
@ -69,12 +69,10 @@ struct Player {
// Move & handle collision
let checkCollision = { (position: SIMD3<Float>) -> Optional<AABB> in
for box in boxes {
let bounds = Self.bounds + position
if bounds.touching(box.geometry) {
return box.geometry
}
}
let bounds = Self.bounds + position
//if bounds.touching(blockGeometry) {
// return box.geometry
//}
return nil
}
self._position.x += self._velocity.x * deltaTime