LoginSignup
10
9

More than 5 years have passed since last update.

[Swift][NSOpenGLView]SwiftでNSOpenGLViewを使う 

Last updated at Posted at 2015-06-15

[Swift][NSOpenGLView]SwiftでNSOpenGLViewを使う 

SwiftでCocoaアプリケーションを開発する際の、NSOpenGLView使用法についてメモ代わりにまとめました。
毎度のことですが、殴り書きご容赦ください。
私はObjective-Cの経験が無く、NSOpenGLViewのSwiftでの実装方法に四苦八苦しました。
NSOpenGLViewを使うメリットは、やはり描写の速さでしょうか。
このサンプルコードでは1000個の点を描写しています。

Screen Shot 2015-06-15 at 9.31.34 PM.png

ViewController

まずはViewControllerのコードです。

ViewController.swift

import Cocoa

class ViewController: NSViewController {

    var OpenGLView:NSOpenGLView!
    var OpenGLContents:OpenGLContentsDraw!


    override func mouseDown(theEvent: NSEvent) {
        self.OpenGLContents.updateScene()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.OpenGLView = NSOpenGLView(frame:CGRectMake(0,0,self.view.bounds.width,self.view.bounds.height))
        self.OpenGLContents = OpenGLContentsDraw(frame:CGRectMake(0,0,self.view.bounds.width,self.view.bounds.height),PointSize:GLfloat(20.0))

        self.OpenGLView.addSubview(self.OpenGLContents)
        self.view.addSubview(self.OpenGLView)


        // Do any additional setup after loading the view.
    }

    override var representedObject: AnyObject? {
        didSet {
        // Update the view, if already loaded.
        }
    }

}

今回は、stroyboardを使用せず、オブジェクトもコードで生成します。
override func viewDisLoad()は、viewのロードが完了した際に呼ばれる関数です。
ここで、NSOpenGLViewオブジェクトを生成します。生成方法は、window上の位置とオブジェクトのサイズをCGRectMakeで指定するのみです。

        self.OpenGLView = NSOpenGLView(frame:CGRectMake(0,0,self.view.bounds.width,self.view.bounds.height))

実際にwindowに追加するには、以下のようにします。

        self.view.addSubview(self.OpenGLView)

これで何も描写されていないNSOpenGLViewがWindow上に生成されるかと思います。
描写内容を生成し、NSOpenGLViewに描写するためにはcontentsを生成し、NSOpenGLViewに追加します。

ここで、新規ファイルを作成し、OpenGLContentsDraw.swiftをプロジェクトに追加します。
少し長いですが、以下のようにコードを書きます。

描写部分

OpenGLContentsDraw.swift


import Foundation
import Cocoa
import OpenGL
import GLKit
import GLUT


class OpenGLContentsDraw:NSOpenGLView{

    var width:GLsizei
    var height:GLsizei
    var PointSize:GLfloat

    init?(frame:NSRect, PointSize:GLfloat) {

        //set size of this view
        self.width = GLsizei(frame.size.width)
        self.height = GLsizei(frame.size.height)
        self.PointSize = PointSize

        //setup pixelFormatAttributes
        let pixelFormatAttributes:[NSOpenGLPixelFormatAttribute] = [
            UInt32(NSOpenGLPFAAccelerated),
            UInt32(NSOpenGLPFADoubleBuffer),
            UInt32(NSOpenGLPFADepthSize), UInt32(32),
            UInt32(NSOpenGLPFAColorSize), UInt32(48),
            UInt32(NSOpenGLPFAAlphaSize), UInt32(16),
            UInt32(NSOpenGLPFAMultisample),
            UInt32(NSOpenGLPFASampleBuffers), UInt32(1),
            UInt32(NSOpenGLPFASamples), UInt32(4),
            UInt32(NSOpenGLPFAMinimumPolicy),
            UInt32(0)
        ]

        let glPixelFormat = NSOpenGLPixelFormat(attributes:pixelFormatAttributes)
        super.init(frame:frame,pixelFormat:glPixelFormat)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func updateScene(){

        self.needsDisplay = true
        self.update()
    }

    // draw something...
    override func drawRect(dirtyRect:NSRect) {

        Clear()

        //draw something

        //blend for PointSmooth
        glBlendFunc(GLenum(GL_SRC_ALPHA), GLenum(GL_ONE_MINUS_SRC_ALPHA))
        glEnable(GLenum(GL_BLEND))
        glEnable(GLenum(GL_POINT_SMOOTH))

        glPointSize(PointSize)
        glColor4f(0.0, 1.0, 0.5,0.4)

        for i in 0..<1000{
            //generate random values
            let x:GLfloat = GLfloat(arc4random_uniform(UInt32(self.width)))
            let y:GLfloat = GLfloat(arc4random_uniform(UInt32(self.height)))

            //draw
            glBegin(GLenum(GL_POINTS))
            glVertex2f(x, y)
            glEnd()
        }

        glDisable(GLenum(GL_BLEND))

        //flush
        glFlush()

    }

    //when window is reshaped
    override func reshape() {

        glViewport(0,0,self.width,self.height)
        glMatrixMode(GLenum(GL_PROJECTION))
        glLoadIdentity()
        glOrtho(0.0,GLdouble(width),0.0,GLdouble(height),-1.0,1.0);
        glMatrixMode(GLenum(GL_MODELVIEW))
        glLoadIdentity()
    }

    //clear buffer
    func Clear()
    {
        glClearColor(0.0, 0.5, 1.0, 1.0)
        glClear(GLenum(GL_COLOR_BUFFER_BIT) | GLenum(GL_DEPTH_BUFFER_BIT));

        glViewport(0,0,width,height)
        glMatrixMode(GLenum(GL_PROJECTION))
        glLoadIdentity()
        glOrtho(0.0,GLdouble(width),0.0,GLdouble(height),-1.0,1.0);
        glMatrixMode(GLenum(GL_MODELVIEW))
        glLoadIdentity()


    }

}

上から順に説明すると、


init?(frame:NSRect, PointSize:GLfloat) {

        //set size of this view
        self.width = GLsizei(frame.size.width)
        self.height = GLsizei(frame.size.height)
        self.PointSize = PointSize

        //setup pixelFormatAttributes
        let pixelFormatAttributes:[NSOpenGLPixelFormatAttribute] = [
            UInt32(NSOpenGLPFAAccelerated),
            UInt32(NSOpenGLPFADoubleBuffer),
            UInt32(NSOpenGLPFADepthSize), UInt32(32),
            UInt32(NSOpenGLPFAColorSize), UInt32(48),
            UInt32(NSOpenGLPFAAlphaSize), UInt32(16),
            UInt32(NSOpenGLPFAMultisample),
            UInt32(NSOpenGLPFASampleBuffers), UInt32(1),
            UInt32(NSOpenGLPFASamples), UInt32(4),
            UInt32(NSOpenGLPFAMinimumPolicy),
            UInt32(0)
        ]

        let glPixelFormat = NSOpenGLPixelFormat(attributes:pixelFormatAttributes)
        super.init(frame:frame,pixelFormat:glPixelFormat)
 }

     required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

初期化部分です。必須項目で、ここでNSOpenGLViewのpixelFormatを設定します。DoubleBufferやDepthtestなどなど
また、OpenGLDrawContentsに渡す引数もここで設定します。
上では、GLfloat型のPointSizeを渡しています。
この引数は、ViewControllerから渡すようになっています。


func updateScene(){

        self.needsDisplay = true
        self.update()
    }


描写のアップデートを司る関数です。
NSOpenGLViewの描写をアップデートするために、update()が用意されていますが、これを単純に呼ぶだけでは描写は更新されません。
その前に、needsDisplayをtrueにする必要があります。


override func drawRect(dirtyRect:NSRect) {
//ここに描写内容を書く
}

drawRectはロード時、windowのreshape時、また、update()がコールされた時に実行されます。
OpenGLの描写内容はここで書きます。


    //when window is reshaped
    override func reshape() {

        glViewport(0,0,self.width,self.height)
        glMatrixMode(GLenum(GL_PROJECTION))
        glLoadIdentity()
        glOrtho(0.0,GLdouble(width),0.0,GLdouble(height),-1.0,1.0);
        glMatrixMode(GLenum(GL_MODELVIEW))
        glLoadIdentity()
    }

windowがreshapeされた時に呼ばれます。
windowサイズが変更された時のため、glViewportをwindowサイズに合わせています。

これくらいでしょうか。

実装

ViewControllerに戻りまして、
OpenGLDrawContentsを生成し、NSOpenGLViewに追加するには以下のようにします。

    OpenGLContentsDraw(frame:CGRectMake(0,0,self.view.bounds.width,self.view.bounds.height),
                         PointSize:GLfloat(20.0))

    self.OpenGLView.addSubview(self.OpenGLContents)

アップデート

マウスで画面をクリックすると、描写をアップデートするようにしました。


    override func mouseDown(theEvent: NSEvent) {
        self.OpenGLContents.updateScene()
   }

今回はテストのために、マウスダウンで描写を更新するようにしましたが、アニメーションを作る際は、NSTimerを使うようです。

参考

10
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
9