mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 13:11:33 +00:00
initial voxel system revamp
This commit is contained in:
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user