Cocoaサンプル - OpenGLで描画
Cocoaアプリケーションプロジェクトをプロジェクト名"CocoaOpenGL"で作成しておきます。
ライブラリの追加
TARGET>Build Phases>Link Binary With Libraries>"+">OpenGL.frameworkをプロジェクトに追加します。
OpenGLViewの作成
MainMenu.xib>Object LibraryからOpenGL Viewを選択、ウインドウにドラッグドロップ>Windowと同じサイズにする
自作OpenGLViewクラスの追加
NSOpenGLViewクラスを継承したMyOpenGLViewクラスをプロジェクトに追加します。NSOpenGLViewに用意されている次のインターフェースを定義しておきます。initWithFrameが初期化、drawRectが描画処理部分、reshapeはリサイズ時の処理です。
MyOpenGLView.h
#import <AppKit/AppKit.h> @interface MyOpenGLView : NSOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format; - (void)awakeFromNib; - (void)drawRect:(NSRect)rect; - (void)reshape; @end
自作ViewとOpenGLViewを関連付ける
Interface BuilderでOpenGLViewを選択>Identity Inspector>Custom Class>MyOpenGLViewにする。
自作Viewの実装
NSOpenGLViewを使ってインスタンス化した場合は初期化処理でinitWithFrameではなくawakeFromNibが呼び出されますのでこちらに初期化処理を記述しておきます。(ちなみに描画処理部分はdrawRectですが、オブジェクトを描画する関数を分けて実装しています。)
MyOpenGLView.h
#import <AppKit/AppKit.h> @interface MyOpenGLView : NSOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format; - (void)awakeFromNib; - (void)drawRect:(NSRect)rect; - (void)reshape; -(void)drawScene; @end
MyOpenGLView.m
#import "MyOpenGLView.h" #import <OpenGL/gl.h> // #import <OpenGL/glu.h> @implementation MyOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format { self = [super initWithFrame:frameRect]; if (self) { // initialize } return self; } // xibにViewを配置しているときはinitWithFrameが呼ばれないので注意 - (void)awakeFromNib { NSLog(@"initialize"); } - (void)drawRect:(NSRect)rect { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // default background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // View Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Modeling Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); [self drawScene]; glFlush(); } - (void)reshape { int width = [self frame].size.width; int height = [self frame].size.height; // view port transform glViewport(0, 0, width, height); // projection transform glMatrixMode(GL_PROJECTION); glLoadIdentity(); // default } - (void)drawScene { glColor3f(0.7f, 0.2f, 0.2f); glBegin(GL_TRIANGLES); { glVertex3f( 0.0f, 0.0f, 0.0f); glVertex3f( 0.5f, 0.0f, 0.0f); glVertex3f( 0.0f, 0.5f ,0.0f); } glEnd(); } @end
カスタムViewを使う方法
プロジェクトを作成し、OpenGL.frameworkを追加しておきます。
CustomViewの作成
MainMenu.xib>Object LibraryからCustom Viewを選択、ウインドウにドラッグドロップ>Windowと同じサイズにする
自作Viewクラスの追加
今度はNSOpenGLViewクラスを継承したMyCustomViewクラスをプロジェクトに追加します。
MyCustomView.h
@interface MyCustomView : NSOpenGLView { NSOpenGLContext* glContext; } - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format; - (void)awakeFromNib; - (void)prepareOpenGL; - (void)drawRect:(NSRect)rect; - (void)reshape; @end
自作ViewとCustomViewを関連付ける
Interface BuilderでCustomViewを選択>Identity Inspector>Custom Class>MyCustomViewにする。
ここからMyCustomViewを実装します。
初期化
先ほどとちがってInterface BuilderでNSOpenGLViewを作成していないので、xibでNSOpenGLViewがインスタンス化されていません。今回は初期化でinitWithFrameが実行され、逆にawakeFromNibは呼ばれません。初期化処理では、ピクセルフォーマットの設定、レンダリングコンテキストの設定を行います。さらに、初期化処理を実行すると内部でセレクタが呼び出され、prepareOpenGLが自動的に実行されます。ここでOpenGLの初期設定を行います。
描画
drawRectでコンテキストのmakeCurrentContext(コンテキストの有効化)と、flushBuffer(コマンドの実行)を実行します。その間で実際の描画処理を行います。
終了処理
予め用意されているclearOpenGLメソッドを実行してコンテキストを片付けます。
MyCustomView.m
@implementation CustomOpenGLView - (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format { NSLog(@"initWithFrame"); // attributeの宣言 NSOpenGLPixelFormatAttribute attributes[] = { NSOpenGLPFADoubleBuffer, // double buffering NSOpenGLPFAAccelerated , // HW accelaration NSOpenGLPFAStencilSize , 32, // stencil buffer size = 32 bits NSOpenGLPFAColorSize , 32, // color buffer size = 32 bits NSOpenGLPFADepthSize , 32, // depth buffer size = 32bits 0 // dummy }; NSOpenGLPixelFormat* pixelFormat; pixelFormat = [ [ [ NSOpenGLPixelFormat alloc ] initWithAttributes : attributes ] autorelease ]; // ピクセルフォーマット設定 self = [ super initWithFrame : frameRect pixelFormat : pixelFormat ]; // コンテキストを取得して有効にする glContext = [ self openGLContext ]; [ glContext makeCurrentContext ]; return self; } - (void)awakeFromNib { NSLog(@"awakeFromNib");// xibにViewを配置していないので呼ばれない。 } - (void)prepareOpenGL { // 初期化 glEnable~など } - (void)drawRect:(NSRect)rect { [ glContext makeCurrentContext ]; // 描画 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // default background color glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // View Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Modeling Transform glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // draw objects glColor3f(0.7f, 0.2f, 0.2f); glBegin(GL_TRIANGLES); { glVertex3f( 0.0f, 0.0f, 0.0f); glVertex3f( 0.5f, 0.0f, 0.0f); glVertex3f( 0.0f, 0.5f ,0.0f); } glEnd(); glFlush(); [ glContext flushBuffer ]; } - (void)reshape { int width = [self frame].size.width; int height = [self frame].size.height; // view port transform glViewport(0, 0, width, height); // projection transform glMatrixMode(GL_PROJECTION); glLoadIdentity(); // default } -(void)dealloc { [glContext release]; [self clearGLContext]; [super dealloc]; }
アニメーション
タイマー開始と更新処理を行う2つのメソッドを追加します。
@interface CustomOpenGLView : NSOpenGLView { NSOpenGLContext* glContext; NSTimer* timer; } // 略 - (void) startTimer; - (void) updateTimer : (NSTimer*) aTimer; @end
初期化の際にタイマーを開始させます。
- (id)initWithFrame:(NSRect)frameRect pixelFormat:(NSOpenGLPixelFormat*)format { // 略 // .... glContext = [ self openGLContext ]; [ glContext makeCurrentContext ]; [self startTimer]; // タイマー開始 return self; }
タイマー開始する処理の中でセレクタを使って更新処理を直接呼び出します。
- (void) startTimer { timer = [ NSTimer scheduledTimerWithTimeInterval : 0.05 target : self selector : @selector( updateTimer : ) // イベントハンドラを直接実行 userInfo : nil repeats : YES ]; [timer retain]; }
あとはイベントハンドラ内で再描画処理や変数の更新処理を実装します。
- (void) updateTimer : (NSTimer*) aTimer { // 更新処理 // 再描画 [self setNeedsDisplay:YES]; [ self display ]; }
終了時にタイマーを片付ける処理を追加して完成です。
-(void)dealloc { // タイマー片付け [timer invalidate]; [timer release]; [glContext release]; [self clearGLContext]; [super dealloc]; }
その他
初期化のコツ
drawRect内で一度だけ初期化処理を呼び出す手もあります。
- (void)drawRect:(NSRect)rect { if(!isInit) { glMatrixMode(GL_PROJECTION);// など isInit = YES; } // 描画 }