はじめに
Xcodeでは、3DプログラミングをするのにMetalを使うかOpenGLを使うかを選べる。
ただし、SwiftでOpenGLを使おうとすると、"in progress"などと書かれていて、きちんと動かない。
しょうがないので、とりあえず動くようにソースを書き換えたメモ。
GameViewController.swift
Xcodeで新しくSwift+OpenGL ESのプロジェクトを作ると、GameViewController.swiftというファイルが作成されるが、その中身は動かない。
なので動くように書き換えたのが下記。
割と力技なので動作保証はなし。
GameViewController.swift
import GLKit
import OpenGLES
class GameViewController: GLKViewController {
enum Uniform_index : Int {
case UNIFORM_MODELVIEWPROJECTION_MATRIX = 0
case UNIFORM_NORMAL_MATRIX = 1
case NUM_UNIFORMS = 2
}
var uniforms: [GLint] = [GLint](count: Uniform_index.NUM_UNIFORMS.rawValue, repeatedValue: GLint(0))
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)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.context = EAGLContext(API: .OpenGLES2)
if self.context == nil {
println("Failed to create ES context")
}
let view = self.view as! GLKView
view.context = self.context
view.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
}
}
func BUFFER_OFFSET(n: Int) -> UnsafePointer<Void> {
let ptr: UnsafePointer<Void> = nil
return ptr + n * sizeof(Void)
}
func setupGL() {
EAGLContext.setCurrentContext(self.context)
self.loadShaders()
self.effect = GLKBaseEffect()
self.effect!.light0.enabled = /*GL_TRUE*/1
self.effect!.light0.diffuseColor = GLKVector4Make(1.0, 0.4, 0.4, 1.0)
glEnable(/*GL_DEPTH_TEST*/0x0B71)
glGenVertexArraysOES(1, &vertexArray)
glBindVertexArrayOES(vertexArray)
glGenBuffers(1, &vertexBuffer)
glBindBuffer(/*GL_ARRAY_BUFFER*/0x8892, vertexBuffer)
glBufferData(/*GL_ARRAY_BUFFER*/0x8892, sizeof(CGFloat) * gCubeVertexData.count, gCubeVertexData, /*GL_STATIC_DRAW*/0x88E4)
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.Position.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.Position.rawValue), GLint(3), GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(24), BUFFER_OFFSET(0))
glEnableVertexAttribArray(GLuint(GLKVertexAttrib.Normal.rawValue))
glVertexAttribPointer(GLuint(GLKVertexAttrib.Normal.rawValue), GLint(3), GLenum(GL_FLOAT), GLboolean(GL_FALSE), GLsizei(24), BUFFER_OFFSET(12))
glBindVertexArrayOES(0);
}
func tearDownGL() {
EAGLContext.setCurrentContext(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, drawInRect rect: CGRect) {
glClearColor(0.65, 0.65, 0.65, 1.0)
glClear(/*GL_COLOR_BUFFER_BIT*/0x00004000 | /*GL_DEPTH_BUFFER_BIT*/0x00000100)
glBindVertexArrayOES(vertexArray)
// Render the object with GLKit
self.effect?.prepareToDraw()
glDrawArrays(/*GL_TRIANGLES*/0x0004, 0, 36)
// Render the object again with ES2
glUseProgram(program)
glUniformMatrix4fv(uniforms[Uniform_index.UNIFORM_MODELVIEWPROJECTION_MATRIX.rawValue], 1, 0, modelViewProjectionMatrix.m)
glUniformMatrix3fv(uniforms[Uniform_index.UNIFORM_NORMAL_MATRIX.rawValue], 1, 0, normalMatrix.m)
glDrawArrays(/*GL_TRIANGLES*/0x0004, 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 = NSBundle.mainBundle().pathForResource("Shader", ofType: "vsh")!
if !self.compileShader(&vertShader, type: /*GL_VERTEX_SHADER*/0x8B31, 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: /*GL_FRAGMENT_SHADER*/0x8B30, file: fragShaderPathname) {
println("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) {
println("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_index.UNIFORM_MODELVIEWPROJECTION_MATRIX.rawValue] = glGetUniformLocation(program, "modelViewProjectionMatrix")
uniforms[Uniform_index.UNIFORM_NORMAL_MATRIX.rawValue] = 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(inout shader: GLuint, type: GLenum, file: String) -> Bool {
var status: GLint = 0 // 初期化しないとポインタを引数に持つ関数に渡せないっぽい?
var source: UnsafePointer<GLchar> = NSString(contentsOfFile: file, encoding: NSUTF8StringEncoding, error: nil)!.UTF8String
if source == nil {
println("Failed to load vertex shader")
return false
}
shader = glCreateShader(type)
glShaderSource(shader, GLsizei(1), &source, nil)
glCompileShader(shader)
glGetShaderiv(shader, /*GL_COMPILE_STATUS*/0x8B81, &status)
if status == 0 {
glDeleteShader(shader);
return false
}
return true
}
func linkProgram(prog: GLuint) -> Bool {
var status: GLint = 0
glLinkProgram(prog)
glGetProgramiv(prog, /*GL_LINK_STATUS*/0x8B82, &status)
if status == 0 {
return false
}
return true
}
func validateProgram(prog: GLuint) -> Bool {
var logLength: GLint = 0
var status: GLint = 0
glValidateProgram(prog)
glGetProgramiv(prog, /*GL_INFO_LOG_LENGTH*/0x8B84, &logLength)
if logLength > 0 {
let log = UnsafeMutablePointer<UInt>.alloc(Int(logLength))
glGetProgramInfoLog(prog, GLsizei(logLength), &logLength, UnsafeMutablePointer<GLchar>(log))
println("Program validate log: \n\(log)")
free(log)
}
glGetProgramiv(prog, GLenum(GL_VALIDATE_STATUS), &status)
if status == 0 {
return false
}
return true
}
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
]
}
GLKMatrix3+Array.swift
import GLKit
extension GLKMatrix3 {
var m: [Float] {
get {
let ary: [Float] = [
m00,
m01,
m02,
m10,
m11,
m12,
m20,
m21,
m22,
]
return ary
}
}
}
GLKMatrix4+Array.swift
import GLKit
extension GLKMatrix4 {
var m: [Float] {
get {
let ary: [Float] = [
m00,
m01,
m02,
m03,
m10,
m11,
m12,
m13,
m20,
m21,
m22,
m23,
m30,
m31,
m32,
m33
]
return ary
}
}
}
つづく?
その2に続くかどうかは未定。