Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Classes/EAGLView.m
/* |
File: EAGLView.m |
Abstract: 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. |
Version: 1.9 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2010 Apple Inc. All Rights Reserved. |
*/ |
#import <QuartzCore/QuartzCore.h> |
#import <OpenGLES/EAGLDrawable.h> |
#import "EAGLView.h" |
@interface EAGLView (EAGLViewPrivate) |
- (BOOL)createFramebuffer; |
- (void)destroyFramebuffer; |
@end |
@interface EAGLView (EAGLViewSprite) |
- (void)setupView; |
@end |
@implementation EAGLView |
@synthesize animating; |
@dynamic animationFrameInterval; |
// You must implement this |
+ (Class) layerClass |
{ |
return [CAEAGLLayer class]; |
} |
//The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder: |
- (id)initWithCoder:(NSCoder*)coder |
{ |
if((self = [super initWithCoder:coder])) { |
// Get the layer |
CAEAGLLayer *eaglLayer = (CAEAGLLayer*) self.layer; |
eaglLayer.opaque = YES; |
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: |
[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; |
context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; |
if(!context || ![EAGLContext setCurrentContext:context] || ![self createFramebuffer]) { |
[self release]; |
return nil; |
} |
animating = FALSE; |
displayLinkSupported = FALSE; |
animationFrameInterval = 1; |
displayLink = nil; |
animationTimer = nil; |
// A system version of 3.1 or greater is required to use CADisplayLink. The NSTimer |
// class is used as fallback when it isn't available. |
NSString *reqSysVer = @"3.1"; |
NSString *currSysVer = [[UIDevice currentDevice] systemVersion]; |
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) |
displayLinkSupported = TRUE; |
[self setupView]; |
[self drawView]; |
} |
return self; |
} |
- (void)layoutSubviews |
{ |
[EAGLContext setCurrentContext:context]; |
[self destroyFramebuffer]; |
[self createFramebuffer]; |
[self drawView]; |
} |
- (BOOL)createFramebuffer |
{ |
glGenFramebuffersOES(1, &viewFramebuffer); |
glGenRenderbuffersOES(1, &viewRenderbuffer); |
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); |
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); |
[context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(id<EAGLDrawable>)self.layer]; |
glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); |
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); |
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); |
if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) { |
NSLog(@"failed to make complete framebuffer object %x", glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES)); |
return NO; |
} |
return YES; |
} |
- (void)destroyFramebuffer |
{ |
glDeleteFramebuffersOES(1, &viewFramebuffer); |
viewFramebuffer = 0; |
glDeleteRenderbuffersOES(1, &viewRenderbuffer); |
viewRenderbuffer = 0; |
if(depthRenderbuffer) { |
glDeleteRenderbuffersOES(1, &depthRenderbuffer); |
depthRenderbuffer = 0; |
} |
} |
- (NSInteger) animationFrameInterval |
{ |
return animationFrameInterval; |
} |
- (void) setAnimationFrameInterval:(NSInteger)frameInterval |
{ |
// Frame interval defines how many display frames must pass between each time the |
// display link fires. The display link will only fire 30 times a second when the |
// frame internal is two on a display that refreshes 60 times a second. The default |
// frame interval setting of one will fire 60 times a second when the display refreshes |
// at 60 times a second. A frame interval setting of less than one results in undefined |
// behavior. |
if (frameInterval >= 1) |
{ |
animationFrameInterval = frameInterval; |
if (animating) |
{ |
[self stopAnimation]; |
[self startAnimation]; |
} |
} |
} |
- (void) startAnimation |
{ |
if (!animating) |
{ |
if (displayLinkSupported) |
{ |
// CADisplayLink is API new to iPhone SDK 3.1. Compiling against earlier versions will result in a warning, but can be dismissed |
// if the system version runtime check for CADisplayLink exists in -initWithCoder:. The runtime check ensures this code will |
// not be called in system versions earlier than 3.1. |
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(drawView)]; |
[displayLink setFrameInterval:animationFrameInterval]; |
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; |
} |
else |
animationTimer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)((1.0 / 60.0) * animationFrameInterval) target:self selector:@selector(drawView) userInfo:nil repeats:TRUE]; |
animating = TRUE; |
} |
} |
- (void)stopAnimation |
{ |
if (animating) |
{ |
if (displayLinkSupported) |
{ |
[displayLink invalidate]; |
displayLink = nil; |
} |
else |
{ |
[animationTimer invalidate]; |
animationTimer = nil; |
} |
animating = FALSE; |
} |
} |
// Sets up an array of values to use as the sprite vertices. |
const GLfloat spriteVertices[] = { |
-0.5f, -0.5f, |
0.5f, -0.5f, |
-0.5f, 0.5f, |
0.5f, 0.5f, |
}; |
// Sets up an array of values for the texture coordinates. |
const GLshort spriteTexcoords[] = { |
0, 0, |
1, 0, |
0, 1, |
1, 1, |
}; |
- (void)setupView |
{ |
CGImageRef spriteImage; |
CGContextRef spriteContext; |
GLubyte *spriteData; |
size_t width, height; |
// Sets up matrices and transforms for OpenGL ES |
glViewport(0, 0, backingWidth, backingHeight); |
glMatrixMode(GL_PROJECTION); |
glLoadIdentity(); |
glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, -1.0f, 1.0f); |
glMatrixMode(GL_MODELVIEW); |
// Clears the view with black |
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
// Sets up pointers and enables states needed for using vertex arrays and textures |
glVertexPointer(2, GL_FLOAT, 0, spriteVertices); |
glEnableClientState(GL_VERTEX_ARRAY); |
glTexCoordPointer(2, GL_SHORT, 0, spriteTexcoords); |
glEnableClientState(GL_TEXTURE_COORD_ARRAY); |
// Creates a Core Graphics image from an image file |
spriteImage = [UIImage imageNamed:@"Sprite.png"].CGImage; |
// Get the width and height of the image |
width = CGImageGetWidth(spriteImage); |
height = CGImageGetHeight(spriteImage); |
// 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. |
if(spriteImage) { |
// Allocated memory needed for the bitmap context |
spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte)); |
// Uses the bitmap creation function provided by the Core Graphics framework. |
spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width * 4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); |
// After you create the context, you can draw the sprite image to the context. |
CGContextDrawImage(spriteContext, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), spriteImage); |
// You don't need the context at this point, so you need to release it to avoid memory leaks. |
CGContextRelease(spriteContext); |
// Use OpenGL ES to generate a name for the texture. |
glGenTextures(1, &spriteTexture); |
// Bind the texture name. |
glBindTexture(GL_TEXTURE_2D, spriteTexture); |
// 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); |
// 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, spriteData); |
// Release the image data |
free(spriteData); |
// Enable use of the texture |
glEnable(GL_TEXTURE_2D); |
// Set a blending function to use |
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
// Enable blending |
glEnable(GL_BLEND); |
} |
} |
// Updates the OpenGL view when the timer fires |
- (void)drawView |
{ |
// Make sure that you are drawing to the current context |
[EAGLContext setCurrentContext:context]; |
glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); |
glRotatef(3.0f, 0.0f, 0.0f, 1.0f); |
glClear(GL_COLOR_BUFFER_BIT); |
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); |
[context presentRenderbuffer:GL_RENDERBUFFER_OES]; |
} |
// Release resources when they are no longer needed. |
- (void)dealloc |
{ |
if([EAGLContext currentContext] == context) { |
[EAGLContext setCurrentContext:nil]; |
} |
[context release]; |
context = nil; |
[super dealloc]; |
} |
@end |
Copyright © 2010 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2010-08-18