Skip to content

Instantly share code, notes, and snippets.

@mike-neko
Last active September 22, 2022 12:00
Show Gist options
  • Save mike-neko/4f1d6609195d5fc5f1d043f486cec6f2 to your computer and use it in GitHub Desktop.
Save mike-neko/4f1d6609195d5fc5f1d043f486cec6f2 to your computer and use it in GitHub Desktop.
Model I/Oでモデルをロードする
import MetalKit
struct FrameUniforms {
var projectionViewMatrinx: matrix_float4x4
var normalMatrinx: matrix_float3x3
}
class ViewController: NSViewController, MTKViewDelegate {
private let defaultCameraMatrix = Matrix.lookAt(eye: float3(0, 2, 6), center: float3(), up: float3(0, 1, 0))
@IBOutlet private weak var mtkView: MTKView!
private var device: MTLDevice!
private var commandQueue: MTLCommandQueue!
private var library: MTLLibrary!
private var renderState: MTLRenderPipelineState!
private var depthStencilState: MTLDepthStencilState!
private var frameUniformBuffer: MTLBuffer!
private let semaphore = DispatchSemaphore(value: 1)
private var mesh: MTKMesh!
private var modelMatrix = matrix_identity_float4x4
// MARK: -
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupMetal()
loadModel()
mtkView.draw()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
// MARK: -
private func setupMetal() {
device = MTLCreateSystemDefaultDevice()!
commandQueue = device.makeCommandQueue()
library = device.newDefaultLibrary()!
mtkView.sampleCount = 4
mtkView.depthStencilPixelFormat = .depth32Float_stencil8
mtkView.colorPixelFormat = .bgra8Unorm
mtkView.clearColor = MTLClearColorMake(0.99, 0.99, 0.99, 1)
mtkView.device = device
mtkView.delegate = self
let depthDescriptor = MTLDepthStencilDescriptor()
depthDescriptor.depthCompareFunction = .less
depthDescriptor.isDepthWriteEnabled = true
depthStencilState = device.makeDepthStencilState(descriptor: depthDescriptor)
frameUniformBuffer = device.makeBuffer(length: MemoryLayout<FrameUniforms>.size, options: [])
}
private func loadModel() {
let allocator = MTKMeshBufferAllocator(device: device)
let mtlVertex = MTLVertexDescriptor()
mtlVertex.attributes[0].format = .float3
mtlVertex.attributes[0].offset = 0
mtlVertex.attributes[0].bufferIndex = 0
mtlVertex.attributes[1].format = .float3
mtlVertex.attributes[1].offset = 12
mtlVertex.attributes[1].bufferIndex = 0
mtlVertex.attributes[2].format = .float2
mtlVertex.attributes[2].offset = 24
mtlVertex.attributes[2].bufferIndex = 0
mtlVertex.layouts[0].stride = 32
mtlVertex.layouts[0].stepRate = 1
let modelDescriptor = MTKModelIOVertexDescriptorFromMetal(mtlVertex)
(modelDescriptor.attributes[0] as! MDLVertexAttribute).name = MDLVertexAttributePosition
(modelDescriptor.attributes[1] as! MDLVertexAttribute).name = MDLVertexAttributeNormal
(modelDescriptor.attributes[2] as! MDLVertexAttribute).name = MDLVertexAttributeTextureCoordinate
let asset = MDLAsset(url: Bundle.main.url(forResource: "teapot", withExtension: "obj")!,
vertexDescriptor: modelDescriptor,
bufferAllocator: allocator)
// 0決め打ち
var mdlArray: NSArray?
mesh = try! MTKMesh.newMeshes(from: asset, device: device, sourceMeshes: &mdlArray).first!
let mdl = mdlArray![0] as! MDLMesh
// 法線追加したい場合
// mdl.addNormals(withAttributeNamed: MDLVertexAttributeNormal, creaseThreshold: 1)
// 位置をだいたい真ん中に補正したい時用
// let diff = mdl.boundingBox.maxBounds - mdl.boundingBox.minBounds
// let scale = 1.0 / max(diff.x, max(diff.y, diff.z))
// let center = (mdl.boundingBox.maxBounds + mdl.boundingBox.minBounds) / vector_float3(2)
// modelMatrix = matrix_multiply(Matrix.scale(x: scale, y: scale, z: scale),
// Matrix.translation(x: -center.x, y: -center.y, z: -center.z))
let vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mesh.vertexDescriptor)
let renderDescriptor = MTLRenderPipelineDescriptor()
renderDescriptor.vertexDescriptor = vertexDescriptor
renderDescriptor.sampleCount = mtkView.sampleCount
renderDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat
renderDescriptor.vertexFunction = library.makeFunction(name: "lambertVertex")
renderDescriptor.fragmentFunction = library.makeFunction(name: "lambertFragment")
renderDescriptor.depthAttachmentPixelFormat = mtkView.depthStencilPixelFormat
renderDescriptor.stencilAttachmentPixelFormat = mtkView.depthStencilPixelFormat
renderState = try! device.makeRenderPipelineState(descriptor: renderDescriptor)
}
// MARK: - MTKViewDelegate
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
func draw(in view: MTKView) {
autoreleasepool {
semaphore.wait()
let commandBuffer = commandQueue.makeCommandBuffer()
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: view.currentRenderPassDescriptor!)
renderEncoder.pushDebugGroup("Render Object")
renderEncoder.setRenderPipelineState(renderState)
renderEncoder.setDepthStencilState(depthStencilState)
updateFramUniforms()
// 0決め打ち
renderEncoder.setVertexBuffer(mesh.vertexBuffers[0].buffer, offset: 0, at: 0)
renderEncoder.setVertexBuffer(frameUniformBuffer, offset: 0, at: 1)
mesh.submeshes.forEach {
renderEncoder.drawIndexedPrimitives(type: $0.primitiveType,
indexCount: $0.indexCount,
indexType: $0.indexType,
indexBuffer: $0.indexBuffer.buffer,
indexBufferOffset: $0.indexBuffer.offset)
}
renderEncoder.popDebugGroup()
renderEncoder.endEncoding()
commandBuffer.present(view.currentDrawable!)
commandBuffer.addCompletedHandler { _ in
self.semaphore.signal()
}
commandBuffer.commit()
}
}
private func updateFramUniforms() {
let p = frameUniformBuffer.contents().assumingMemoryBound(to: FrameUniforms.self)
let cameraMatrix = Matrix.lookAt(eye: float3(0, 2, 4), center: float3(), up: float3(0, 1, 0))
let projectionMatrix = Matrix.perspective(fovyRadians: radians(fromDegrees: 75),
aspect: Float(mtkView.drawableSize.width / mtkView.drawableSize.height),
nearZ: 0.1,
farZ: 100)
let viewModelMatrix = matrix_multiply(cameraMatrix, modelMatrix)
p.pointee.projectionViewMatrinx = matrix_multiply(projectionMatrix, viewModelMatrix)
let mat3 = Matrix.toUpperLeft3x3(from4x4: viewModelMatrix)
p.pointee.normalMatrinx = matrix_invert(matrix_transpose(mat3))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment