mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 13:11:33 +00:00
basic block picking
This commit is contained in:
@ -38,6 +38,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
|
|||||||
|
|
||||||
# Game logic classes
|
# Game logic classes
|
||||||
Chunk.swift
|
Chunk.swift
|
||||||
|
Raycast.swift
|
||||||
Player.swift
|
Player.swift
|
||||||
Game.swift
|
Game.swift
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ class Game: GameDelegate {
|
|||||||
var projection: matrix_float4x4 = .identity
|
var projection: matrix_float4x4 = .identity
|
||||||
var chunk = Chunk(position: .zero)
|
var chunk = Chunk(position: .zero)
|
||||||
|
|
||||||
|
var rayhitPos = SIMD3<Float>.zero
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.resetPlayer()
|
self.resetPlayer()
|
||||||
self.generateWorld()
|
self.generateWorld()
|
||||||
@ -75,10 +77,10 @@ class Game: GameDelegate {
|
|||||||
|
|
||||||
let deltaTime = min(Float(time.delta.asFloat), 1.0 / 15)
|
let deltaTime = min(Float(time.delta.asFloat), 1.0 / 15)
|
||||||
|
|
||||||
|
var destroy = false
|
||||||
if let pad = GameController.current?.state {
|
if let pad = GameController.current?.state {
|
||||||
// Delete block underneath player
|
|
||||||
if pad.pressed(.south) {
|
if pad.pressed(.south) {
|
||||||
self.chunk.setBlock(at: SIMD3(player.position + .down * 0.2), type: .air)
|
destroy = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Player reset
|
// Player reset
|
||||||
@ -102,17 +104,40 @@ class Game: GameDelegate {
|
|||||||
self.player.update(deltaTime: deltaTime, chunk: chunk)
|
self.player.update(deltaTime: deltaTime, chunk: chunk)
|
||||||
self.camera.position = player.eyePosition
|
self.camera.position = player.eyePosition
|
||||||
self.camera.rotation = player.eyeRotation
|
self.camera.rotation = player.eyeRotation
|
||||||
|
|
||||||
|
if let hit = raycast(
|
||||||
|
chunk: chunk,
|
||||||
|
origin: player.eyePosition,
|
||||||
|
direction: .forward * simd_matrix3x3(player.eyeRotation),
|
||||||
|
maxDistance: 3.333
|
||||||
|
) {
|
||||||
|
self.rayhitPos = hit.position
|
||||||
|
if destroy {
|
||||||
|
self.chunk.setBlock(at: hit.map, type: .air)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(_ renderer: Renderer, _ time: GameTime) {
|
func draw(_ renderer: Renderer, _ time: GameTime) {
|
||||||
let instances = chunk.compactMap { block, position in
|
let totalTime = Float(time.total.asFloat)
|
||||||
|
|
||||||
|
var instances = chunk.compactMap { block, position in
|
||||||
if case let .solid(color) = block.type {
|
if case let .solid(color) = block.type {
|
||||||
Instance(
|
Instance(
|
||||||
position: SIMD3<Float>(position) + 0.5,
|
position: SIMD3<Float>(chunk.position &+ position) + 0.5,
|
||||||
scale: .init(repeating: 0.5),
|
scale: .init(repeating: 0.5),
|
||||||
color: color)
|
color: color)
|
||||||
} else { nil }
|
} else { nil }
|
||||||
}
|
}
|
||||||
|
instances.append(
|
||||||
|
Instance(
|
||||||
|
position: rayhitPos,
|
||||||
|
scale: .init(repeating: 0.0725 * 0.5),
|
||||||
|
rotation:
|
||||||
|
.init(angle: totalTime * 3.0, axis: .init(0, 1, 0)) *
|
||||||
|
.init(angle: totalTime * 1.5, axis: .init(1, 0, 0)) *
|
||||||
|
.init(angle: totalTime * 0.7, axis: .init(0, 0, 1)),
|
||||||
|
color: .init(r: 0.5, g: 0.5, b: 1).linear))
|
||||||
if !instances.isEmpty {
|
if !instances.isEmpty {
|
||||||
renderer.batch(instances: instances, camera: self.camera)
|
renderer.batch(instances: instances, camera: self.camera)
|
||||||
}
|
}
|
||||||
|
88
Sources/Voxelotl/Raycast.swift
Normal file
88
Sources/Voxelotl/Raycast.swift
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import simd
|
||||||
|
|
||||||
|
struct RaycastHit {
|
||||||
|
let position: SIMD3<Float>
|
||||||
|
let distance: Float
|
||||||
|
let map: SIMD3<Int>
|
||||||
|
let normal: SIMD3<Float>
|
||||||
|
}
|
||||||
|
|
||||||
|
func raycast(
|
||||||
|
chunk: Chunk,
|
||||||
|
origin rayPosition: SIMD3<Float>,
|
||||||
|
direction: SIMD3<Float>,
|
||||||
|
maxDistance: Float
|
||||||
|
) -> Optional<RaycastHit> {
|
||||||
|
let deltaDistance = abs(SIMD3(repeating: simd_length(direction)) / direction)
|
||||||
|
|
||||||
|
var mapPosition = SIMD3<Int>(floor(rayPosition))
|
||||||
|
var sideDistance: SIMD3<Float> = .zero
|
||||||
|
var step: SIMD3<Int> = .zero
|
||||||
|
if direction.x < 0 {
|
||||||
|
step.x = -1
|
||||||
|
sideDistance.x = (rayPosition.x - Float(mapPosition.x)) * deltaDistance.x
|
||||||
|
} else {
|
||||||
|
step.x = 1
|
||||||
|
sideDistance.x = (Float(mapPosition.x) + 1 - rayPosition.x) * deltaDistance.x
|
||||||
|
}
|
||||||
|
if direction.y < 0 {
|
||||||
|
step.y = -1
|
||||||
|
sideDistance.y = (rayPosition.y - Float(mapPosition.y)) * deltaDistance.y
|
||||||
|
} else {
|
||||||
|
step.y = 1
|
||||||
|
sideDistance.y = (Float(mapPosition.y) + 1 - rayPosition.y) * deltaDistance.y
|
||||||
|
}
|
||||||
|
if direction.z < 0 {
|
||||||
|
step.z = -1
|
||||||
|
sideDistance.z = (rayPosition.z - Float(mapPosition.z)) * deltaDistance.z
|
||||||
|
} else {
|
||||||
|
step.z = 1
|
||||||
|
sideDistance.z = (Float(mapPosition.z) + 1 - rayPosition.z) * deltaDistance.z
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run digital differential analysis (3DDDA)
|
||||||
|
var side: Int
|
||||||
|
while true {
|
||||||
|
if sideDistance.x < sideDistance.y {
|
||||||
|
if sideDistance.x < sideDistance.z {
|
||||||
|
sideDistance.x += deltaDistance.x
|
||||||
|
mapPosition.x += step.x
|
||||||
|
side = 0b100
|
||||||
|
} else {
|
||||||
|
sideDistance.z += deltaDistance.z
|
||||||
|
mapPosition.z += step.z
|
||||||
|
side = 0b001
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sideDistance.y < sideDistance.z {
|
||||||
|
sideDistance.y += deltaDistance.y
|
||||||
|
mapPosition.y += step.y
|
||||||
|
side = 0b010
|
||||||
|
} else {
|
||||||
|
sideDistance.z += deltaDistance.z
|
||||||
|
mapPosition.z += step.z
|
||||||
|
side = 0b001
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var distance: Float = if side == 0b100 {
|
||||||
|
abs(Float(mapPosition.x) - rayPosition.x + Float(1 - step.x) / 2) / direction.x
|
||||||
|
} else if side == 0b010 {
|
||||||
|
abs(Float(mapPosition.y) - rayPosition.y + Float(1 - step.y) / 2) / direction.y
|
||||||
|
} else {
|
||||||
|
abs(Float(mapPosition.z) - rayPosition.z + Float(1 - step.z) / 2) / direction.z
|
||||||
|
}
|
||||||
|
distance = abs(distance)
|
||||||
|
|
||||||
|
if distance > maxDistance {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if chunk.getBlock(at: mapPosition).type != .air {
|
||||||
|
return .init(
|
||||||
|
position: rayPosition + direction * distance,
|
||||||
|
distance: distance,
|
||||||
|
map: mapPosition,
|
||||||
|
normal: .zero)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user