diff --git a/Terrain.xcodeproj/project.pbxproj b/Terrain.xcodeproj/project.pbxproj index 835a59d..ffd4d83 100644 --- a/Terrain.xcodeproj/project.pbxproj +++ b/Terrain.xcodeproj/project.pbxproj @@ -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 = ""; }; C0C15AC0218E2A90007494E2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C0C15AC1218E2A90007494E2 /* Terrain2.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Terrain2.entitlements; sourceTree = ""; }; + C0C15AC5218E32B2007494E2 /* Terrain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Terrain.swift; sourceTree = ""; }; /* 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 */, ); diff --git a/Terrain2/Renderer.swift b/Terrain2/Renderer.swift index 901ab5c..1e8f3e1 100644 --- a/Terrain2/Renderer.swift +++ b/Terrain2/Renderer.swift @@ -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, diff --git a/Terrain2/Terrain.swift b/Terrain2/Terrain.swift new file mode 100644 index 0000000..9a979f9 --- /dev/null +++ b/Terrain2/Terrain.swift @@ -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() + } +}