Extract Terrain into its own class

This commit is contained in:
Eryn Wells 2018-11-03 15:59:11 -04:00
parent ee2431d9c4
commit 5fa89d1e47
3 changed files with 89 additions and 62 deletions

View file

@ -23,6 +23,7 @@
C0C15AB9218E2A90007494E2 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = C0C15AB8218E2A90007494E2 /* Shaders.metal */; };
C0C15ABC218E2A90007494E2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C0C15ABB218E2A90007494E2 /* Assets.xcassets */; };
C0C15ABF218E2A90007494E2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C0C15ABD218E2A90007494E2 /* Main.storyboard */; };
C0C15AC6218E32B3007494E2 /* Terrain.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C15AC5218E32B2007494E2 /* Terrain.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -49,6 +50,7 @@
C0C15ABE218E2A90007494E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
C0C15AC0218E2A90007494E2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C0C15AC1218E2A90007494E2 /* Terrain2.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Terrain2.entitlements; sourceTree = "<group>"; };
C0C15AC5218E32B2007494E2 /* Terrain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Terrain.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -122,6 +124,7 @@
C0C15AB2218E2A90007494E2 /* AppDelegate.swift */,
C0C15AB4218E2A90007494E2 /* GameViewController.swift */,
C0C15AB6218E2A90007494E2 /* Renderer.swift */,
C0C15AC5218E32B2007494E2 /* Terrain.swift */,
C0C15AB8218E2A90007494E2 /* Shaders.metal */,
C0C15ABA218E2A90007494E2 /* ShaderTypes.h */,
C0C15ABB218E2A90007494E2 /* Assets.xcassets */,
@ -247,6 +250,7 @@
files = (
C0C15AB9218E2A90007494E2 /* Shaders.metal in Sources */,
C0C15AB5218E2A90007494E2 /* GameViewController.swift in Sources */,
C0C15AC6218E32B3007494E2 /* Terrain.swift in Sources */,
C0C15AB7218E2A90007494E2 /* Renderer.swift in Sources */,
C0C15AB3218E2A90007494E2 /* AppDelegate.swift in Sources */,
);

View file

@ -42,7 +42,7 @@ class Renderer: NSObject, MTKViewDelegate {
var rotation: Float = 0
var mesh: MTKMesh
var terrain: Terrain
init?(metalKitView: MTKView) {
self.device = metalKitView.device!
@ -61,12 +61,12 @@ class Renderer: NSObject, MTKViewDelegate {
metalKitView.colorPixelFormat = MTLPixelFormat.bgra8Unorm_srgb
metalKitView.sampleCount = 1
let mtlVertexDescriptor = Renderer.buildMetalVertexDescriptor()
terrain = Terrain(dimensions: float2(8, 8), segments: uint2(20, 20), device: device)!
do {
pipelineState = try Renderer.buildRenderPipelineWithDevice(device: device,
metalKitView: metalKitView,
mtlVertexDescriptor: mtlVertexDescriptor)
mtlVertexDescriptor: terrain.vertexDescriptor)
} catch {
print("Unable to compile render pipeline state. Error info: \(error)")
return nil
@ -77,13 +77,6 @@ class Renderer: NSObject, MTKViewDelegate {
depthStateDesciptor.isDepthWriteEnabled = true
self.depthState = device.makeDepthStencilState(descriptor:depthStateDesciptor)!
do {
mesh = try Renderer.buildMesh(device: device, mtlVertexDescriptor: mtlVertexDescriptor)
} catch {
print("Unable to build MetalKit Mesh. Error info: \(error)")
return nil
}
do {
colorMap = try Renderer.loadTexture(device: device, textureName: "ColorMap")
} catch {
@ -95,31 +88,6 @@ class Renderer: NSObject, MTKViewDelegate {
}
class func buildMetalVertexDescriptor() -> MTLVertexDescriptor {
// Creete a Metal vertex descriptor specifying how vertices will by laid out for input into our render
// pipeline and how we'll layout our Model IO vertices
let mtlVertexDescriptor = MTLVertexDescriptor()
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].format = MTLVertexFormat.float3
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].offset = 0
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].bufferIndex = BufferIndex.meshPositions.rawValue
mtlVertexDescriptor.attributes[VertexAttribute.texcoord.rawValue].format = MTLVertexFormat.float2
mtlVertexDescriptor.attributes[VertexAttribute.texcoord.rawValue].offset = 0
mtlVertexDescriptor.attributes[VertexAttribute.texcoord.rawValue].bufferIndex = BufferIndex.meshGenerics.rawValue
mtlVertexDescriptor.layouts[BufferIndex.meshPositions.rawValue].stride = 12
mtlVertexDescriptor.layouts[BufferIndex.meshPositions.rawValue].stepRate = 1
mtlVertexDescriptor.layouts[BufferIndex.meshPositions.rawValue].stepFunction = MTLVertexStepFunction.perVertex
mtlVertexDescriptor.layouts[BufferIndex.meshGenerics.rawValue].stride = 8
mtlVertexDescriptor.layouts[BufferIndex.meshGenerics.rawValue].stepRate = 1
mtlVertexDescriptor.layouts[BufferIndex.meshGenerics.rawValue].stepFunction = MTLVertexStepFunction.perVertex
return mtlVertexDescriptor
}
class func buildRenderPipelineWithDevice(device: MTLDevice,
metalKitView: MTKView,
mtlVertexDescriptor: MTLVertexDescriptor) throws -> MTLRenderPipelineState {
@ -144,30 +112,6 @@ class Renderer: NSObject, MTKViewDelegate {
return try device.makeRenderPipelineState(descriptor: pipelineDescriptor)
}
class func buildMesh(device: MTLDevice,
mtlVertexDescriptor: MTLVertexDescriptor) throws -> MTKMesh {
/// Create and condition mesh data to feed into a pipeline using the given vertex descriptor
let metalAllocator = MTKMeshBufferAllocator(device: device)
let plane = MDLMesh.newPlane(withDimensions: float2(6, 6),
segments: uint2(20, 20),
geometryType: .triangles,
allocator: metalAllocator)
let mdlVertexDescriptor = MTKModelIOVertexDescriptorFromMetal(mtlVertexDescriptor)
guard let attributes = mdlVertexDescriptor.attributes as? [MDLVertexAttribute] else {
throw RendererError.badVertexDescriptor
}
attributes[VertexAttribute.position.rawValue].name = MDLVertexAttributePosition
attributes[VertexAttribute.texcoord.rawValue].name = MDLVertexAttributeTextureCoordinate
plane.vertexDescriptor = mdlVertexDescriptor
return try MTKMesh(mesh:plane, device:device)
}
class func loadTexture(device: MTLDevice,
textureName: String) throws -> MTLTexture {
/// Load texture data with optimal parameters for sampling
@ -248,20 +192,20 @@ class Renderer: NSObject, MTKViewDelegate {
renderEncoder.setVertexBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue)
renderEncoder.setFragmentBuffer(dynamicUniformBuffer, offset:uniformBufferOffset, index: BufferIndex.uniforms.rawValue)
for (index, element) in mesh.vertexDescriptor.layouts.enumerated() {
for (index, element) in terrain.mesh.vertexDescriptor.layouts.enumerated() {
guard let layout = element as? MDLVertexBufferLayout else {
return
}
if layout.stride != 0 {
let buffer = mesh.vertexBuffers[index]
let buffer = terrain.mesh.vertexBuffers[index]
renderEncoder.setVertexBuffer(buffer.buffer, offset:buffer.offset, index: index)
}
}
renderEncoder.setFragmentTexture(colorMap, index: TextureIndex.color.rawValue)
for submesh in mesh.submeshes {
for submesh in terrain.mesh.submeshes {
renderEncoder.drawIndexedPrimitives(type: submesh.primitiveType,
indexCount: submesh.indexCount,
indexType: submesh.indexType,

79
Terrain2/Terrain.swift Normal file
View file

@ -0,0 +1,79 @@
//
// Terrain.swift
// Terrain
//
// Created by Eryn Wells on 11/3/18.
// Copyright © 2018 Eryn Wells. All rights reserved.
//
import Cocoa
import MetalKit
class Terrain: NSObject {
/// Creete a Metal vertex descriptor specifying how vertices will by laid out for input into our render pipeline and how we'll layout our Model IO vertices.
class func buildVertexDescriptor() -> MTLVertexDescriptor {
let mtlVertexDescriptor = MTLVertexDescriptor()
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].format = MTLVertexFormat.float3
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].offset = 0
mtlVertexDescriptor.attributes[VertexAttribute.position.rawValue].bufferIndex = BufferIndex.meshPositions.rawValue
mtlVertexDescriptor.attributes[VertexAttribute.texcoord.rawValue].format = MTLVertexFormat.float2
mtlVertexDescriptor.attributes[VertexAttribute.texcoord.rawValue].offset = 0
mtlVertexDescriptor.attributes[VertexAttribute.texcoord.rawValue].bufferIndex = BufferIndex.meshGenerics.rawValue
mtlVertexDescriptor.layouts[BufferIndex.meshPositions.rawValue].stride = 12
mtlVertexDescriptor.layouts[BufferIndex.meshPositions.rawValue].stepRate = 1
mtlVertexDescriptor.layouts[BufferIndex.meshPositions.rawValue].stepFunction = MTLVertexStepFunction.perVertex
mtlVertexDescriptor.layouts[BufferIndex.meshGenerics.rawValue].stride = 8
mtlVertexDescriptor.layouts[BufferIndex.meshGenerics.rawValue].stepRate = 1
mtlVertexDescriptor.layouts[BufferIndex.meshGenerics.rawValue].stepFunction = MTLVertexStepFunction.perVertex
return mtlVertexDescriptor
}
/// Create and condition mesh data to feed into a pipeline using the given vertex descriptor.
class func buildMesh(withDimensions dimensions: float2, segments: uint2, device: MTLDevice, vertexDescriptor: MTLVertexDescriptor) throws -> MTKMesh {
let metalAllocator = MTKMeshBufferAllocator(device: device)
let plane = MDLMesh.newPlane(withDimensions: dimensions,
segments: segments,
geometryType: .triangles,
allocator: metalAllocator)
let mdlVertexDescriptor = MTKModelIOVertexDescriptorFromMetal(vertexDescriptor)
guard let attributes = mdlVertexDescriptor.attributes as? [MDLVertexAttribute] else {
throw RendererError.badVertexDescriptor
}
attributes[VertexAttribute.position.rawValue].name = MDLVertexAttributePosition
attributes[VertexAttribute.texcoord.rawValue].name = MDLVertexAttributeTextureCoordinate
plane.vertexDescriptor = mdlVertexDescriptor
return try MTKMesh(mesh:plane, device:device)
}
let dimensions: float2
let segments: uint2
let vertexDescriptor: MTLVertexDescriptor
let mesh: MTKMesh
init?(dimensions dim: float2, segments seg: uint2, device: MTLDevice) {
dimensions = dim
segments = seg
vertexDescriptor = Terrain.buildVertexDescriptor()
do {
mesh = try Terrain.buildMesh(withDimensions: dimensions, segments: segments, device: device, vertexDescriptor: vertexDescriptor)
} catch let e {
print("Couldn't create mesh. Error: \(e)")
return nil
}
super.init()
}
}