Skip to content

Instantly share code, notes, and snippets.

@skyfe79
Forked from kconner/GameViewController.swift
Created February 27, 2016 16:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save skyfe79/8b4c6a0cb458cd6a04d6 to your computer and use it in GitHub Desktop.
Save skyfe79/8b4c6a0cb458cd6a04d6 to your computer and use it in GitHub Desktop.
Xcode 6.3 OpenGL Game template ported to Swift. (Add "-D DEBUG" to Other Swift Flags setting)
import GLKit
// Uniform index.
private enum Uniform {
case ModelViewProjectionMatrix, NormalMatrix
}
private var gUniforms: [Uniform: GLint] = [:]
extension GLKMatrix3 {
var array: [Float] {
return (0..<9).map { i in
self[i]
}
}
}
extension GLKMatrix4 {
var array: [Float] {
return (0..<16).map { i in
self[i]
}
}
}
extension Array {
var bufferSize: size_t {
return sizeof(Element) * self.count
}
}
private func unsafeBufferOffset(size: size_t) -> UnsafePointer<Void> {
return UnsafePointer(bitPattern: size)
}
final 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
deinit {
self.tearDownGL()
if EAGLContext.currentContext() === self.context {
EAGLContext.setCurrentContext(nil)
}
}
// MARK: UIViewController
override func viewDidLoad() {
super.viewDidLoad()
if let context = EAGLContext(API: .OpenGLES2) {
self.context = context
} else {
println("Failed to create ES context")
}
self.gameView.context = self.context
self.gameView.drawableDepthFormat = .Format24
self.setupGL()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
if self.isViewLoaded() && self.view.window == nil {
self.view = nil
self.tearDownGL()
if EAGLContext.currentContext() === self.context {
EAGLContext.setCurrentContext(nil)
}
self.context = nil
}
}
// MARK: Helpers
var gameView: GLKView {
return self.view as! GLKView
}
func setupGL() {
EAGLContext.setCurrentContext(self.context)
self.loadShaders()
self.effect = GLKBaseEffect()
self.effect!.light0.enabled = GLboolean(GL_TRUE)
self.effect!.light0.diffuseColor = GLKVector4Make(1.0, 0.4, 0.4, 1.0)
glEnable(GLenum(GL_DEPTH_TEST))
glGenVertexArraysOES(1, &vertexArray)
glBindVertexArrayOES(vertexArray)
glGenBuffers(1, &vertexBuffer)
glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
glBufferData(GLenum(GL_ARRAY_BUFFER), gCubeVertexData.bufferSize, gCubeVertexData, GLenum(GL_STATIC_DRAW))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.Position.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.Position.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 24, unsafeBufferOffset(0))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.Normal.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.Normal.rawValue), 3, GLenum(GL_FLOAT), GLboolean(GL_FALSE), 24, unsafeBufferOffset(12))
glBindVertexArrayOES(0);
}
func tearDownGL() {
EAGLContext.setCurrentContext(self.context)
glDeleteBuffers(1, &self.vertexBuffer)
glDeleteVertexArraysOES(1, &self.vertexArray)
self.effect = nil
if self.program != 0 {
glDeleteProgram(self.program)
self.program = 0
}
}
// MARK: GLKViewController
func update() {
let aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height)
let projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0), Float(aspect), 0.1, 100.0)
self.effect?.transform.projectionMatrix = projectionMatrix
var baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0, 0.0, -4.0)
baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, self.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, self.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, self.rotation, 1.0, 1.0, 1.0)
modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix)
self.normalMatrix = GLKMatrix3InvertAndTranspose(GLKMatrix4GetMatrix3(modelViewMatrix), nil)
self.modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix)
self.rotation += Float(self.timeSinceLastUpdate * 0.5)
}
override func glkView(view: GLKView, drawInRect rect: CGRect) {
glClearColor(0.65, 0.65, 0.65, 1.0)
glClear(GLbitfield(GL_COLOR_BUFFER_BIT | 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)
glUniformMatrix4fv(gUniforms[.ModelViewProjectionMatrix]!, 1, GLboolean(GL_FALSE), self.modelViewProjectionMatrix.array)
glUniformMatrix3fv(gUniforms[.NormalMatrix]!, 1, GLboolean(GL_FALSE), self.normalMatrix.array)
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.
self.program = glCreateProgram()
// Create and compile vertex shader.
vertShaderPathname = NSBundle.mainBundle().pathForResource("Shader", ofType: "vsh")!
if !self.compileShader(&vertShader, type: GLenum(GL_VERTEX_SHADER), file: vertShaderPathname) {
println("Failed to compile vertex shader")
return false
}
// Create and compile fragment shader.
fragShaderPathname = NSBundle.mainBundle().pathForResource("Shader", ofType: "fsh")!
if !self.compileShader(&fragShader, type: GLenum(GL_FRAGMENT_SHADER), file: fragShaderPathname) {
println("Failed to compile fragment shader");
return false
}
// Attach vertex shader to program.
glAttachShader(self.program, vertShader)
// Attach fragment shader to program.
glAttachShader(self.program, fragShader)
// Bind attribute locations.
// This needs to be done prior to linking.
glBindAttribLocation(self.program, GLuint(GLKVertexAttrib.Position.rawValue), "position")
glBindAttribLocation(self.program, GLuint(GLKVertexAttrib.Normal.rawValue), "normal")
// Link program.
if !self.linkProgram(self.program) {
println("Failed to link program: \(self.program)")
if vertShader != 0 {
glDeleteShader(vertShader)
vertShader = 0
}
if fragShader != 0 {
glDeleteShader(fragShader)
fragShader = 0
}
if self.program != 0 {
glDeleteProgram(self.program)
self.program = 0
}
return false
}
// Get uniform locations.
gUniforms[.ModelViewProjectionMatrix] = glGetUniformLocation(self.program, "modelViewProjectionMatrix")
gUniforms[.NormalMatrix] = glGetUniformLocation(self.program, "normalMatrix")
// Release vertex and fragment shaders.
if vertShader != 0 {
glDetachShader(self.program, vertShader)
glDeleteShader(vertShader);
}
if fragShader != 0 {
glDetachShader(self.program, fragShader);
glDeleteShader(fragShader);
}
return true
}
func compileShader(inout shader: GLuint, type: GLenum, file: String) -> Bool {
var error: NSError?
if let source = NSString(contentsOfFile: file, encoding: NSUTF8StringEncoding, error: &error)?.UTF8String {
[source].withUnsafeBufferPointer { sourcePointersPointer -> Void in
shader = glCreateShader(type)
glShaderSource(shader, 1, sourcePointersPointer.baseAddress, nil)
glCompileShader(shader)
}
#if DEBUG
var logLength: GLint = 0
glGetShaderiv(shader, GLenum(GL_INFO_LOG_LENGTH), &logLength)
if 0 < logLength {
var log = Array<GLchar>(count: Int(logLength), repeatedValue: 0)
log.withUnsafeBufferPointer { logPointer -> Void in
glGetShaderInfoLog(shader, logLength, &logLength, UnsafeMutablePointer(logPointer.baseAddress))
NSLog("Shader compile log: \n%@", String(UTF8String: log)!)
}
}
#endif
var status: GLint = 0
glGetShaderiv(shader, GLenum(GL_COMPILE_STATUS), &status)
if status == 0 {
glDeleteShader(shader);
return false
}
} else {
println("Failed to load vertex shader")
return false
}
return true
}
func linkProgram(prog: GLuint) -> Bool {
glLinkProgram(prog)
#if DEBUG
var logLength: GLint = 0
glGetProgramiv(prog, GLenum(GL_INFO_LOG_LENGTH), &logLength)
if 0 < logLength {
var log = Array<GLchar>(count: Int(logLength), repeatedValue: 0)
log.withUnsafeBufferPointer { logPointer -> Void in
glGetProgramInfoLog(prog, logLength, &logLength, UnsafeMutablePointer(logPointer.baseAddress))
NSLog("Program link log: \n%@", String(UTF8String: log)!)
}
}
#endif
var status: GLint = 0
glGetProgramiv(prog, GLenum(GL_LINK_STATUS), &status)
if status == 0 {
return false
}
return true
}
func validateProgram(prog: GLuint) -> Bool {
#if DEBUG
var logLength: GLint = 0
glGetProgramiv(prog, GLenum(GL_INFO_LOG_LENGTH), &logLength)
if 0 < logLength {
var log = Array<GLchar>(count: Int(logLength), repeatedValue: 0)
log.withUnsafeBufferPointer { logPointer -> Void in
glGetProgramInfoLog(prog, logLength, &logLength, UnsafeMutablePointer(logPointer.baseAddress))
NSLog("Program validate log: \n%@", String(UTF8String: log)!)
}
}
#endif
var status: GLint = 0
glGetProgramiv(prog, GLenum(GL_VALIDATE_STATUS), &status)
if status == 0 {
return false
}
return true
}
}
private let 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
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment