Last active
September 22, 2022 12:00
-
-
Save mike-neko/4f1d6609195d5fc5f1d043f486cec6f2 to your computer and use it in GitHub Desktop.
Model I/Oでモデルをロードする
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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