The full source code
import GLKit
import OpenGLES
func BUFFER_OFFSET(_ i: Int) -> UnsafePointer<Void>? {
return UnsafePointer<Void>(bitPattern: i)
}
let UNIFORM_MODELVIEWPROJECTION_MATRIX = 0
let UNIFORM_NORMAL_MATRIX = 1
var uniforms = [GLint](repeating: 0, count: 2)
class GameViewController: GLKViewController {
var program: GLuint = 0
var modelViewProjectionMatrix:GLKMatrix4 = GLKMatrix4Identity
var normalMatrix: GLKMatrix3 = GLKMatrix3Identity
var rotation: Float = 0.0
var vertexArray: GLuint = 0
var vertexBuffer: GLuint = 0
var context: EAGLContext? = nil
var effect: GLKBaseEffect? = nil
var panR:UIPanGestureRecognizer? = nil
deinit {
self.tearDownGL()
if EAGLContext.current() === self.context {
EAGLContext.setCurrent(nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.context = EAGLContext(api: .openGLES2)
if !(self.context != nil) {
print("Failed to create ES context")
}
let view = self.view as! GLKView
view.context = self.context!
view.drawableDepthFormat = .format24
self.setupGL()
self.panR = UIPanGestureRecognizer(target: self, action: #selector(GameViewController.doPan(panR:)))
self.view.addGestureRecognizer(self.panR!)
}
func doPan(panR:UIPanGestureRecognizer) {
self.update()
let gView:GLKView = self.view as! GLKView
gView.display()
}
override func viewWillAppear(_ animated: Bool) {
self.isPaused = true
self.resumeOnDidBecomeActive = false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
if self.isViewLoaded() && (self.view.window != nil) {
self.view = nil
self.tearDownGL()
if EAGLContext.current() === self.context {
EAGLContext.setCurrent(nil)
}
self.context = nil
}
}
func setupGL() {
EAGLContext.setCurrent(self.context)
if(self.loadShaders() == false) {
print("Failed to load shaders")
}
self.effect = GLKBaseEffect()
self.effect!.light0.enabled = GLboolean(GL_TRUE)
self.effect!.light0.diffuseColor = GLKVector4Make(1.0, 0.4, 0.4, 1.0)
let glView:GLKView = self.view as! GLKView
glView.context = self.context!
self.preferredFramesPerSecond = 60
// glView.drawableColorFormat = GLKViewDrawableColorFormat.RGBA8888;
// glView.drawableDepthFormat = GLKViewDrawableDepthFormat.format24;
// glView.drawableStencilFormat = GLKViewDrawableStencilFormat.format8;
// Enable multisampling
glView.drawableMultisample = GLKViewDrawableMultisample.multisample4X;
glEnable(GLenum(GL_DEPTH_TEST))
glGenVertexArraysOES(1, &vertexArray)
glBindVertexArrayOES(vertexArray)
glGenBuffers(1, &vertexBuffer)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
glBufferData(GLenum(GL_ARRAY_BUFFER), GLsizeiptr(sizeof(GLfloat) * gCubeVertexData.count), &gCubeVertexData, GLenum(GL_STATIC_DRAW))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 24, BUFFER_OFFSET(0))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.normal.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.normal.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 24, BUFFER_OFFSET(12))
glBindVertexArrayOES(0)
}
func tearDownGL() {
EAGLContext.setCurrent(self.context)
glDeleteBuffers(1, &vertexBuffer)
glDeleteVertexArraysOES(1, &vertexArray)
self.effect = nil
if program != 0 {
glDeleteProgram(program)
program = 0
}
}
// MARK: - GLKView and GLKViewController delegate methods
func update() {
let aspect = fabsf(Float(self.view.bounds.size.width / self.view.bounds.size.height))
let projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), aspect, 0.1, 100.0)
self.effect?.transform.projectionMatrix = projectionMatrix
var baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -4.0)
baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, rotation, 0.0, 1.0, 0.0)
// Compute the model view matrix for the object rendered with GLKit
var modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -1.5)
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, rotation, 1.0, 1.0, 1.0)
modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix)
self.effect?.transform.modelviewMatrix = modelViewMatrix
// Compute the model view matrix for the object rendered with ES2
modelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, 1.5)
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, rotation, 1.0, 1.0, 1.0)
modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix)
normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), nil)
modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix)
rotation += Float(self.timeSinceLastUpdate * 0.5)
}
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
glClearColor(0.65, 0.65, 0.65, 1.0)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT) | GLbitfield(GL_DEPTH_BUFFER_BIT))
glBindVertexArrayOES(vertexArray)
// Render the object with GLKit
self.effect?.prepareToDraw()
glDrawArrays(GLenum(GL_TRIANGLES) , 0, 36)
// Render the object again with ES2
glUseProgram(program)
withUnsafePointer(&modelViewProjectionMatrix, {
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, UnsafePointer($0))
})
withUnsafePointer(&normalMatrix, {
glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0, UnsafePointer($0))
})
glDrawArrays(GLenum(GL_TRIANGLES), 0, 36)
}
// MARK: - OpenGL ES 2 shader compilation
func loadShaders() -> Bool {
var vertShader: GLuint = 0
var fragShader: GLuint = 0
var vertShaderPathname: String
var fragShaderPathname: String
// Create shader program.
program = glCreateProgram()
// Create and compile vertex shader.
vertShaderPathname = Bundle.main().pathForResource("Shader", ofType: "vsh")!
if self.compileShader(&vertShader, type: GLenum(GL_VERTEX_SHADER), file: vertShaderPathname) == false {
print("Failed to compile vertex shader")
return false
}
// Create and compile fragment shader.
fragShaderPathname = Bundle.main().pathForResource("Shader", ofType: "fsh")!
if !self.compileShader(&fragShader, type: GLenum(GL_FRAGMENT_SHADER), file: fragShaderPathname) {
print("Failed to compile fragment shader")
return false
}
// Attach vertex shader to program.
glAttachShader(program, vertShader)
// Attach fragment shader to program.
glAttachShader(program, fragShader)
// Bind attribute locations.
// This needs to be done prior to linking.
glBindAttribLocation(program, GLuint(GLKVertexAttrib.position.rawValue), "position")
glBindAttribLocation(program, GLuint(GLKVertexAttrib.normal.rawValue), "normal")
// Link program.
if !self.linkProgram(program) {
print("Failed to link program: \(program)")
if vertShader != 0 {
glDeleteShader(vertShader)
vertShader = 0
}
if fragShader != 0 {
glDeleteShader(fragShader)
fragShader = 0
}
if program != 0 {
glDeleteProgram(program)
program = 0
}
return false
}
// Get uniform locations.
uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(program, "modelViewProjectionMatrix")
uniforms[UNIFORM_NORMAL_MATRIX] = glGetUniformLocation(program, "normalMatrix")
// Release vertex and fragment shaders.
if vertShader != 0 {
glDetachShader(program, vertShader)
glDeleteShader(vertShader)
}
if fragShader != 0 {
glDetachShader(program, fragShader)
glDeleteShader(fragShader)
}
return true
}
func compileShader(_ shader: inout GLuint, type: GLenum, file: String) -> Bool {
var status: GLint = 0
var source: UnsafePointer<Int8>
do {
source = try NSString(contentsOfFile: file, encoding: String.Encoding.utf8.rawValue).utf8String!
} catch {
print("Failed to load vertex shader")
return false
}
var castSource: UnsafePointer<GLchar>? = UnsafePointer<GLchar>(source)
shader = glCreateShader(type)
glShaderSource(shader, 1, &castSource, nil)
glCompileShader(shader)
//#if defined(DEBUG)
// var logLength: GLint = 0
// glGetShaderiv(shader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
// if logLength > 0 {
// var log = UnsafeMutablePointer<GLchar>(malloc(Int(logLength)))
// glGetShaderInfoLog(shader, logLength, &logLength, log)
// NSLog("Shader compile log: \n%s", log)
// free(log)
// }
//#endif
glGetShaderiv(shader, GLenum(GL_COMPILE_STATUS), &status)
if status == 0 {
glDeleteShader(shader)
return false
}
return true
}
func linkProgram(_ prog: GLuint) -> Bool {
var status: GLint = 0
glLinkProgram(prog)
//#if defined(DEBUG)
// var logLength: GLint = 0
// glGetShaderiv(shader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
// if logLength > 0 {
// var log = UnsafeMutablePointer<GLchar>(malloc(Int(logLength)))
// glGetShaderInfoLog(shader, logLength, &logLength, log)
// NSLog("Shader compile log: \n%s", log)
// free(log)
// }
//#endif
glGetProgramiv(prog, GLenum(GL_LINK_STATUS), &status)
if status == 0 {
return false
}
return true
}
func validateProgram(prog: GLuint) -> Bool {
var logLength: GLsizei = 0
var status: GLint = 0
glValidateProgram(prog)
glGetProgramiv(prog, GLenum(GL_INFO_LOG_LENGTH), &logLength)
if logLength > 0 {
var log: [GLchar] = [GLchar](repeating: 0, count: Int(logLength))
glGetProgramInfoLog(prog, logLength, &logLength, &log)
print("Program validate log: \n\(log)")
}
glGetProgramiv(prog, GLenum(GL_VALIDATE_STATUS), &status)
var returnVal = true
if status == 0 {
returnVal = false
}
return returnVal
}
}
var gCubeVertexData: [GLfloat] = [
// Data layout for each line below is:
// positionX, positionY, positionZ, normalX, normalY, normalZ,
0.5, -0.5, -0.5, 1.0, 0.0, 0.0,
0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
0.5, -0.5, 0.5, 1.0, 0.0, 0.0,
0.5, 0.5, -0.5, 1.0, 0.0, 0.0,
0.5, 0.5, 0.5, 1.0, 0.0, 0.0,
0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
-0.5, 0.5, -0.5, 0.0, 1.0, 0.0,
-0.5, 0.5, 0.5, 0.0, 1.0, 0.0,
-0.5, 0.5, -0.5, -1.0, 0.0, 0.0,
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
-0.5, 0.5, 0.5, -1.0, 0.0, 0.0,
-0.5, -0.5, -0.5, -1.0, 0.0, 0.0,
-0.5, -0.5, 0.5, -1.0, 0.0, 0.0,
-0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
-0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
-0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
0.5, -0.5, -0.5, 0.0, -1.0, 0.0,
0.5, -0.5, 0.5, 0.0, -1.0, 0.0,
0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
-0.5, 0.5, 0.5, 0.0, 0.0, 1.0,
-0.5, -0.5, 0.5, 0.0, 0.0, 1.0,
0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
0.5, 0.5, -0.5, 0.0, 0.0, -1.0,
-0.5, -0.5, -0.5, 0.0, 0.0, -1.0,
-0.5, 0.5, -0.5, 0.0, 0.0, -1.0
]