iOS and OpenGL ES Programming – Part 3

< Previous (iOS Programming – Part 2) | (iOS Programming – Part 4) Next >

The next class we are going to analyze is EAGLView. The prefix EAGL means Embedded Apple OpenGL. EAGLView class is the kernel of this application. Its responsability is to provide the OpenGL ES context which shall allow us to render on device’s screen.

EAGLView.h

//
//  EAGLView.h
//  OpenGLES_iPhone
//
//  Created by mmalc Crawford on 11/18/10.
//  Copyright 2010 Apple Inc. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>

@class EAGLContext;

// 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
    // The pixel dimensions of the CAEAGLLayer.
    GLint framebufferWidth;
    GLint framebufferHeight;

    // The OpenGL ES names for the framebuffer and renderbuffer used to render to this view.
    GLuint defaultFramebuffer, colorRenderbuffer;
}

@property (nonatomic, retain) EAGLContext *context;

- (void)setFramebuffer;
- (BOOL)presentFramebuffer;

@end

In the following headers import you may notice that we have ES1 and ES2. ES1 correspond to OpenGL 1.0 while ES2 correspond to OpenGL ES 2.0

#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>

@class EAGLContext; is a forward declaration of the class EAGLContent that should be imported later on someone in the project.

The class EAGLView inherits the class UIView. The UIView class belongs to the UIKit is a framework which provides all classes we needed to construct and manage the application’s user interface. Also, it provides an application object, drawing model, windows, views, and controls for the touch screen interface.

@interface EAGLView : UIView {

 

EAGLView.m

//
//  EAGLView.m
//  OpenGLES_iPhone
//
//  Created by mmalc Crawford on 11/18/10.
//  Copyright 2010 Apple Inc. All rights reserved.
//

#import <QuartzCore/QuartzCore.h>
#import 'EAGLView.h'

@interface EAGLView (PrivateMethods)
- (void)createFramebuffer;
- (void)deleteFramebuffer;
@end

@implementation EAGLView

@synthesize context;

// You must implement this method
+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

//The EAGL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:.
- (id)initWithCoderFrownNSCoder*)coder
{
    self = [super initWithCoder:coder];
	if (self) {
        CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

        eaglLayer.opaque = TRUE;
        eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,
                                        kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
                                        nil];
    }

    return self;
}

- (void)dealloc
{
    [self deleteFramebuffer];
    [context release];
    [super dealloc];
}

- (void)setContextFrownEAGLContext *)newContext
{
    if (context != newContext) {
        [self deleteFramebuffer];

        [context release];
        context = [newContext retain];

        [EAGLContext setCurrentContext:nil];
    }
}

- (void)createFramebuffer
{
    if (context && !defaultFramebuffer) {
        [EAGLContext setCurrentContext:context];

        // Create default framebuffer object.
        glGenFramebuffers(1, &defaultFramebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);

        // Create color render buffer and allocate backing store.
        glGenRenderbuffers(1, &colorRenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
        [context renderbufferStorage:GL_RENDERBUFFER fromDrawableFrownCAEAGLLayer *)self.layer];
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth);
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight);

        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);

        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
            NSLog(@'Failed to make complete framebuffer object %x', glCheckFramebufferStatus(GL_FRAMEBUFFER));
    }
}

- (void)deleteFramebuffer
{
    if (context) {
        [EAGLContext setCurrentContext:context];

        if (defaultFramebuffer) {
            glDeleteFramebuffers(1, &defaultFramebuffer);
            defaultFramebuffer = 0;
        }

        if (colorRenderbuffer) {
            glDeleteRenderbuffers(1, &colorRenderbuffer);
            colorRenderbuffer = 0;
        }
    }
}

- (void)setFramebuffer
{
    if (context) {
        [EAGLContext setCurrentContext:context];

        if (!defaultFramebuffer)
            [self createFramebuffer];

        glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);

        glViewport(0, 0, framebufferWidth, framebufferHeight);
    }
}

- (BOOL)presentFramebuffer
{
    BOOL success = FALSE;

    if (context) {
        [EAGLContext setCurrentContext:context];

        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);

        success = [context presentRenderbuffer:GL_RENDERBUFFER];
    }

    return success;
}

- (void)layoutSubviews
{
    // The framebuffer will be re-created at the beginning of the next setFramebuffer method call.
    [self deleteFramebuffer];
}

@end

As you may notice we are importing QuazCore.

#import <QuartzCore/QuartzCore.h>

The QuazCore is a framework which provide us with access to Core Animation and other effects. The Core Animation provide us a layer which allow OpenGL commands to render with optimal performance.

In Objective-C there is not thing such as private method. However, that doesn’t mean that you cannot specify that something should be considerate private.

@interface EAGLView (PrivateMethods)
- (void)createFramebuffer;
- (void)deleteFramebuffer;
@end

Some people considerate this a hack created especially for Objective-C. Objective-C is purely dynamic. This means that the moment of method dispatch the exact same dynamic method resolution point as every other method dispatch. What does this means? This means that at runtime, every method implementation has the same exposure. Every method implementation of the APIs provided at runtime will work with methods and selectors equally the same across all methods. However, indicating that something is private and shouldn’t be used outside is a way to enforce some discipline from the programmer. As you may see, createFrameBuffer and deleteFrameBuffer must be considerate private and being used as such.

The following line indicate the compiler to create the getter and setter for context:

@synthesize context;

Next, we declare the method layerClass. This method shall override the layerClass method inherit from the UIView class.

+ (Class)layerClass
{
    return [CAEAGLLayer class];
}

This method must return the class CAEAGLLayer because this class is a wrapper for a Core Animation surface. CAEAGL means Core Animation Embedded Apple OpenGL. The CAEAGLLayer class provide us with support for drawing OpenGL content.

The initWithCoder method will be the first method to be called.

- (id)initWithCoderFrownNSCoder*)coder
{
    self = [super initWithCoder:coder];
	if (self) {
        CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;

        eaglLayer.opaque = TRUE;
        eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                        [NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking,
                                        kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
                                        nil];
    }

    return self;
}

In this method, the parent’s initWithCoder method is called first. if the object returned match then we assign it to the self property. Later, we point with eagleLayer pointer to the object’s CAEAGLLayer.

The property opaque of the object’s pointed by eaglLayer is set to TRUE. This means that we are not going to allow the rendering to be transparent. This will provide us with a good performance since allowing CAEAGLLayer object to be transparent would require a lot of blending to the content behind the layer.

Next, we are setting drawableProperties. This property allow us to provide a dictionary of objects and keys. They specify when the drawable surface will retain its contents after displaying them. Plus it will set the internal color buffer format which will be used for the drawable surface. This may seems complicated but it is not. Lets brake it down so we can understand.

NSDictionary is a class which declares the programmatic interface to objects. These objects will be a manager of  immutable associations of keys and values. dictionaryWithObjectsAndKeys is a class method which belongs to NSDictionary class. dictionaryWithObjectAndKeys method shall creates a dictionary which containing entries constructed from the specified set of values and keys and then return it. In order to return us this dictionary, we need to provide some arguments to dictionaryWithObjectsAndKeys. The first value is [NSNumber numberWithBool:FALSE], numberwithBool is a class method from NSNumber class. Since the its argument is set to FALSE, numberWithBool will create an NSNumber object containing a given value (FALSE) and treating this value as a BOOL.Then it will return us this NSNumber Object which will be used as the first parameter of dictionaryWithObjectsAndKeys. This first parameter is actually the first value that we will add to this new dictionary. kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, are other values that are going to be added. kEAGLDrawablePropertyRetainedBacking is the first key and it is set to ‘false’. This key, kEAGLDrawablePropertyRetainedBacking, determine whether the surface retains whatever is drawn on it which means the layer won’t autorefresh its contents.  kEAGLColorFormatRGBA8 indicates that we are setting the template to a 16 bit RGB with Alpha. kEAGLDrawablePropertyColorFormat sets the internal color buffer for the layer. nil is used to indicate that we null-terminated the list of alternating values and keys.

Finally, initWithCoder method return the object self.

(To be continued…)

< Previous (iOS Programming – Part 2) | (iOS Programming – Part 4) Next >

References

  • http://developer.apple.com/library/IOS/#documentation/UIKit/Reference/UIKit_Framework/_index.html
  • Daley, Michael. Learning iOS Game Programming. Boston, MA: Pearson Education, 2010
  • http://developer.apple.com/library/ios/#DOCUMENTATION/QuartzCore/Reference/CAEAGLLayer_Class/CAEGLLayer/CAEGLLayer.html
  • http://developer.apple.com/library/mac/#documentation/cocoa/reference/foundation/classes/nsdictionary_class/Reference/Reference.html
  • http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsnumber_Class/Reference/Reference.html
  • http://www.visualnewt.com/OpenGL/learning_iphones_opengl_es/part_ii_-_the_iphone_opengl.html
  • http://developer.apple.com/library/ios/#DOCUMENTATION/iPhone/Reference/EAGLDrawable_Ref/EAGLDrawable/EAGLDrawable.html

Notification

These examples are provided for educational purposes. Using this code is under your own responsibility and risk. The code and information is given ‘as is’. I do not take responsibilities of how they are used.

© 2011, Alejandro G. Carlstein Ramos Mejia. All rights reserved.

Share

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.

*

Click to Insert Smiley

SmileBig SmileGrinLaughFrownBig FrownCryNeutralWinkKissRazzChicCoolAngryReally AngryConfusedQuestionThinkingPainShockYesNoLOLSillyBeautyLashesCuteShyBlushKissedIn LoveDroolGiggleSnickerHeh!SmirkWiltWeepIDKStruggleSide FrownDazedHypnotizedSweatEek!Roll EyesSarcasmDisdainSmugMoney MouthFoot in MouthShut MouthQuietShameBeat UpMeanEvil GrinGrit TeethShoutPissed OffReally PissedMad RazzDrunken RazzSickYawnSleepyDanceClapJumpHandshakeHigh FiveHug LeftHug RightKiss BlowKissingByeGo AwayCall MeOn the PhoneSecretMeetingWavingStopTime OutTalk to the HandLoserLyingDOH!Fingers CrossedWaitingSuspenseTremblePrayWorshipStarvingEatVictoryCurseAlienAngelClownCowboyCyclopsDevilDoctorFemale FighterMale FighterMohawkMusicNerdPartyPirateSkywalkerSnowmanSoldierVampireZombie KillerGhostSkeletonBunnyCatCat 2ChickChickenChicken 2CowCow 2DogDog 2DuckGoatHippoKoalaLionMonkeyMonkey 2MousePandaPigPig 2SheepSheep 2ReindeerSnailTigerTurtleBeerDrinkLiquorCoffeeCakePizzaWatermelonBowlPlateCanFemaleMaleHeartBroken HeartRoseDead RosePeaceYin YangUS FlagMoonStarSunCloudyRainThunderUmbrellaRainbowMusic NoteAirplaneCarIslandAnnouncebrbMailCellPhoneCameraFilmTVClockLampSearchCoinsComputerConsolePresentSoccerCloverPumpkinBombHammerKnifeHandcuffsPillPoopCigarette