initial voxel implementation

This commit is contained in:
Alex Zenla 2024-08-18 18:16:27 -07:00
parent 9b455652ff
commit 799c7a2a55
Signed by: alex
GPG Key ID: C0780728420EBFE5
4 changed files with 133 additions and 26 deletions

View File

@ -3,6 +3,8 @@ add_executable(Voxelotl MACOSX_BUNDLE
test.png
Chunk.swift
shadertypes.h
shader.metal

View File

@ -0,0 +1,57 @@
public struct Chunk {
public static let chunkSize: Int = 16
public let position: SIMD3<Int>
private var blocks: [Block]
init(position: SIMD3<Int>, blocks: [Block]) {
self.position = position
self.blocks = blocks
}
init(position: SIMD3<Int>) {
self.position = position
self.blocks = Array(
repeating: BlockType.air,
count: Chunk.chunkSize * Chunk.chunkSize * Chunk.chunkSize
).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]
}
mutating func setBlockInternally(at position: SIMD3<Int>, type: BlockType) {
blocks[position.x + position.y * Chunk.chunkSize + position.z * Chunk.chunkSize * Chunk.chunkSize].type = type
}
mutating func fill(allBy calculation: () -> BlockType) {
blocks.indices.forEach { i in
blocks[i].type = calculation()
}
}
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])
}
}
}
}
}
public enum BlockType: Equatable {
case air
case solid(Color<Float16>)
}
public struct Block {
public var type: BlockType
public init(_ type: BlockType) {
self.type = type
}
}

View File

@ -1,4 +1,5 @@
import simd
import Foundation
struct Box {
var geometry: AABB
@ -24,26 +25,54 @@ struct Instance {
}
}
let boxes: [Box] = [
Box(geometry: .fromUnitCube(
position: .init( 0, -1, 0) * 2,
scale: .init(10, 0.1, 10) * 2)),
Box(geometry: .fromUnitCube(
position: .init(-2.5, 0, -3) * 2,
scale: .init(repeating: 2)),
color: .init(rgb888: 0xFF80BF).linear),
Box(geometry: .fromUnitCube(
position: .init(-2.5, -0.5, -5) * 2,
scale: .init(repeating: 2)),
color: .init(rgb888: 0xBFFFFF).linear)
]
class Game: GameDelegate {
private var fpsCalculator = FPSCalculator()
var camera = Camera(fov: 60, size: .one, range: 0.06...50)
var camera = Camera(fov: 60, size: .one, range: 0.06...900)
var player = Player()
var projection: matrix_float4x4 = .identity
var boxes: [Box] = []
init() {
var chunk = Chunk(position: .zero)
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,
]
chunk
.fill(allBy: { options[Int(arc4random_uniform(UInt32(options.count)))] })
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.position = SIMD3(0.5, Float(Chunk.chunkSize) + 0.5, 0.5)
}
func fixedUpdate(_ time: GameTime) {
}
@ -74,7 +103,7 @@ class Game: GameDelegate {
}
instances.append(
Instance(
position: .init(0, sin(totalTime * 1.5 * cubeSpeedMul) * 0.5, -2) * 2,
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))

View File

@ -20,7 +20,9 @@ struct Player {
private var _onGround: Bool = false
public var position: SIMD3<Float> { self._position + .init(0, Self.eyeLevel, 0) }
public var position: SIMD3<Float> {
get { self._position + .init(0, Self.eyeLevel, 0) } set { self._position = newValue }
}
public var rotation: SIMD2<Float> { self._rotation }
mutating func update(deltaTime: Float, boxes: [Box]) {
@ -36,22 +38,27 @@ struct Player {
}
self._rotation.y = self._rotation.y.clamp(-.pi * 0.5, .pi * 0.5)
if self._onGround {
// Movement on ground
let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
let rotc = cos(self._rotation.x), rots = sin(self._rotation.x)
self._velocity.x = (movement.x * rotc - movement.y * rots) * Self.accelerationCoeff
self._velocity.z = (movement.y * rotc + movement.x * rots) * Self.accelerationCoeff
// Movement (slower in air than on ground)
let movement = pad.leftStick.cardinalDeadzone(min: 0.1, max: 1)
let rotc = cos(self._rotation.x), rots = sin(self._rotation.x)
let movementScale: Float = self._onGround ? 1.0 : 0.4
self._velocity.x = (
movement.x * rotc - movement.y * rots
) * Self.accelerationCoeff * movementScale
self._velocity.z = (
movement.y * rotc + movement.x * rots
) * Self.accelerationCoeff * movementScale
if self._onGround {
// Jumping
if pad.pressed(.east) {
self._velocity.y = Self.jumpVelocity
self._onGround = false
}
}
// Flying
self._velocity.y += pad.rightTrigger * 36 * deltaTime
// Flying and unflying
self._velocity.y += (pad.rightTrigger - pad.leftTrigger) * 36 * deltaTime
// Reset
if pad.pressed(.back) {
@ -112,5 +119,17 @@ struct Player {
self._velocity.x = 0
self._velocity.z = 0
}
if self._velocity.x > 10 {
self._velocity.x = 10
}
if self._velocity.y > 10 {
self._velocity.y = 10
}
if abs(self._velocity.y) > 40 {
self._velocity.y = Float(signOf: self._velocity.y, magnitudeOf: 40.0)
}
}
}