mirror of
https://github.com/GayPizzaSpecifications/voxelotl-engine.git
synced 2025-08-03 13:11:33 +00:00
multiple chunks
This commit is contained in:
@ -45,6 +45,7 @@ add_executable(Voxelotl MACOSX_BUNDLE
|
|||||||
|
|
||||||
# Game logic classes
|
# Game logic classes
|
||||||
Chunk.swift
|
Chunk.swift
|
||||||
|
World.swift
|
||||||
Raycast.swift
|
Raycast.swift
|
||||||
Player.swift
|
Player.swift
|
||||||
Game.swift
|
Game.swift
|
||||||
@ -59,6 +60,7 @@ set_source_files_properties(
|
|||||||
|
|
||||||
target_include_directories(Voxelotl PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
|
target_include_directories(Voxelotl PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
target_link_libraries(Voxelotl PRIVATE SDLSwift)
|
target_link_libraries(Voxelotl PRIVATE SDLSwift)
|
||||||
|
target_compile_definitions(Voxelotl PRIVATE $<$<CONFIG:Debug>:DEBUG>)
|
||||||
set_target_properties(Voxelotl PROPERTIES
|
set_target_properties(Voxelotl PROPERTIES
|
||||||
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES
|
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES
|
||||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "gay.pizza.voxelotl"
|
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "gay.pizza.voxelotl"
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
public struct Chunk {
|
public struct Chunk {
|
||||||
public static let chunkSize: Int = 16
|
public static let shift = 4 // 16
|
||||||
public static let blockCount = chunkSize * chunkSize * chunkSize
|
public static let size: Int = 1 << shift
|
||||||
|
public static let mask: Int = size - 1
|
||||||
|
|
||||||
private static let yStride = chunkSize
|
public static let blockCount = size * size * size
|
||||||
private static let zStride = chunkSize * chunkSize
|
|
||||||
|
private static let yStride = size
|
||||||
|
private static let zStride = size * size
|
||||||
|
|
||||||
public let position: SIMD3<Int>
|
public let origin: 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)
|
assert(blocks.count == Self.blockCount)
|
||||||
self.position = position
|
self.origin = position
|
||||||
self.blocks = blocks
|
self.blocks = blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
init(position: SIMD3<Int>) {
|
init(position: SIMD3<Int>) {
|
||||||
self.position = position
|
self.origin = position
|
||||||
self.blocks = Array(
|
self.blocks = Array(
|
||||||
repeating: BlockType.air,
|
repeating: BlockType.air,
|
||||||
count: Self.blockCount
|
count: Self.blockCount
|
||||||
@ -23,47 +26,40 @@ public struct Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getBlock(at position: SIMD3<Int>) -> Block {
|
func getBlock(at position: SIMD3<Int>) -> Block {
|
||||||
|
getBlock(internal: position &- self.origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBlock(internal position: SIMD3<Int>) -> Block {
|
||||||
if position.x < 0 || position.y < 0 || position.z < 0 {
|
if position.x < 0 || position.y < 0 || position.z < 0 {
|
||||||
Block(.air)
|
Block(.air)
|
||||||
} else if position.x >= Self.chunkSize || position.y >= Self.chunkSize || position.z >= Self.chunkSize {
|
} else if position.x >= Self.size || position.y >= Self.size || position.z >= Self.size {
|
||||||
Block(.air)
|
Block(.air)
|
||||||
} else {
|
} else {
|
||||||
blocks[position.x + position.y * Self.yStride + position.z * Self.zStride]
|
blocks[position.x + position.y * Self.yStride + position.z * Self.zStride]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func setBlock(at position: SIMD3<Int>, type: BlockType) {
|
mutating func setBlock(at position: SIMD3<Int>, type: BlockType) {
|
||||||
|
setBlock(internal: position &- self.origin, type: type)
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func setBlock(internal position: SIMD3<Int>, type: BlockType) {
|
||||||
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 {
|
if position.x >= Self.size || position.y >= Self.size || position.z >= Self.size {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks[position.x + position.y * Self.yStride + position.z * Self.zStride].type = type
|
blocks[position.x + position.y * Self.yStride + position.z * Self.zStride].type = type
|
||||||
}
|
}
|
||||||
|
|
||||||
mutating func fill(allBy calculation: (_ position: SIMD3<Int>) -> BlockType) {
|
mutating func fill(allBy calculation: (_ position: SIMD3<Int>) -> BlockType) {
|
||||||
var i = 0
|
for i in 0..<Self.blockCount {
|
||||||
for x in 0..<Self.chunkSize {
|
let x = i & Self.mask
|
||||||
for y in 0..<Self.chunkSize {
|
let y = (i &>> Self.shift) & Self.mask
|
||||||
for z in 0..<Self.chunkSize {
|
let z = (i &>> (Self.shift + Self.shift)) & Self.mask
|
||||||
blocks[i].type = calculation(SIMD3(x, y, z))
|
blocks[i].type = calculation(self.origin &+ SIMD3(x, y, z))
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,12 +71,12 @@ public struct Chunk {
|
|||||||
|
|
||||||
var position = SIMD3<Int>()
|
var position = SIMD3<Int>()
|
||||||
for i in self.blocks.indices {
|
for i in self.blocks.indices {
|
||||||
out.append(try transform(blocks[i], position))
|
out.append(try transform(blocks[i], self.origin &+ position))
|
||||||
position.x += 1
|
position.x += 1
|
||||||
if position.x == Self.chunkSize {
|
if position.x == Self.size {
|
||||||
position.x = 0
|
position.x = 0
|
||||||
position.y += 1
|
position.y += 1
|
||||||
if position.y == Self.chunkSize {
|
if position.y == Self.size {
|
||||||
position.y = 0
|
position.y = 0
|
||||||
position.z += 1
|
position.z += 1
|
||||||
}
|
}
|
||||||
@ -98,14 +94,14 @@ public struct Chunk {
|
|||||||
|
|
||||||
var position = SIMD3<Int>()
|
var position = SIMD3<Int>()
|
||||||
for i in self.blocks.indices {
|
for i in self.blocks.indices {
|
||||||
if let element = try transform(blocks[i], position) {
|
if let element = try transform(blocks[i], self.origin &+ position) {
|
||||||
out.append(element)
|
out.append(element)
|
||||||
}
|
}
|
||||||
position.x += 1
|
position.x += 1
|
||||||
if position.x == Self.chunkSize {
|
if position.x == Self.size {
|
||||||
position.x = 0
|
position.x = 0
|
||||||
position.y += 1
|
position.y += 1
|
||||||
if position.y == Self.chunkSize {
|
if position.y == Self.size {
|
||||||
position.y = 0
|
position.y = 0
|
||||||
position.z += 1
|
position.z += 1
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ 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 chunk = Chunk(position: .zero)
|
var world = World()
|
||||||
|
|
||||||
func create(_ renderer: Renderer) {
|
func create(_ renderer: Renderer) {
|
||||||
self.resetPlayer()
|
self.resetPlayer()
|
||||||
@ -34,7 +34,7 @@ class Game: GameDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func resetPlayer() {
|
private func resetPlayer() {
|
||||||
self.player.position = .init(repeating: 0.5) + .init(0, Float(Chunk.chunkSize), 0)
|
self.player.position = .init(repeating: 0.5) + .init(0, Float(Chunk.size), 0)
|
||||||
self.player.velocity = .zero
|
self.player.velocity = .zero
|
||||||
self.player.rotation = .init(.pi, 0)
|
self.player.rotation = .init(.pi, 0)
|
||||||
}
|
}
|
||||||
@ -50,20 +50,11 @@ class Game: GameDelegate {
|
|||||||
UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32,
|
UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32,
|
||||||
UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32))
|
UInt64(Arc4Random.instance.next()) | UInt64(Arc4Random.instance.next()) << 32))
|
||||||
#endif
|
#endif
|
||||||
let noise = ImprovedPerlin<Float>(random: &random)
|
#if DEBUG
|
||||||
self.chunk.fill(allBy: { position in
|
self.world.generate(width: 2, height: 1, depth: 1, random: &random)
|
||||||
let fpos = SIMD3<Float>(position)
|
#else
|
||||||
return if fpos.y / Float(Chunk.chunkSize)
|
self.world.generate(width: 5, height: 3, depth: 5, random: &random)
|
||||||
+ noise.get(fpos * 0.07) * 0.7
|
#endif
|
||||||
+ noise.get(fpos * 0.321 + 100) * 0.3 < 0.6 {
|
|
||||||
.solid(.init(
|
|
||||||
r: Float16(noise.get(fpos * 0.1)),
|
|
||||||
g: Float16(noise.get(fpos * 0.1 + 10)),
|
|
||||||
b: Float16(noise.get(fpos * 0.1 + 100))).mix(.white, 0.4).linear)
|
|
||||||
} else {
|
|
||||||
.air
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixedUpdate(_ time: GameTime) {
|
func fixedUpdate(_ time: GameTime) {
|
||||||
@ -77,27 +68,24 @@ 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
|
var reset = false, generate = false
|
||||||
if let pad = GameController.current?.state {
|
if let pad = GameController.current?.state {
|
||||||
// Player reset
|
if pad.pressed(.back) { reset = true }
|
||||||
if pad.pressed(.back) {
|
if pad.pressed(.start) { generate = true }
|
||||||
self.resetPlayer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regenerate
|
|
||||||
if pad.pressed(.start) {
|
|
||||||
self.generateWorld()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if Keyboard.pressed(.r) { reset = true }
|
||||||
|
if Keyboard.pressed(.g) { generate = true }
|
||||||
|
|
||||||
if Keyboard.pressed(.r) {
|
// Player reset
|
||||||
|
if reset {
|
||||||
self.resetPlayer()
|
self.resetPlayer()
|
||||||
}
|
}
|
||||||
if Keyboard.pressed(.g) {
|
// Regenerate
|
||||||
|
if generate {
|
||||||
self.generateWorld()
|
self.generateWorld()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.player.update(deltaTime: deltaTime, chunk: &chunk)
|
self.player.update(deltaTime: deltaTime, world: world)
|
||||||
self.camera.position = player.eyePosition
|
self.camera.position = player.eyePosition
|
||||||
self.camera.rotation = player.eyeRotation
|
self.camera.rotation = player.eyeRotation
|
||||||
}
|
}
|
||||||
@ -114,14 +102,7 @@ class Game: GameDelegate {
|
|||||||
specular: Color(rgba8888: 0x2F2F2F00).linear,
|
specular: Color(rgba8888: 0x2F2F2F00).linear,
|
||||||
gloss: 75)
|
gloss: 75)
|
||||||
|
|
||||||
var instances = chunk.compactMap { block, position in
|
var instances = world.instances
|
||||||
if case let .solid(color) = block.type {
|
|
||||||
Instance(
|
|
||||||
position: SIMD3<Float>(chunk.position &+ position) + 0.5,
|
|
||||||
scale: .init(repeating: 0.5),
|
|
||||||
color: color)
|
|
||||||
} else { nil }
|
|
||||||
}
|
|
||||||
instances.append(
|
instances.append(
|
||||||
Instance(
|
Instance(
|
||||||
position: player.rayhitPos,
|
position: player.rayhitPos,
|
||||||
|
@ -8,7 +8,7 @@ struct Player {
|
|||||||
to: .init(Self.radius, Self.height, Self.radius))
|
to: .init(Self.radius, Self.height, Self.radius))
|
||||||
|
|
||||||
static let eyeLevel: Float = 1.4
|
static let eyeLevel: Float = 1.4
|
||||||
static let epsilon = Float.ulpOfOne * 10
|
static let epsilon = Float.ulpOfOne * 20
|
||||||
|
|
||||||
static let accelerationCoeff: Float = 75
|
static let accelerationCoeff: Float = 75
|
||||||
static let airAccelCoeff: Float = 3
|
static let airAccelCoeff: Float = 3
|
||||||
@ -40,7 +40,7 @@ struct Player {
|
|||||||
|
|
||||||
enum JumpInput { case off, press, held }
|
enum JumpInput { case off, press, held }
|
||||||
|
|
||||||
mutating func update(deltaTime: Float, chunk: inout Chunk) {
|
mutating func update(deltaTime: Float, world: World) {
|
||||||
var turning: SIMD2<Float> = .zero
|
var turning: SIMD2<Float> = .zero
|
||||||
var movement: SIMD2<Float> = .zero
|
var movement: SIMD2<Float> = .zero
|
||||||
var flying: Int = .zero
|
var flying: Int = .zero
|
||||||
@ -133,9 +133,9 @@ struct Player {
|
|||||||
self._velocity.y -= Self.gravityCoeff * deltaTime
|
self._velocity.y -= Self.gravityCoeff * deltaTime
|
||||||
|
|
||||||
// Move & handle collision
|
// Move & handle collision
|
||||||
let checkCorner = { (chunk: Chunk, bounds: AABB, corner: SIMD3<Float>) -> Optional<AABB> in
|
let checkCorner = { (world: World, bounds: AABB, corner: SIMD3<Float>) -> Optional<AABB> in
|
||||||
let blockPos = SIMD3(floor(corner.x), floor(corner.y), floor(corner.z))
|
let blockPos = SIMD3(floor(corner.x), floor(corner.y), floor(corner.z))
|
||||||
if case BlockType.solid = chunk.getBlock(at: SIMD3<Int>(blockPos)).type {
|
if case BlockType.solid = world.getBlock(at: SIMD3<Int>(blockPos)).type {
|
||||||
let blockGeometry = AABB(from: blockPos, to: blockPos + 1)
|
let blockGeometry = AABB(from: blockPos, to: blockPos + 1)
|
||||||
if bounds.touching(blockGeometry) {
|
if bounds.touching(blockGeometry) {
|
||||||
return blockGeometry
|
return blockGeometry
|
||||||
@ -143,7 +143,7 @@ struct Player {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let checkCollision = { (chunk: Chunk, position: SIMD3<Float>) -> Optional<AABB> in
|
let checkCollision = { (world: World, position: SIMD3<Float>) -> Optional<AABB> in
|
||||||
let bounds = Self.bounds + position
|
let bounds = Self.bounds + position
|
||||||
let corners: [SIMD3<Float>] = [
|
let corners: [SIMD3<Float>] = [
|
||||||
.init(bounds.left, bounds.bottom, bounds.far),
|
.init(bounds.left, bounds.bottom, bounds.far),
|
||||||
@ -160,14 +160,14 @@ struct Player {
|
|||||||
.init(bounds.right, bounds.top, bounds.near)
|
.init(bounds.right, bounds.top, bounds.near)
|
||||||
]
|
]
|
||||||
for corner in corners {
|
for corner in corners {
|
||||||
if let geometry = checkCorner(chunk, bounds, corner) {
|
if let geometry = checkCorner(world, bounds, corner) {
|
||||||
return geometry
|
return geometry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
self._position.y += self._velocity.y * deltaTime
|
self._position.y += self._velocity.y * deltaTime
|
||||||
if let aabb = checkCollision(chunk, self._velocity.y > 0 ? self._position + .down * Self.epsilon : self.position) {
|
if let aabb = checkCollision(world, self._velocity.y > 0 ? self._position + .down * Self.epsilon : self.position) {
|
||||||
if self._velocity.y < 0 {
|
if self._velocity.y < 0 {
|
||||||
self._position.y = aabb.top + Self.epsilon
|
self._position.y = aabb.top + Self.epsilon
|
||||||
self._onGround = true
|
self._onGround = true
|
||||||
@ -180,7 +180,7 @@ struct Player {
|
|||||||
self._onGround = false
|
self._onGround = false
|
||||||
}
|
}
|
||||||
self._position.x += self._velocity.x * deltaTime
|
self._position.x += self._velocity.x * deltaTime
|
||||||
if let aabb = checkCollision(chunk, self._position) {
|
if let aabb = checkCollision(world, self._position) {
|
||||||
if self._velocity.x < 0 {
|
if self._velocity.x < 0 {
|
||||||
self._position.x = aabb.right + Self.radius + Self.epsilon
|
self._position.x = aabb.right + Self.radius + Self.epsilon
|
||||||
} else {
|
} else {
|
||||||
@ -189,7 +189,7 @@ struct Player {
|
|||||||
self._velocity.x = 0
|
self._velocity.x = 0
|
||||||
}
|
}
|
||||||
self._position.z += self._velocity.z * deltaTime
|
self._position.z += self._velocity.z * deltaTime
|
||||||
if let aabb = checkCollision(chunk, self._position) {
|
if let aabb = checkCollision(world, self._position) {
|
||||||
if self._velocity.z < 0 {
|
if self._velocity.z < 0 {
|
||||||
self._position.z = aabb.near + Self.radius + Self.epsilon
|
self._position.z = aabb.near + Self.radius + Self.epsilon
|
||||||
} else {
|
} else {
|
||||||
@ -200,16 +200,16 @@ struct Player {
|
|||||||
|
|
||||||
// Block picking
|
// Block picking
|
||||||
if let hit = raycast(
|
if let hit = raycast(
|
||||||
chunk: chunk,
|
world: world,
|
||||||
origin: self.eyePosition,
|
origin: self.eyePosition,
|
||||||
direction: .forward * simd_matrix3x3(self.eyeRotation),
|
direction: .forward * simd_matrix3x3(self.eyeRotation),
|
||||||
maxDistance: 3.8
|
maxDistance: 3.8
|
||||||
) {
|
) {
|
||||||
self.rayhitPos = hit.position
|
self.rayhitPos = hit.position
|
||||||
if destroy {
|
if destroy {
|
||||||
chunk.setBlock(at: hit.map, type: .air)
|
world.setBlock(at: hit.map, type: .air)
|
||||||
} else if place {
|
} else if place {
|
||||||
chunk.setBlock(at: hit.map.offset(by: hit.side), type: .solid(.white))
|
world.setBlock(at: hit.map.offset(by: hit.side), type: .solid(.white))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import simd
|
import simd
|
||||||
|
|
||||||
public func raycast(
|
public func raycast(
|
||||||
chunk: Chunk,
|
world: World,
|
||||||
origin rayPosition: SIMD3<Float>,
|
origin rayPosition: SIMD3<Float>,
|
||||||
direction: SIMD3<Float>,
|
direction: SIMD3<Float>,
|
||||||
maxDistance: Float
|
maxDistance: Float
|
||||||
@ -74,7 +74,7 @@ public func raycast(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return a result if we hit something solid
|
// return a result if we hit something solid
|
||||||
if chunk.getBlock(at: mapPosition).type != .air {
|
if world.getBlock(at: mapPosition).type != .air {
|
||||||
return .init(
|
return .init(
|
||||||
position: rayPosition + direction * distance,
|
position: rayPosition + direction * distance,
|
||||||
distance: distance,
|
distance: distance,
|
||||||
|
61
Sources/Voxelotl/World.swift
Normal file
61
Sources/Voxelotl/World.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
public class World {
|
||||||
|
private var _chunks: Dictionary<SIMD3<Int>, Chunk>
|
||||||
|
|
||||||
|
public init() {
|
||||||
|
self._chunks = [:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func getBlock(at position: SIMD3<Int>) -> Block {
|
||||||
|
return if let chunk = self._chunks[position &>> Chunk.shift] {
|
||||||
|
chunk.getBlock(at: position)
|
||||||
|
} else { Block(.air) }
|
||||||
|
}
|
||||||
|
|
||||||
|
func setBlock(at position: SIMD3<Int>, type: BlockType) {
|
||||||
|
self._chunks[position &>> Chunk.shift]?.setBlock(at: position, type: type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func generate(width: Int, height: Int, depth: Int, random: inout any RandomProvider) {
|
||||||
|
let noise = ImprovedPerlin<Float>(random: &random)
|
||||||
|
|
||||||
|
for x in 0..<width {
|
||||||
|
for y in 0..<height {
|
||||||
|
for z in 0..<depth {
|
||||||
|
let chunkID = SIMD3(x, y, z) &- SIMD3(width, height, depth) / 2
|
||||||
|
let chunkOrigin = chunkID &<< Chunk.shift
|
||||||
|
var chunk = Chunk(position: chunkOrigin)
|
||||||
|
chunk.fill(allBy: { position in
|
||||||
|
let fpos = SIMD3<Float>(position)
|
||||||
|
return if fpos.y / Float(Chunk.size)
|
||||||
|
+ noise.get(fpos * 0.05) * 1.1
|
||||||
|
+ noise.get(fpos * 0.1 + 500) * 0.5
|
||||||
|
+ noise.get(fpos * 0.3 + 100) * 0.23 < 0.6 {
|
||||||
|
.solid(.init(
|
||||||
|
r: Float16(noise.get(fpos * 0.1)),
|
||||||
|
g: Float16(noise.get(fpos * 0.1 + 10)),
|
||||||
|
b: Float16(noise.get(fpos * 0.1 + 100))).mix(.white, 0.6).linear)
|
||||||
|
} else {
|
||||||
|
.air
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self._chunks[chunkID] = chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var instances: [Instance] {
|
||||||
|
self._chunks.values.flatMap { chunk in
|
||||||
|
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)
|
||||||
|
} else { nil }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user