-
Notifications
You must be signed in to change notification settings - Fork 4
/
Model.swift
155 lines (127 loc) · 6.22 KB
/
Model.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//
// Model.swift
// Triangle
//
// Created by Andrew K. on 6/24/14.
// Copyright (c) 2014 Andrew Kharchyshyn. All rights reserved.
//
import UIKit
import Metal
import QuartzCore
@objc class Model: NSObject
{
var time:CFTimeInterval = 0.0
var baseEffect: BaseEffect
let name: String
var vertexCount: Int
var texture: MTLTexture?
var positionX:Float = 0.0
var positionY:Float = 0.0
var positionZ:Float = 0.0
var rotationX:Float = 0.0
var rotationY:Float = 0.0
var rotationZ:Float = 0.0
var scale:Float = 1.0
var vertexBuffer: MTLBuffer?
var uniformBufferGenerator: AnyObject
var uniformsBuffer: MTLBuffer?
var samplerState: MTLSamplerState?
var avaliableUniformBuffers = dispatch_semaphore_create(3)
init(name: String,
baseEffect: BaseEffect,
vertices: Array<Vertex>,
vertexCount: Int,
texture: MTLTexture)
{
self.name = name
self.baseEffect = baseEffect
self.vertexCount = vertexCount
self.texture = texture
self.uniformBufferGenerator = UniformsBufferGenerator(numberOfInflightBuffers: 3, withDevice: baseEffect.device)
super.init()
self.vertexBuffer = generateVertexBuffer(vertices, vertexCount: vertexCount, device: baseEffect.device)
self.samplerState = generateSamplerStateForTexture(baseEffect.device)
}
func render(commandQueue: MTLCommandQueue, drawable: CAMetalDrawable, parentMVMatrix: AnyObject)
{
var parentModelViewMatrix: Matrix4 = parentMVMatrix as Matrix4
var myModelViewMatrix: Matrix4 = modelMatrix() as Matrix4
myModelViewMatrix.multiplyLeft(parentModelViewMatrix)
var projectionMatrix: Matrix4 = baseEffect.projectionMatrix as Matrix4
self.uniformsBuffer = getUniformsBuffer(myModelViewMatrix, projMatrix: projectionMatrix, device: baseEffect.device)
//We are using 3 uniform buffers, we need to wait in case CPU wants to write in first uniform buffer, while GPU is still using it (case when GPU is 2 frames ahead CPU)
dispatch_semaphore_wait(avaliableUniformBuffers, DISPATCH_TIME_FOREVER)
var commandBuffer = commandQueue.commandBuffer()
commandBuffer.addCompletedHandler(
{
(buffer:MTLCommandBuffer!) -> Void in
var q = dispatch_semaphore_signal(self.avaliableUniformBuffers)
})
// MTLRenderPassDescriptor object represents a collection of configurable states
var renderPassDesc = MTLRenderPassDescriptor()
renderPassDesc.colorAttachments[0].texture = drawable.texture
renderPassDesc.colorAttachments[0].loadAction = MTLLoadAction.Clear
// Create MTLRenderCommandEncoder object which translates all states into a command for GPU
var commandEncoder:MTLRenderCommandEncoder = commandBuffer.renderCommandEncoderWithDescriptor(renderPassDesc)
commandEncoder.setRenderPipelineState(baseEffect.renderPipelineState)
commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, atIndex: 0)
commandEncoder.setVertexBuffer(uniformsBuffer, offset: 0, atIndex: 1)
commandEncoder.setFragmentTexture(self.texture, atIndex: 0)
commandEncoder.setFragmentSamplerState(self.samplerState, atIndex: 0)
commandEncoder.setCullMode(MTLCullMode.Front)
commandEncoder.drawPrimitives(MTLPrimitiveType.Triangle, vertexStart: 0, vertexCount: vertexCount);
commandEncoder.endEncoding();
// After command in command buffer is encoded for GPU we provide drawable that will be invoked when this command buffer has been scheduled for execution
commandBuffer.presentDrawable(drawable)
// Commit commandBuffer to his commandQueue in which he will be executed after commands before him in queue
commandBuffer.commit();
}
func modelMatrix() -> AnyObject //AnyObject is used as a workaround against comiler error, waiting for fix in following betas
{
var matrix = Matrix4()
matrix.translate(positionX, y: positionY, z: positionZ)
matrix.rotateAroundX(rotationX, y: rotationY, z: rotationZ)
matrix.scale(scale, y: scale, z: scale)
return matrix
}
func updateWithDelta(delta: CFTimeInterval)
{
time += delta
}
func generateSamplerStateForTexture(device: MTLDevice) -> MTLSamplerState?
{
var pSamplerDescriptor:MTLSamplerDescriptor? = MTLSamplerDescriptor();
if let sampler = pSamplerDescriptor
{
sampler.minFilter = MTLSamplerMinMagFilter.Nearest
sampler.magFilter = MTLSamplerMinMagFilter.Nearest
sampler.mipFilter = MTLSamplerMipFilter.NotMipmapped
sampler.maxAnisotropy = 1
sampler.sAddressMode = MTLSamplerAddressMode.ClampToEdge
sampler.tAddressMode = MTLSamplerAddressMode.ClampToEdge
sampler.rAddressMode = MTLSamplerAddressMode.ClampToEdge
sampler.normalizedCoordinates = true
sampler.lodMinClamp = 0
sampler.lodMaxClamp = FLT_MAX
}
else
{
println(">> ERROR: Failed creating a sampler descriptor!")
}
return device.newSamplerStateWithDescriptor(pSamplerDescriptor)
}
// Two following methods are used as a glue for Objective-C buffer generator code and Swift code
func getUniformsBuffer(mvMatrix: AnyObject, projMatrix: AnyObject, device: MTLDevice) -> MTLBuffer?
{
var mv:Matrix4 = mvMatrix as Matrix4
var proj:Matrix4 = projMatrix as Matrix4
var generator: UniformsBufferGenerator = self.uniformBufferGenerator as UniformsBufferGenerator
uniformsBuffer = generator.bufferWithProjectionMatrix(proj, modelViewMatrix: mv)
return uniformsBuffer
}
func generateVertexBuffer(vertices: Array<Vertex>, vertexCount: Int, device: MTLDevice) -> MTLBuffer?
{
vertexBuffer = VertexBufferGenerator.generateBufferVertices(vertices, vertexCount: vertexCount, device: device)
return vertexBuffer
}
}