mirror of
				https://github.com/GayPizzaSpecifications/voxelotl-engine.git
				synced 2025-11-04 10:59:39 +00:00 
			
		
		
		
	depth buffer implementation
This commit is contained in:
		@ -36,26 +36,25 @@ public class Application {
 | 
				
			|||||||
      return .exitFailure
 | 
					      return .exitFailure
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create Metal renderer
 | 
					 | 
				
			||||||
    view = SDL_Metal_CreateView(window)
 | 
					 | 
				
			||||||
    do {
 | 
					 | 
				
			||||||
      let layer = unsafeBitCast(SDL_Metal_GetLayer(view), to: CAMetalLayer.self)
 | 
					 | 
				
			||||||
      layer.displaySyncEnabled = cfg.vsyncMode == .off ? false : true
 | 
					 | 
				
			||||||
      self.renderer = try Renderer(layer: layer)
 | 
					 | 
				
			||||||
    } catch RendererError.initFailure(let message) {
 | 
					 | 
				
			||||||
      printErr("Renderer init error: \(message)")
 | 
					 | 
				
			||||||
      return .exitFailure
 | 
					 | 
				
			||||||
    } catch {
 | 
					 | 
				
			||||||
      printErr("Renderer init error: unexpected error")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Get window metrics
 | 
					    // Get window metrics
 | 
				
			||||||
    var backBufferWidth: Int32 = 0, backBufferHeight: Int32 = 0
 | 
					    var backBufferWidth: Int32 = 0, backBufferHeight: Int32 = 0
 | 
				
			||||||
    guard SDL_GetWindowSizeInPixels(window, &backBufferWidth, &backBufferHeight) >= 0 else {
 | 
					    guard SDL_GetWindowSizeInPixels(window, &backBufferWidth, &backBufferHeight) >= 0 else {
 | 
				
			||||||
      printErr("SDL_GetWindowSizeInPixels() error: \(String(cString: SDL_GetError()))")
 | 
					      printErr("SDL_GetWindowSizeInPixels() error: \(String(cString: SDL_GetError()))")
 | 
				
			||||||
      return .exitFailure
 | 
					      return .exitFailure
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    renderer!.resize(size: SIMD2<Int>(Int(backBufferWidth), Int(backBufferHeight)))
 | 
					
 | 
				
			||||||
 | 
					    // Create Metal renderer
 | 
				
			||||||
 | 
					    view = SDL_Metal_CreateView(window)
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					      let layer = unsafeBitCast(SDL_Metal_GetLayer(view), to: CAMetalLayer.self)
 | 
				
			||||||
 | 
					      layer.displaySyncEnabled = cfg.vsyncMode == .off ? false : true
 | 
				
			||||||
 | 
					      self.renderer = try Renderer(layer: layer, size: SIMD2<Int>(Int(backBufferWidth), Int(backBufferHeight)))
 | 
				
			||||||
 | 
					    } catch RendererError.initFailure(let message) {
 | 
				
			||||||
 | 
					      printErr("Renderer init error: \(message)")
 | 
				
			||||||
 | 
					      return .exitFailure
 | 
				
			||||||
 | 
					    } catch {
 | 
				
			||||||
 | 
					      printErr("Renderer init error: unexpected error")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    lastCounter = SDL_GetPerformanceCounter()
 | 
					    lastCounter = SDL_GetPerformanceCounter()
 | 
				
			||||||
    return .running
 | 
					    return .running
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,7 @@
 | 
				
			|||||||
public extension FloatingPoint {
 | 
					public extension FloatingPoint {
 | 
				
			||||||
  @inline(__always) var degrees: Self { self * (180 / Self.pi) }
 | 
					  @inline(__always) var degrees: Self { self * (180 / Self.pi) }
 | 
				
			||||||
  @inline(__always) var radians: Self { self * (Self.pi / 180) }
 | 
					  @inline(__always) var radians: Self { self * (Self.pi / 180) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @inline(__always) func lerp(_ a: Self, _ b: Self) -> Self { b * self + a * (1 - self) }
 | 
				
			||||||
 | 
					  @inline(__always) func mlerp(_ a: Self, _ b: Self) -> Self { a + (b - a) * self }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -43,18 +43,36 @@ public extension simd_float4x4 {
 | 
				
			|||||||
      .init(0,  0, 0, 1))
 | 
					      .init(0,  0, 0, 1))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static func perspective(verticalFov: T, aspect: T, near: T, far: T) -> Self {
 | 
					  static func orthographic(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Self {
 | 
				
			||||||
    let h = 1 / tan(verticalFov * T(0.5))
 | 
					    let
 | 
				
			||||||
    let w = h / aspect
 | 
					      invWidth  = 1 / (right - left),
 | 
				
			||||||
 | 
					      invHeight = 1 / (top - bottom),
 | 
				
			||||||
    let invClipRange = 1 / (far - near)
 | 
					      invDepth  = 1 / (far - near)
 | 
				
			||||||
    let z = -(far + near) * invClipRange
 | 
					    let
 | 
				
			||||||
    let z2 = -(2 * far * near) * invClipRange
 | 
					      tx = -(right + left) * invWidth,
 | 
				
			||||||
 | 
					      ty = -(top + bottom) * invHeight,
 | 
				
			||||||
 | 
					      tz = -near * invDepth
 | 
				
			||||||
 | 
					    let x = 2 * invWidth, y = 2 * invHeight, z = invDepth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return .init(
 | 
					    return .init(
 | 
				
			||||||
      .init(w, 0,  0,  0),
 | 
					    .init( x,  0,  0, 0),
 | 
				
			||||||
      .init(0, h,  0,  0),
 | 
					    .init( 0,  y,  0, 0),
 | 
				
			||||||
      .init(0, 0,  z, -1),
 | 
					    .init( 0,  0,  z, 0),
 | 
				
			||||||
      .init(0, 0, z2,  0))
 | 
					    .init(tx, ty, tz, 1))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static func perspective(verticalFov fovY: T, aspect: T, near: T, far: T) -> Self {
 | 
				
			||||||
 | 
					    let tanHalfFovY = tan(fovY * T(0.5))
 | 
				
			||||||
 | 
					    let invClipRange = 1 / (near - far)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let y = 1 / tanHalfFovY
 | 
				
			||||||
 | 
					    let x = y / aspect
 | 
				
			||||||
 | 
					    let z = far * invClipRange
 | 
				
			||||||
 | 
					    let w = near * z // (far * near) * invClipRange
 | 
				
			||||||
 | 
					    return .init(
 | 
				
			||||||
 | 
					      .init(x, 0, 0,  0),
 | 
				
			||||||
 | 
					      .init(0, y, 0,  0),
 | 
				
			||||||
 | 
					      .init(0, 0, z, -1),
 | 
				
			||||||
 | 
					      .init(0, 0, w,  0))
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -41,13 +41,18 @@ fileprivate let cubeIndices: [UInt16] = [
 | 
				
			|||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Renderer {
 | 
					class Renderer {
 | 
				
			||||||
 | 
					  private let depthFormat: MTLPixelFormat = .depth16Unorm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private var device: MTLDevice
 | 
					  private var device: MTLDevice
 | 
				
			||||||
  private var layer: CAMetalLayer
 | 
					  private var layer: CAMetalLayer
 | 
				
			||||||
  private var viewport = MTLViewport()
 | 
					  private var viewport = MTLViewport()
 | 
				
			||||||
 | 
					  private var aspectRatio: Float
 | 
				
			||||||
  private var queue: MTLCommandQueue
 | 
					  private var queue: MTLCommandQueue
 | 
				
			||||||
  private var lib: MTLLibrary
 | 
					  private var lib: MTLLibrary
 | 
				
			||||||
  private let passDescription = MTLRenderPassDescriptor()
 | 
					  private let passDescription = MTLRenderPassDescriptor()
 | 
				
			||||||
  private var pso: MTLRenderPipelineState
 | 
					  private var pso: MTLRenderPipelineState
 | 
				
			||||||
 | 
					  private var depthStencilState: MTLDepthStencilState
 | 
				
			||||||
 | 
					  private var depthStencilTexture: MTLTexture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private var vtxBuffer: MTLBuffer, idxBuffer: MTLBuffer
 | 
					  private var vtxBuffer: MTLBuffer, idxBuffer: MTLBuffer
 | 
				
			||||||
  private var defaultTexture: MTLTexture
 | 
					  private var defaultTexture: MTLTexture
 | 
				
			||||||
@ -62,7 +67,7 @@ class Renderer {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  init(layer metalLayer: CAMetalLayer) throws {
 | 
					  init(layer metalLayer: CAMetalLayer, size: SIMD2<Int>) throws {
 | 
				
			||||||
    self.layer = metalLayer
 | 
					    self.layer = metalLayer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Select best Metal device
 | 
					    // Select best Metal device
 | 
				
			||||||
@ -79,9 +84,36 @@ class Renderer {
 | 
				
			|||||||
      throw RendererError.initFailure("Failed to create command queue")
 | 
					      throw RendererError.initFailure("Failed to create command queue")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    self.queue = queue
 | 
					    self.queue = queue
 | 
				
			||||||
    passDescription.colorAttachments[0].loadAction  = MTLLoadAction.clear
 | 
					
 | 
				
			||||||
    passDescription.colorAttachments[0].storeAction = MTLStoreAction.store
 | 
					    self.viewport = MTLViewport(
 | 
				
			||||||
 | 
					      originX: 0.0,
 | 
				
			||||||
 | 
					      originY: 0.0,
 | 
				
			||||||
 | 
					      width:  Double(size.x),
 | 
				
			||||||
 | 
					      height: Double(size.y),
 | 
				
			||||||
 | 
					      znear: 1.0,
 | 
				
			||||||
 | 
					      zfar: -1.0)
 | 
				
			||||||
 | 
					    self.aspectRatio = Float(size.x) / Float(size.y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    passDescription.colorAttachments[0].loadAction  = .clear
 | 
				
			||||||
 | 
					    passDescription.colorAttachments[0].storeAction = .store
 | 
				
			||||||
    passDescription.colorAttachments[0].clearColor  = MTLClearColorMake(0.1, 0.1, 0.1, 1.0)
 | 
					    passDescription.colorAttachments[0].clearColor  = MTLClearColorMake(0.1, 0.1, 0.1, 1.0)
 | 
				
			||||||
 | 
					    passDescription.depthAttachment.loadAction  = .clear
 | 
				
			||||||
 | 
					    passDescription.depthAttachment.storeAction = .dontCare
 | 
				
			||||||
 | 
					    passDescription.depthAttachment.clearDepth  = 1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    guard let depthStencilTexture = Self.createDepthTexture(device, size, format: depthFormat) else {
 | 
				
			||||||
 | 
					      throw RendererError.initFailure("Failed to create depth buffer")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    self.depthStencilTexture = depthStencilTexture
 | 
				
			||||||
 | 
					    passDescription.depthAttachment.texture = self.depthStencilTexture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let stencilDepthDescription = MTLDepthStencilDescriptor()
 | 
				
			||||||
 | 
					    stencilDepthDescription.depthCompareFunction = .less  // OpenGL default
 | 
				
			||||||
 | 
					    stencilDepthDescription.isDepthWriteEnabled  = true
 | 
				
			||||||
 | 
					    guard let depthStencilState = device.makeDepthStencilState(descriptor: stencilDepthDescription) else {
 | 
				
			||||||
 | 
					      throw RendererError.initFailure("Failed to create depth stencil state")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    self.depthStencilState = depthStencilState
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Create shader library & grab functions
 | 
					    // Create shader library & grab functions
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
@ -97,6 +129,7 @@ class Renderer {
 | 
				
			|||||||
    pipeDescription.vertexFunction   = vertexProgram
 | 
					    pipeDescription.vertexFunction   = vertexProgram
 | 
				
			||||||
    pipeDescription.fragmentFunction = fragmentProgram
 | 
					    pipeDescription.fragmentFunction = fragmentProgram
 | 
				
			||||||
    pipeDescription.colorAttachments[0].pixelFormat = layer.pixelFormat
 | 
					    pipeDescription.colorAttachments[0].pixelFormat = layer.pixelFormat
 | 
				
			||||||
 | 
					    pipeDescription.depthAttachmentPixelFormat = depthFormat
 | 
				
			||||||
    do {
 | 
					    do {
 | 
				
			||||||
      self.pso = try device.makeRenderPipelineState(descriptor: pipeDescription)
 | 
					      self.pso = try device.makeRenderPipelineState(descriptor: pipeDescription)
 | 
				
			||||||
    } catch {
 | 
					    } catch {
 | 
				
			||||||
@ -207,7 +240,37 @@ class Renderer {
 | 
				
			|||||||
    return newTexture
 | 
					    return newTexture
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static func createDepthTexture(_ device: MTLDevice, _ size: SIMD2<Int>, format: MTLPixelFormat
 | 
				
			||||||
 | 
					  ) -> MTLTexture? {
 | 
				
			||||||
 | 
					    let texDescriptor = MTLTextureDescriptor.texture2DDescriptor(
 | 
				
			||||||
 | 
					      pixelFormat: format,
 | 
				
			||||||
 | 
					      width:       size.x,
 | 
				
			||||||
 | 
					      height:      size.y,
 | 
				
			||||||
 | 
					      mipmapped:   false)
 | 
				
			||||||
 | 
					    texDescriptor.depth = 1
 | 
				
			||||||
 | 
					    texDescriptor.sampleCount = 1
 | 
				
			||||||
 | 
					    texDescriptor.usage       = [ .renderTarget, .shaderRead ]
 | 
				
			||||||
 | 
					#if !NDEBUG
 | 
				
			||||||
 | 
					    texDescriptor.storageMode = .private
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    texDescriptor.storageMode = .memoryless
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    guard let depthStencilTexture = device.makeTexture(descriptor: texDescriptor) else { return nil }
 | 
				
			||||||
 | 
					    depthStencilTexture.label = "Depth buffer"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return depthStencilTexture
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  func resize(size: SIMD2<Int>) {
 | 
					  func resize(size: SIMD2<Int>) {
 | 
				
			||||||
 | 
					    if Int(self.viewport.width) != size.x || Int(self.viewport.height) != size.y {
 | 
				
			||||||
 | 
					      if let depthStencilTexture = Self.createDepthTexture(device, size, format: depthFormat) {
 | 
				
			||||||
 | 
					        self.depthStencilTexture = depthStencilTexture
 | 
				
			||||||
 | 
					        passDescription.depthAttachment.texture = self.depthStencilTexture
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self.aspectRatio = Float(size.x) / Float(size.y)
 | 
				
			||||||
    self.viewport = MTLViewport(
 | 
					    self.viewport = MTLViewport(
 | 
				
			||||||
      originX: 0.0,
 | 
					      originX: 0.0,
 | 
				
			||||||
      originY: 0.0,
 | 
					      originY: 0.0,
 | 
				
			||||||
@ -220,14 +283,21 @@ class Renderer {
 | 
				
			|||||||
  var time: Float = 0  //FIXME: temp
 | 
					  var time: Float = 0  //FIXME: temp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  func paint() throws {
 | 
					  func paint() throws {
 | 
				
			||||||
 | 
					#if true
 | 
				
			||||||
    let projection = matrix_float4x4.perspective(
 | 
					    let projection = matrix_float4x4.perspective(
 | 
				
			||||||
      verticalFov: Float(90.0).radians,
 | 
					      verticalFov: Float(90.0).radians,
 | 
				
			||||||
      aspect: Float(self.viewport.width / self.viewport.height),
 | 
					      aspect: aspectRatio,
 | 
				
			||||||
      near: 0.1,
 | 
					      near: 0.003,
 | 
				
			||||||
      far: 10)
 | 
					      far: 4)
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    let projection = matrix_float4x4.orthographic(
 | 
				
			||||||
 | 
					      left: -aspectRatio, right: aspectRatio,
 | 
				
			||||||
 | 
					      bottom: -1, top: 1,
 | 
				
			||||||
 | 
					      near: 0, far: -4)
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    let view = matrix_float4x4.identity
 | 
					    let view = matrix_float4x4.identity
 | 
				
			||||||
    let model: matrix_float4x4 =
 | 
					    let model: matrix_float4x4 =
 | 
				
			||||||
      .translate(.init(0, sin(time * 0.5) * 0.5, -2)) *
 | 
					      .translate(.init(0, sin(time * 0.5) * 0.75, -2)) *
 | 
				
			||||||
      .scale(0.5) *
 | 
					      .scale(0.5) *
 | 
				
			||||||
      .rotate(y: time)
 | 
					      .rotate(y: time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -248,9 +318,11 @@ class Renderer {
 | 
				
			|||||||
      throw RendererError.drawFailure("Failed to make render encoder from command buffer")
 | 
					      throw RendererError.drawFailure("Failed to make render encoder from command buffer")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    encoder.setCullMode(.back)
 | 
				
			||||||
 | 
					    encoder.setFrontFacing(.counterClockwise)  // OpenGL default
 | 
				
			||||||
    encoder.setViewport(viewport)
 | 
					    encoder.setViewport(viewport)
 | 
				
			||||||
    encoder.setCullMode(MTLCullMode.none)
 | 
					 | 
				
			||||||
    encoder.setRenderPipelineState(pso)
 | 
					    encoder.setRenderPipelineState(pso)
 | 
				
			||||||
 | 
					    encoder.setDepthStencilState(depthStencilState)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    encoder.setFragmentTexture(cubeTexture ?? defaultTexture, index: 0)
 | 
					    encoder.setFragmentTexture(cubeTexture ?? defaultTexture, index: 0)
 | 
				
			||||||
    encoder.setVertexBuffer(vtxBuffer,
 | 
					    encoder.setVertexBuffer(vtxBuffer,
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user