mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 02:59:37 +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
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public let position: SIMD3<Int>
 | 
					  private static let yStride = size
 | 
				
			||||||
 | 
					  private static let zStride = size * size
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  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,9 +26,13 @@ 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]
 | 
				
			||||||
@ -33,10 +40,14 @@ public struct Chunk {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -44,26 +55,11 @@ public struct Chunk {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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 {
 | 
				
			||||||
 | 
					      if pad.pressed(.back) { reset = true }
 | 
				
			||||||
 | 
					      if pad.pressed(.start) { generate = true }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if Keyboard.pressed(.r) { reset = true }
 | 
				
			||||||
 | 
					    if Keyboard.pressed(.g) { generate = true }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Player reset
 | 
					    // Player reset
 | 
				
			||||||
      if pad.pressed(.back) {
 | 
					    if reset {
 | 
				
			||||||
      self.resetPlayer()
 | 
					      self.resetPlayer()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Regenerate
 | 
					    // Regenerate
 | 
				
			||||||
      if pad.pressed(.start) {
 | 
					    if generate {
 | 
				
			||||||
        self.generateWorld()
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if Keyboard.pressed(.r) {
 | 
					 | 
				
			||||||
      self.resetPlayer()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if Keyboard.pressed(.g) {
 | 
					 | 
				
			||||||
      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