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.