mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 02:59:37 +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