Get all the pieces together

Still doesn't render though. :\
This commit is contained in:
Eryn Wells 2018-11-03 14:01:34 -04:00
parent 8a664fff70
commit 67134c157b
5 changed files with 158 additions and 22 deletions

View file

@ -14,6 +14,7 @@
C0C15AA0218DE0B2007494E2 /* Renderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C15A9F218DE0B2007494E2 /* Renderer.swift */; };
C0C15AA2218DE21E007494E2 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C15AA1218DE21E007494E2 /* Math.swift */; };
C0C15AA4218DE615007494E2 /* Terrain.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C15AA3218DE615007494E2 /* Terrain.swift */; };
C0C15AA6218DF065007494E2 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = C0C15AA5218DF065007494E2 /* Shaders.metal */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -27,6 +28,7 @@
C0C15A9F218DE0B2007494E2 /* Renderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Renderer.swift; sourceTree = "<group>"; };
C0C15AA1218DE21E007494E2 /* Math.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Math.swift; sourceTree = "<group>"; };
C0C15AA3218DE615007494E2 /* Terrain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Terrain.swift; sourceTree = "<group>"; };
C0C15AA5218DF065007494E2 /* Shaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -64,6 +66,7 @@
C0C15A9F218DE0B2007494E2 /* Renderer.swift */,
C0C15AA3218DE615007494E2 /* Terrain.swift */,
C0C15AA1218DE21E007494E2 /* Math.swift */,
C0C15AA5218DF065007494E2 /* Shaders.metal */,
C0C15A8F218DDD87007494E2 /* Assets.xcassets */,
C0C15A91218DDD87007494E2 /* MainMenu.xib */,
C0C15A94218DDD87007494E2 /* Info.plist */,
@ -142,6 +145,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0C15AA6218DF065007494E2 /* Shaders.metal in Sources */,
C0C15A9D218DDDC3007494E2 /* TerrainViewController.swift in Sources */,
C0C15AA0218DE0B2007494E2 /* Renderer.swift in Sources */,
C0C15AA4218DE615007494E2 /* Terrain.swift in Sources */,

View file

@ -12,10 +12,67 @@ import MetalKit
class Renderer: NSObject, MTKViewDelegate {
let device: MTLDevice
var device: MTLDevice!
var library: MTLLibrary!
var commandQueue: MTLCommandQueue!
var renderPipeline: MTLRenderPipelineState!
init(device: MTLDevice) {
var terrainGridSize = CGSize(width: 11, height: 11)
var terrain = Terrain()
func setupMetal(withDevice device: MTLDevice, pixelFormat: MTLPixelFormat) {
self.device = device
guard let queue = device.makeCommandQueue() else {
fatalError("Unable to create Metal command queue")
}
queue.label = "Terrain"
self.commandQueue = queue
let bundle = Bundle(for: type(of: self))
guard let library = try? device.makeDefaultLibrary(bundle: bundle) else {
fatalError("Unable to create default Metal library")
}
self.library = library
setupRenderPipeline(withDevice: device, library: library, pixelFormat: pixelFormat)
}
func setupRenderPipeline(withDevice device: MTLDevice, library: MTLLibrary, pixelFormat: MTLPixelFormat) {
let vertexShader = library.makeFunction(name: "passthroughVertex")
let fragmentShader = library.makeFunction(name: "passthroughFragment")
let desc = MTLRenderPipelineDescriptor()
desc.label = "Pixel Pipeline"
desc.vertexFunction = vertexShader
desc.fragmentFunction = fragmentShader
if let renderAttachment = desc.colorAttachments[0] {
renderAttachment.pixelFormat = pixelFormat
// Pulled all this from SO. I don't know what it means, but it makes the alpha channel work.
// TODO: Learn what this means???
// https://stackoverflow.com/q/43727335/1174185
renderAttachment.isBlendingEnabled = true
renderAttachment.alphaBlendOperation = .add
renderAttachment.rgbBlendOperation = .add
renderAttachment.sourceRGBBlendFactor = .sourceAlpha
renderAttachment.sourceAlphaBlendFactor = .sourceAlpha
renderAttachment.destinationRGBBlendFactor = .oneMinusSourceAlpha
renderAttachment.destinationAlphaBlendFactor = .oneMinusSourceAlpha
}
do {
renderPipeline = try device.makeRenderPipelineState(descriptor: desc)
} catch let e {
print("Couldn't set up pixel pipeline! \(e)")
renderPipeline = nil
}
}
func prepareToRender() {
guard let buffer = device.makeBuffer(length: terrain.minimumBufferSize(forGridSize: terrainGridSize), options: .storageModeShared) else {
fatalError("Couldn't create terrain buffer")
}
terrain.generateVertexes(intoBuffer: buffer, size: terrainGridSize)
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
@ -23,7 +80,29 @@ class Renderer: NSObject, MTKViewDelegate {
}
func draw(in view: MTKView) {
// TODO.
guard let buffer = commandQueue.makeCommandBuffer() else {
return
}
var didEncode = false
buffer.label = "Terrain"
if let renderPass = view.currentRenderPassDescriptor {
if let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) {
encoder.label = "Terrain"
encoder.setRenderPipelineState(renderPipeline)
encoder.setVertexBuffer(terrain.buffer, offset: 0, index: 0)
encoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: terrain.vertexCount(forGridSize: terrainGridSize))
encoder.setTriangleFillMode(.lines)
encoder.endEncoding()
didEncode = true
}
}
if didEncode, let drawable = view.currentDrawable {
buffer.present(drawable)
}
buffer.commit()
}
}

35
Terrain/Shaders.metal Normal file
View file

@ -0,0 +1,35 @@
//
// Shaders.metal
// Terrain
//
// Created by Eryn Wells on 11/3/18.
// Copyright © 2018 Eryn Wells. All rights reserved.
//
#include <metal_stdlib>
using namespace metal;
// From HelloCompute sample code project.
// Vertex shader outputs and per-fragmeht inputs. Includes clip-space position and vertex outputs interpolated by rasterizer and fed to each fragment genterated by clip-space primitives.
struct RasterizerData {
// The [[position]] attribute qualifier of this member indicates this value is the clip space position of the vertex when this structure is returned from the vertex shader.
float4 position [[position]];
// Since this member does not have a special attribute qualifier, the rasterizer will interpolate its value with values of other vertices making up the triangle and pass that interpolated value to the fragment shader for each fragment in that triangle.
float2 textureCoordinate;
};
vertex RasterizerData passthroughVertex(constant packed_float3 *vertexes [[buffer(0)]],
uint vid [[vertex_id]])
{
constant packed_float3 &v = vertexes[vid];
RasterizerData out;
out.position = float4(v, 1.0);
out.textureCoordinate = float2();
return out;
}
fragment half4 passthroughFragment(RasterizerData in [[stage_in]])
{
return half4(1.0);
}

View file

@ -9,40 +9,52 @@
import Cocoa
class Terrain: NSObject {
static let vertexesPerCell = 6
var buffer: MTLBuffer?
override init() {
super.init()
}
func vertexCount(forGridSize size: CGSize) -> Int {
let (width, height) = (Int(size.width), Int(size.height))
let count = Terrain.vertexesPerCell * width * height
return count
}
func minimumBufferSize(forGridSize size: CGSize) -> Int {
let float3Stride = MemoryLayout<Float3>.stride
let count = vertexCount(forGridSize: size)
let minimumLength = float3Stride * count
return minimumLength
}
/// Generate a grid of triangles and place the result in the given buffer.
/// @param buffer The buffer to copy the results into
/// @param size The size of the grid. Each square is a pair of triangles.
func generateVertexes(intoBuffer buffer: MTLBuffer, size: CGSize) {
let VertexesPerCell = 6
let float3Stride = MemoryLayout<Float3>.stride
let (width, height) = (Int(size.width), Int(size.height))
let expectedCount = VertexesPerCell * width * height
let expectedLength = float3Stride * expectedCount
let expectedLength = minimumBufferSize(forGridSize: size)
guard buffer.length >= expectedLength else {
fatalError("Terrain.generateVertexes: buffer must be at least \(expectedLength) bytes to fix grid of size \(size)")
}
var vertexes = [Float3]()
vertexes.reserveCapacity(expectedCount)
vertexes.reserveCapacity(vertexCount(forGridSize: size))
let (cellWidth, cellHeight) = (Float(2.0) / Float(width), Float(2.0) / Float(height))
let (cellWidth, cellHeight) = (Float(2.0) / Float(size.width), Float(2.0) / Float(size.height))
for y in 0..<Int(size.height) {
for x in 0..<Int(size.width) {
let base = VertexesPerCell * (y * width + x)
vertexes[base+0] = Float3(x: Float(x) * cellWidth, y: Float(y) * cellHeight, z: 0.0)
vertexes[base+1] = Float3(x: Float(x) * cellWidth, y: Float(y) * cellHeight + cellHeight, z: 0.0)
vertexes[base+2] = Float3(x: Float(x) * cellWidth + cellWidth, y: Float(y) * cellHeight, z: 0.0)
vertexes[base+3] = Float3(x: Float(x) * cellWidth + cellWidth, y: Float(y) * cellHeight, z: 0.0)
vertexes[base+4] = Float3(x: Float(x) * cellWidth, y: Float(y) * cellHeight + cellHeight, z: 0.0)
vertexes[base+5] = Float3(x: Float(x) * cellWidth + cellWidth, y: Float(y) * cellHeight + cellHeight, z: 0.0)
vertexes.append(Float3(x: Float(x) * cellWidth, y: Float(y) * cellHeight, z: 0.0))
vertexes.append(Float3(x: Float(x) * cellWidth, y: Float(y) * cellHeight + cellHeight, z: 0.0))
vertexes.append(Float3(x: Float(x) * cellWidth + cellWidth, y: Float(y) * cellHeight, z: 0.0))
vertexes.append(Float3(x: Float(x) * cellWidth + cellWidth, y: Float(y) * cellHeight, z: 0.0))
vertexes.append(Float3(x: Float(x) * cellWidth, y: Float(y) * cellHeight + cellHeight, z: 0.0))
vertexes.append(Float3(x: Float(x) * cellWidth + cellWidth, y: Float(y) * cellHeight + cellHeight, z: 0.0))
}
}
self.buffer = buffer
memcpy(buffer.contents(), vertexes, expectedLength)
}
}

View file

@ -29,11 +29,17 @@ class TerrainViewController: NSViewController {
}
override func viewDidLoad() {
guard let device = metalView.device else {
fatalError("Couldn't get device from Metal view")
}
renderer = Renderer(device: device)
renderer = Renderer()
metalView.delegate = renderer
}
override func viewWillAppear() {
super.viewWillAppear()
guard let device = metalView.device else {
return
}
renderer.setupMetal(withDevice: device, pixelFormat: metalView.colorPixelFormat)
renderer.prepareToRender()
}
}