iphone OpenGL ESを使いこなしたい。
このジャンルで情報を発信しているのは「強火で進め」というブログ。
そこで進められていたOpenGL本を買ってみた。
その本の情報はWeb上に公開されているけれど、
本の良さっていうのは
「たしかこのへんに。。。」と手や目の感覚が覚えていることだ。
とか言い聞かせつつ、買った。
iphone dev centerで公開されているsample code(GLPaint)を理解することで、
OpenGL ESを理解していきたい。
このサンプルを実行すると、ペイントができる。
シャッフルすると、画面を初期化する。
クラスはたったの4つ。
まずは全てのまとめ役、AppController。
AppController.h●
#import "PaintingView.h" #import "SoundEffect.h" //CLASS INTERFACES: @interface AppController : NSObject <UIAccelerometerDelegate> //UIAccelerometerDelegateプロトコルを実装したクラス { UIWindow *window; //ベース PaintingView *drawingView; UIAccelerationValue myAccelerometer[3];//重力計の値 SoundEffect *erasingSound; SoundEffect *selectSound; CFTimeInterval lastTime;//? } @end
- 本家Appleがこの作り方をしている。AppControllerで全体をまとめるべき
- xibファイルが見つからない。なくてもiphoneアプリは作れる
- 加速度計はローパス(重力計測)、ハイパス(重力以外計測)の2つの使い方
- このクラスにUIAccelerometerDelegateして、一定間隔ごとに加速度計で測る
- AppControllerでは、自作クラスの.hしかimportしない
- 変数の直前に*を付けるのが、Apple流
AppController.m
#import "AppController.h" //CONSTANTS: #define kPaletteHeight 30 //k? #define kPaletteSize 5 #define kAccelerometerFrequency 25 //Hz #define kFilteringFactor 0.1 #define kMinEraseInterval 0.5 #define kEraseAccelerationThreshold 2.0 // Padding for margins #define kLeftMargin 10.0 #define kTopMargin 10.0 #define kRightMargin 10.0 //FUNCTIONS:● static void HSL2RGB(float h, float s, float l, float* outR, float* outG, float* outB) // ポインタによって書きかえられる変数の名前=outほにゃらら { float temp1,temp2; float temp[3]; int i; // Check for saturation. If there isn't any just return the luminance value for each, which results in gray. if(s == 0.0) { if(outR) *outR = l; if(outG) *outG = l; if(outB) *outB = l; return; } // Test for luminance and compute temporary values based on luminance and saturation if(l < 0.5) temp2 = l * (1.0 + s); else temp2 = l + s - l * s; temp1 = 2.0 * l - temp2; // Compute intermediate values based on hue temp[0] = h + 1.0 / 3.0; temp[1] = h; temp[2] = h - 1.0 / 3.0; for(i = 0; i < 3; ++i) { // Adjust the range if(temp[i] < 0.0) temp[i] += 1.0; if(temp[i] > 1.0) temp[i] -= 1.0; if(6.0 * temp[i] < 1.0) temp[i] = temp1 + (temp2 - temp1) * 6.0 * temp[i]; else { if(2.0 * temp[i] < 1.0) temp[i] = temp2; else { if(3.0 * temp[i] < 2.0) temp[i] = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - temp[i]) * 6.0; else temp[i] = temp1; } } } // Assign temporary values to R, G, B if(outR) *outR = temp[0]; if(outG) *outG = temp[1]; if(outB) *outB = temp[2]; } //CLASS IMPLEMENTATIONS: @implementation AppController - (void) applicationDidFinishLaunching:(UIApplication*)application // アプリケーションが起動し終わった時に呼ばれる、初期化 { CGRect rect = [[UIScreen mainScreen] applicationFrame]; //矩形(x,y,w,h) CGFloat components[3]; // 色 //Create a full-screen window window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // (CGRect)[[UIScreen mainScreen] bounds]] // initWithFrame(CGRect) [window setBackgroundColor:[UIColor blackColor]]; //Create the OpenGL drawing view and add it to the window drawingView = [[PaintingView alloc] initWithFrame:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height)]; // - kPaletteHeight [window addSubview:drawingView]; //ベースのwindowにサブビューを載せる // Create a segmented control so that the user can choose the brush color. UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems: // 「UISegmentedControlはindexが取り出せる」//どの色を使うか決定するためのコントローラ? [NSArray arrayWithObjects: // 画像配列 [UIImage imageNamed:@"Red.png"], // [UIImage imageWithContentsOfFile:aImagePath]; [UIImage imageNamed:@"Yellow.png"], // imageNamedは速いけれどキャッシュしちゃう [UIImage imageNamed:@"Green.png"], //つまりreleaseしてもメモリ割当量が減らない [UIImage imageNamed:@"Blue.png"], [UIImage imageNamed:@"Purple.png"], nil]]; //最後にnilってあたりが、イイ! // Compute a rectangle that is positioned correctly for the segmented control you'll use as a brush color palette CGRect frame = CGRectMake(rect.origin.x + kLeftMargin, rect.size.height - kPaletteHeight - kTopMargin, rect.size.width - (kLeftMargin + kRightMargin), kPaletteHeight); CGRectMake (x, y, width, height) segmentedControl.frame = frame; // 色セレクタの大きさが決定 // When the user chooses a color, the method changeBrushColor: is called. // ブラシ色 // この関数、何度も呼ばれますよね?(でもここはapplicationDidFinishLaunching...) [segmentedControl addTarget:self action:@selector(changeBrushColor:) forControlEvents:UIControlEventValueChanged]; // ▲ segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar; // Make sure the color of the color complements the black background segmentedControl.tintColor = [UIColor darkGrayColor]; // Set the third color (index values start at 0) segmentedControl.selectedSegmentIndex = 2; // Add the control to the window [window addSubview:segmentedControl]; // ベースのwindowにサブビューを重ねる // Now that the control is added, you can release it [segmentedControl release]; // もうリリース!? // Define a starting color HSL2RGB((CGFloat) 2.0 / (CGFloat)kPaletteSize, kSaturation, kLuminosity, &components[0], &components[1], &components[2]); // Set the color using OpenGL glColor4f(components[0], components[1], components[2], kBrushOpacity); //Show the window [window makeKeyAndVisible]; // お決まりの初期化?▲ // Look in the Info.plist file and you'll see the status bar is hidden // Set the style to black so it matches the background of the application [application setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:NO]; // Now show the status bar, but animate to the style. [application setStatusBarHidden:NO animated:YES]; // これでステータスバー隠す!?(hiddenがnoなら隠さないのでは?) //Configure and enable the accelerometer● // お決まりの加速度計初期化 [[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / kAccelerometerFrequency)]; [[UIAccelerometer sharedAccelerometer] setDelegate:self]; //Load the sounds● NSBundle *mainBundle = [NSBundle mainBundle]; // リソースファイルにアクセスするにはNSBundleクラスを使います erasingSound = [[SoundEffect alloc] initWithContentsOfFile:[mainBundle pathForResource:@"Erase" ofType:@"caf"]]; selectSound = [[SoundEffect alloc] initWithContentsOfFile:[mainBundle pathForResource:@"Select" ofType:@"caf"]]; } // Release resources when they are no longer needed,● - (void) dealloc { [selectSound release]; //使ったクラスを解放 [erasingSound release]; [drawingView release]; [window release]; [super dealloc]; //親を解放 } // Change the brush color● - (void)changeBrushColor:(id)sender { CGFloat components[3]; //色の三要素(0~1) [selectSound play]; //選択音 //Set the new brush color HSL2RGB((CGFloat)[sender selectedSegmentIndex] / (CGFloat)kPaletteSize, kSaturation, kLuminosity, &components[0], &components[1], &components[2]); glColor4f(components[0], components[1], components[2], kBrushOpacity); //アルファ付きの描画色をセット // 描画色の影響はどこに? } // Called when the accelerometer detects motion; plays the erase sound and redraws the view if the motion is over a threshold.● - (void) accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration // お決まりの行 // 1秒に何十回も呼ばれる関数 { UIAccelerationValue length,x,y,z; //Use a basic high-pass filter to remove the influence of the gravity▲ myAccelerometer[0] = acceleration.x * kFilteringFactor + myAccelerometer[0] * (1.0 - kFilteringFactor); myAccelerometer[1] = acceleration.y * kFilteringFactor + myAccelerometer[1] * (1.0 - kFilteringFactor); myAccelerometer[2] = acceleration.z * kFilteringFactor + myAccelerometer[2] * (1.0 - kFilteringFactor); // Compute values for the three axes of the acceleromater x = acceleration.x - myAccelerometer[0]; y = acceleration.y - myAccelerometer[0]; z = acceleration.z - myAccelerometer[0]; //Compute the intensity of the current acceleration //もしもシャッフルが強かった時 ● length = sqrt(x * x + y * y + z * z); // If above a given threshold, play the erase sounds and erase the drawing view if((length >= kEraseAccelerationThreshold) && (CFAbsoluteTimeGetCurrent() > lastTime + kMinEraseInterval)) { [erasingSound play]; [drawingView erase]; lastTime = CFAbsoluteTimeGetCurrent(); //現在の時間 } } @end
- applicationDidFinishLaunching
- (void) accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration 毎回か速度計を使って行う処理をここに書く
-
-
-
-
-
-
PaintingView.h●
#import "EAGLView.h" //!! //継承します //CONSTANTS: #define kBrushOpacity (1.0 / 3.0) #define kBrushPixelStep 3 #define kBrushScale 2 #define kLuminosity 0.75 #define kSaturation 1.0 //CLASS INTERFACES: @interface PaintingView : EAGLView { GLuint brushTexture; //OPEN GLの世界でのint? GLuint drawingTexture; GLuint drawingFramebuffer; CGPoint location; // (x, y) CGPoint previousLocation; Boolean firstTouch; } @property(nonatomic, readwrite) CGPoint location; // readwriteはデフォルト @property(nonatomic, readwrite) CGPoint previousLocation; // プロパティは必要なぶんだけ - (void) erase; @end
PaintingView.m
#import "PaintingView.h" //CLASS IMPLEMENTATIONS: @implementation PaintingView @synthesize location; //@property(.h) -> @synthesize(.m) @synthesize previousLocation; - (id) initWithFrame:(CGRect)frame { NSMutableArray* recordedPaths; //可変長配列 CGImageRef brushImage; // UIImage -> CGImageRef -> 画像情報にアクセス CGContextRef brushContext; GLubyte *brushData; size_t width, height; if((self = [super initWithFrame:frame pixelFormat:GL_RGB565_OES depthFormat:0 preserveBackbuffer:YES])) { [self setCurrentContext]; // Create a texture from an image // First create a UIImage object from the data in a image file, and then extract the Core Graphics image brushImage = [UIImage imageNamed:@"Particle.png"].CGImage; // Get the width and height of the image width = CGImageGetWidth(brushImage); height = CGImageGetHeight(brushImage); // Texture dimensions must be a power of 2. If you write an application that allows users to supply an image, // you'll want to add code that checks the dimensions and takes appropriate action if they are not a power of 2. // Make sure the image exists if(brushImage) { // Allocate memory needed for the bitmap context brushData = (GLubyte *) malloc(width * height * 4); // Use the bitmatp creation function provided by the Core Graphics framework. brushContext = CGBitmapContextCreate(brushData, width, width, 8, width * 4, CGImageGetColorSpace(brushImage), kCGImageAlphaPremultipliedLast); // After you create the context, you can draw the image to the context. CGContextDrawImage(brushContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), brushImage); // You don't need the context at this point, so you need to release it to avoid memory leaks. CGContextRelease(brushContext); // Use OpenGL ES to generate a name for the texture. glGenTextures(1, &brushTexture); // Bind the texture name. glBindTexture(GL_TEXTURE_2D, brushTexture); // Specify a 2D texture image, providing the a pointer to the image data in memory glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, brushData); // Release the image data; it's no longer needed free(brushData); // Set the texture parameters to use a minifying filter and a linear filer (weighted average) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Enable use of the texture glEnable(GL_TEXTURE_2D); // Set a blending function to use glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Enable blending glEnable(GL_BLEND); } //Set up OpenGL states glDisable(GL_DITHER); glMatrixMode(GL_PROJECTION); glOrthof(0, frame.size.width, 0, frame.size.height, -1, 1); glMatrixMode(GL_MODELVIEW); glEnable(GL_TEXTURE_2D); glEnableClientState(GL_VERTEX_ARRAY); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_POINT_SPRITE_OES); glTexEnvf(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, GL_TRUE); glPointSize(width / kBrushScale); //Make sure to start with a cleared buffer [self erase]; //Playback recorded path, which is "Shake Me" recordedPaths = [NSMutableArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Recording" ofType:@"data"]]; if([recordedPaths count]) [self performSelector:@selector(playback:) withObject:recordedPaths afterDelay:0.2]; } return self; } // Releases resources when they are not longer needed. - (void) dealloc { glDeleteFramebuffersOES(1, &drawingFramebuffer); glDeleteTextures(1, &drawingTexture); [super dealloc]; } // Erases the screen - (void) erase { //Clear the buffer glClear(GL_COLOR_BUFFER_BIT); //Display the buffer [self swapBuffers]; // self = PaintingView // バックバッファとフロントバッファを切り替える } // Drawings a line onscreen based on where the user touches // 注目ポイント! - (void) renderLineFromPoint:(CGPoint)start toPoint:(CGPoint)end { static GLfloat* vertexBuffer = NULL; //static?→ずっと値を保持 // 一時配列だけれど、x,yの情報を保存 static NSUInteger vertexMax = 64; NSUInteger vertexCount = 0, count, i; //Allocate vertex array buffer // 最初は呼ばれる if(vertexBuffer == NULL) vertexBuffer = malloc(vertexMax * 2 * sizeof(GLfloat)); // Add points to the buffer so there are drawing points every X pixels count = MAX( ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) / kBrushPixelStep) , 1 ); for(i = 0; i < count; ++i) { if(vertexCount == vertexMax) { vertexMax = 2 * vertexMax; vertexBuffer = realloc(vertexBuffer, vertexMax * 2 * sizeof(GLfloat)); // mallocで確保した領域を、reallocで拡大縮小する } vertexBuffer[2 * vertexCount + 0] = start.x + (end.x - start.x) * ((GLfloat)i / (GLfloat)count); vertexBuffer[2 * vertexCount + 1] = start.y + (end.y - start.y) * ((GLfloat)i / (GLfloat)count); vertexCount += 1; } //Render the vertex array glVertexPointer(2, GL_FLOAT, 0, vertexBuffer); glDrawArrays(GL_POINTS, 0, vertexCount); // Display the buffer [self swapBuffers]; // バックバッファとフロントバッファを切り替える } // Reads previously recorded points and draws them onscreen. This is the Shake Me message that appears when the application launches. - (void) playback:(NSMutableArray*)recordedPaths { NSData* data = [recordedPaths objectAtIndex:0]; CGPoint* point = (CGPoint*)[data bytes]; NSUInteger count = [data length] / sizeof(CGPoint), i; //Render the current path for(i = 0; i < count - 1; ++i, ++point) [self renderLineFromPoint:*point toPoint:*(point + 1)]; //Render the next path after a short delay [recordedPaths removeObjectAtIndex:0]; if([recordedPaths count]) [self performSelector:@selector(playback:) withObject:recordedPaths afterDelay:0.01]; } // Handles the start of a touch - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { CGRect bounds = [self bounds]; UITouch* touch = [[event touchesForView:self] anyObject]; firstTouch = YES; //Convert touch point from UIView referential to OpenGL one (upside-down flip) location = [touch locationInView:self]; location.y = bounds.size.height - location.y; } // Handles the continuation of a touch. - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { CGRect bounds = [self bounds]; // 矩形(x,y,w,h) UITouch* touch = [[event touchesForView:self] anyObject]; //Convert touch point from UIView referential to OpenGL one (upside-down flip) if (firstTouch) { firstTouch = NO; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; } else { location = [touch locationInView:self]; location.y = bounds.size.height - location.y; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; } // Render the stroke [self renderLineFromPoint:previousLocation toPoint:location]; } // Handles the end of a touch event when the touch is a tap. - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { CGRect bounds = [self bounds]; UITouch* touch = [[event touchesForView:self] anyObject]; if (firstTouch) { firstTouch = NO; previousLocation = [touch previousLocationInView:self]; previousLocation.y = bounds.size.height - previousLocation.y; [self renderLineFromPoint:previousLocation toPoint:location]; } } // Handles the end of a touch event.▲ - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event // NSSet(集合演算) { // If appropriate, add code necessary to save the state of the application. // This application is not saving state. } @end
SoundEffect.h●
#import <UIKit/UIKit.h> //user interface #import <AudioToolbox/AudioServices.h> //sound @interface SoundEffect : NSObject { SystemSoundID _soundID; // why underbar?→インスタンス変数 } + (id)soundEffectWithContentsOfFile:(NSString *)aPath; // alloc - (id)initWithContentsOfFile:(NSString *)path; // init - (void)play; @end
SoundEffect.m
#import "SoundEffect.h" @implementation SoundEffect // Creates a sound effect object from the specified sound file + (id)soundEffectWithContentsOfFile:(NSString *)aPath { if (aPath) { return [[[SoundEffect alloc] initWithContentsOfFile:aPath] autorelease]; // alloc, init, autorelease } return nil; } // Initializes a sound effect object with the contents of the specified sound file - (id)initWithContentsOfFile:(NSString *)path { self = [super init]; // 親クラスNSObjectのinit? // init関数の中で、親のinitも呼ぶ // Gets the file located at the specified path. if (self != nil) { NSURL *aFileURL = [NSURL fileURLWithPath:path isDirectory:NO]; // If the file exists, calls Core Audio to create a system sound ID. if (aFileURL != nil) { SystemSoundID aSoundID; OSStatus error = AudioServicesCreateSystemSoundID((CFURLRef)aFileURL, &aSoundID); if (error == kAudioServicesNoError) { // success _soundID = aSoundID; } else { NSLog(@"Error %d loading sound at path: %@", error, path); [self release], self = nil; } } else { NSLog(@"NSURL is nil for path: %@", path); [self release], self = nil; } } return self; } //解放● -(void)dealloc { AudioServicesDisposeSystemSoundID(_soundID); [super dealloc]; } //演奏● -(void)play { AudioServicesPlaySystemSound(_soundID); } @end
- 音源の解放AudioServicesDisposeSystemSoundID
- 音源の演奏AudioServicesPlaySystemSound
EAGLView.h
#import <UIKit/UIKit.h> #import <OpenGLES/EAGL.h> #import <OpenGLES/ES1/gl.h> #import <OpenGLES/ES1/glext.h> //CLASSES: @class EAGLView; //PROTOCOLS: @protocol EAGLViewDelegate <NSObject> - (void) didResizeEAGLSurfaceForView:(EAGLView*)view; //Called whenever the EAGL surface has been resized @end //CLASS INTERFACE: /* This class wraps the CAEAGLLayer from CoreAnimation into a convenient UIView subclass. The view content is basically an EAGL surface you render your OpenGL scene into. Note that setting the view non-opaque will only work if the EAGL surface has an alpha channel. */ @interface EAGLView : UIView { @private GLuint _format; //プライベートなクラス変数にはアンダーバー? GLuint _depthFormat; BOOL _autoresize; EAGLContext *_context; GLuint _framebuffer; GLuint _renderbuffer; GLuint _depthBuffer; CGSize _size; BOOL _hasBeenCurrent; id<EAGLViewDelegate> _delegate; } - (id) initWithFrame:(CGRect)frame; //These also set the current context - (id) initWithFrame:(CGRect)frame pixelFormat:(GLuint)format; - (id) initWithFrame:(CGRect)frame pixelFormat:(GLuint)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained; @property(readonly) GLuint framebuffer; @property(readonly) GLuint pixelFormat; @property(readonly) GLuint depthFormat; @property(readonly) EAGLContext *context; @property BOOL autoresizesSurface; //NO by default - Set to YES to have the EAGL surface automatically resized when the view bounds change, otherwise the EAGL surface contents is rendered scaled @property(readonly, nonatomic) CGSize surfaceSize; @property(assign) id<EAGLViewDelegate> delegate; - (void) setCurrentContext; - (BOOL) isCurrentContext; - (void) clearCurrentContext; - (void) swapBuffers; //This also checks the current OpenGL error and logs an error if needed - (CGPoint) convertPointFromViewToSurface:(CGPoint)point; - (CGRect) convertRectFromViewToSurface:(CGRect)rect; @end
EAGLView.m
#import <QuartzCore/QuartzCore.h> #import <OpenGLES/EAGLDrawable.h> #import "EAGLView.h" //CLASS IMPLEMENTATIONS: @implementation EAGLView @synthesize delegate=_delegate, autoresizesSurface=_autoresize, surfaceSize=_size, framebuffer = _framebuffer, pixelFormat = _format, depthFormat = _depthFormat, context = _context; + (Class) layerClass { return [CAEAGLLayer class]; } - (BOOL) _createSurface { CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer]; CGSize newSize; GLuint oldRenderbuffer; GLuint oldFramebuffer; if(![EAGLContext setCurrentContext:_context]) { return NO; } newSize = [eaglLayer bounds].size; newSize.width = roundf(newSize.width); newSize.height = roundf(newSize.height); glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint *) &oldRenderbuffer); glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, (GLint *) &oldFramebuffer); glGenRenderbuffersOES(1, &_renderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, _renderbuffer); if(![_context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)eaglLayer]) { glDeleteRenderbuffersOES(1, &_renderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_BINDING_OES, oldRenderbuffer); return NO; } glGenFramebuffersOES(1, &_framebuffer); glBindFramebufferOES(GL_FRAMEBUFFER_OES, _framebuffer); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, _renderbuffer); if (_depthFormat) { glGenRenderbuffersOES(1, &_depthBuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, _depthBuffer); glRenderbufferStorageOES(GL_RENDERBUFFER_OES, _depthFormat, newSize.width, newSize.height); glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, _depthBuffer); } _size = newSize; if(!_hasBeenCurrent) { glViewport(0, 0, newSize.width, newSize.height); glScissor(0, 0, newSize.width, newSize.height); _hasBeenCurrent = YES; } else { glBindFramebufferOES(GL_FRAMEBUFFER_OES, oldFramebuffer); } glBindRenderbufferOES(GL_RENDERBUFFER_OES, oldRenderbuffer); // Error handling here [_delegate didResizeEAGLSurfaceForView:self]; return YES; } - (void) _destroySurface { EAGLContext *oldContext = [EAGLContext currentContext]; if (oldContext != _context) [EAGLContext setCurrentContext:_context]; if(_depthFormat) { glDeleteRenderbuffersOES(1, &_depthBuffer); _depthBuffer = 0; } glDeleteRenderbuffersOES(1, &_renderbuffer); _renderbuffer = 0; glDeleteFramebuffersOES(1, &_framebuffer); _framebuffer = 0; if (oldContext != _context) [EAGLContext setCurrentContext:oldContext]; } - (id) initWithFrame:(CGRect)frame { return [self initWithFrame:frame pixelFormat:GL_RGB565_OES depthFormat:0 preserveBackbuffer:NO]; } - (id) initWithFrame:(CGRect)frame pixelFormat:(GLuint)format { return [self initWithFrame:frame pixelFormat:format depthFormat:0 preserveBackbuffer:NO]; } - (id) initWithFrame:(CGRect)frame pixelFormat:(GLuint)format depthFormat:(GLuint)depth preserveBackbuffer:(BOOL)retained { if((self = [super initWithFrame:frame])) { CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer]; eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; _format = format; _depthFormat = depth; _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; if(_context == nil) { [self release]; return nil; } if(![self _createSurface]) { [self release]; return nil; } } return self; } - (void) dealloc { [self _destroySurface]; [_context release]; _context = nil; [super dealloc]; } - (void) layoutSubviews { CGRect bounds = [self bounds]; if(_autoresize && ((roundf(bounds.size.width) != _size.width) || (roundf(bounds.size.height) != _size.height))) { [self _destroySurface]; [self _createSurface]; } } - (void) setAutoresizesEAGLSurface:(BOOL)autoresizesEAGLSurface; { _autoresize = autoresizesEAGLSurface; if(_autoresize) [self layoutSubviews]; } - (void) setCurrentContext { if(![EAGLContext setCurrentContext:_context]) { printf("Failed to set current context %p in %s\n", _context, __FUNCTION__); } } - (BOOL) isCurrentContext { return ([EAGLContext currentContext] == _context ? YES : NO); } - (void) clearCurrentContext { if(![EAGLContext setCurrentContext:nil]) printf("Failed to clear current context in %s\n", __FUNCTION__); } - (void) swapBuffers { EAGLContext *oldContext = [EAGLContext currentContext]; GLuint oldRenderbuffer; if(oldContext != _context) [EAGLContext setCurrentContext:_context]; // CHECK_GL_ERROR(); glGetIntegerv(GL_RENDERBUFFER_BINDING_OES, (GLint *) &oldRenderbuffer); glBindRenderbufferOES(GL_RENDERBUFFER_OES, _renderbuffer); if(![_context presentRenderbuffer:GL_RENDERBUFFER_OES]) printf("Failed to swap renderbuffer in %s\n", __FUNCTION__); if(oldContext != _context) [EAGLContext setCurrentContext:oldContext]; } - (CGPoint) convertPointFromViewToSurface:(CGPoint)point { CGRect bounds = [self bounds]; return CGPointMake((point.x - bounds.origin.x) / bounds.size.width * _size.width, (point.y - bounds.origin.y) / bounds.size.height * _size.height); } - (CGRect) convertRectFromViewToSurface:(CGRect)rect { CGRect bounds = [self bounds]; return CGRectMake((rect.origin.x - bounds.origin.x) / bounds.size.width * _size.width, (rect.origin.y - bounds.origin.y) / bounds.size.height * _size.height, rect.size.width / bounds.size.width * _size.width, rect.size.height / bounds.size.height * _size.height); } @end
#import <UIKit/UIKit.h> int main(int argc, char *argv[]) { NSAutoreleasePool* pool = [NSAutoreleasePool new]; UIApplicationMain(argc, argv, nil, @"AppController"); [pool release]; return 0; }
Copyright (C) 2008 Apple Inc. All Rights Reserved.