iOSEntryPoint

Basic Tutorial that shows step-by-step how to initialise OpenGL ES 2.0, use it for drawing a triangle and terminate it.

 /*!*********************************************************************************************************************
 \File         OpenGLESHelloAPI_iOS.cpp
 \Title        OpenGL ES 2.0 HelloAPI Tutorial
 \Author       PowerVR by Imagination, Developer Technology Team
 \Copyright    Copyright (c) Imagination Technologies Limited.
 \brief        Basic Tutorial that shows step-by-step how to initialise OpenGL ES 2.0, use it for drawing a triangle and terminate it.
               Entry Point: main
 ***********************************************************************************************************************/
 
 #include "OpenGLESHelloAPI.h"
 
 #import <QuartzCore/QuartzCore.h>
 #import <UIKit/UIKit.h>
 #import <OpenGLES/EAGL.h>
 #import <OpenGLES/EAGLDrawable.h>
 #import <OpenGLES/ES2/gl.h>
 #import <OpenGLES/ES2/glext.h>
 
 const float	Kfps(120.0);
 
 /*!*********************************************************************************************************************
  Class EAGLView
 ***********************************************************************************************************************/
 @class EAGLView;
 @interface EAGLView : UIView
 {
 @private
 	EAGLContext*        _context;
 	GLuint				m_framebuffer;
 	GLuint				m_renderbuffer;
 	GLuint				m_depthBuffer;
 	
 	NSTimer*			m_timer;			// Timer for rendering our OpenGL ES content.
 	id					m_displayLink;		// Prefer using displaylink, if it is available.
 	
 	BOOL				m_animating;
     BOOL				m_displayLinkSupported;
 
 	OpenGLESAPI* helloAPI;
 }
 
 + (Class) layerClass;
 - (void) renderScene;
 - (void) dealloc;
 - (id)   initWithFrame:(CGRect)frame scale:(CGFloat)scale;
 - (BOOL) createEAGLContext:(CAEAGLLayer*)eaglLayer frame:(CGRect)frame scale:(CGFloat)scale;
 - (BOOL) createRenderbuffer:(CAEAGLLayer*)eaglLayer;
 - (void) deInitializeGLState;
 
 @end
 
 @implementation EAGLView
 
 + (Class) layerClass
 {
 	return [CAEAGLLayer class];
 }
 
 /*!*********************************************************************************************************************
 \return  Whether the function succeeds or not.
 \brief	Initialises EAGL and sets of the context for rendering.
  ***********************************************************************************************************************/
 - (BOOL) createEAGLContext:(CAEAGLLayer*)eaglLayer frame:(CGRect)frame scale:(CGFloat)scale
 {

Create a context.

EAGL has to create what is known as a context for OpenGL ES. The concept of a context is OpenGL ES's way of encapsulating any resources and state. What appear to be "global" functions in OpenGL actually only operate on the current context. A context is required for any operations in OpenGL ES.

Initialise EAGL.

 	[eaglLayer setDrawableProperties: [	NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO],
 									   kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]];
 
 	

Create a context for rendering with OpenGL ES2.

 	_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
 
 	if((!_context) || (![EAGLContext setCurrentContext:_context])){	return FALSE;	}
 

Scale the display appropriately.

 	if([self respondsToSelector:@selector(contentScaleFactor)])
 	{
 		self.contentScaleFactor = scale;
 	}
 	else
 	{
 		self.bounds    = CGRectMake(0.0, 0.0, frame.size.width * scale, frame.size.height * scale);
 		self.transform = CGAffineTransformScale(self.transform, 1 / scale, 1 / scale);
 	}
 	return TRUE;
 }
 
 /*!*********************************************************************************************************************
 \return        Whether the function succeeds or not.
 \brief	Creates a render buffer suitable for rendering to.
  ***********************************************************************************************************************/
 - (BOOL) createRenderbuffer:(CAEAGLLayer*)eaglLayer
 {

Create a render buffer.

iOS requires a render buffer to be created and attached with a frame buffer. Applications on iOS do not render to the 'default' frame buffer (framebuffer 0). Instead, clients are required to create their own frame buffer and render to this instead. The OS can then composite the developer's frame buffer with its own buffers to create the UI.

Create a render buffer.

 	GLuint oldRenderbuffer;
 	glGetIntegerv(GL_RENDERBUFFER_BINDING, (GLint *) &oldRenderbuffer);
 	glGenRenderbuffers(1, &m_renderbuffer);
 	glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
 
 	if(![_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer])
 	{
 		glDeleteRenderbuffers(1, &m_renderbuffer);
 		glBindRenderbuffer(GL_RENDERBUFFER_BINDING, oldRenderbuffer);
 		return FALSE;
 	}
 
 	GLint width, height;
 	glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,  &width);
 	glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
 

Create a frame buffer.

 	glGenFramebuffers(1, &m_framebuffer);
 	glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
 	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
 

Create a depth buffer.

 	glGenRenderbuffers(1, &m_depthBuffer);
 	glBindRenderbuffer(GL_RENDERBUFFER, m_depthBuffer);
 	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
 	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthBuffer);
 

Attach the render buffer to the framebuffer.

 	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE){	return FALSE;	}
 

Set the viewport size to the window size.

 	glViewport(0, 0, width, height);
 
 	return TRUE;
 }
 
 /*!*********************************************************************************************************************
 \param[in]		frame The Frame to initialise with.
 \return        Return self on success or nil.
 \brief	Initialises EAGL and sets of the context for rendering. Also call functions to setup GL resources.
 ***********************************************************************************************************************/
 - (id) initWithFrame:(CGRect)frame scale:(CGFloat)scale
 {
 	if(!(self = [super initWithFrame:frame]))
 		return self;
 
 	CAEAGLLayer* eaglLayer = (CAEAGLLayer*)[self layer];
 	if(![self createEAGLContext:eaglLayer frame:frame scale:scale])
 	{
 		[self release];
 		return nil;
 	}
 	
 	if(![self createRenderbuffer:eaglLayer])
 	{
 		[self release];
 		return nil;
 	}
 	
 	return self;
 }
 
 /*!*********************************************************************************************************************
 \brief	Renders the scene to the framebuffer. Usually called within a loop.
 ***********************************************************************************************************************/
 - (void) renderScene
 {	// Bind the developer's render buffer

As previously mentioned, iOS requires developers to render to their own render buffer, which is later composited by the OS.

First though, take the previously bound render buffer, so it can be reset after rendering.

 	GLuint oldRenderBuffer;
 	glGetIntegerv(GL_RENDERBUFFER_BINDING, (GLint*)&oldRenderBuffer);
 	glBindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
 
 	if(!helloAPI->drawFrame())
     {
         [view stopAnimation];
     }
 
 

Present the display data to the screen.

When rendering to a window surface, OpenGL ES is double buffered. This means that OpenGL ES renders directly to one frame buffer, known as the back buffer, whilst the display reads from another - the front buffer. eglSwapBuffers signals to the windowing system that OpenGL ES 2.0 has finished rendering a scene, and that the display should now draw to the screen from the new data. At the same time, the front buffer is made available for OpenGL ES 2.0 to start rendering to. Essentially, this call swaps the front and back buffers.

 	if(![_context presentRenderbuffer:GL_RENDERBUFFER]){ NSLog(@"Failed to swap renderbuffer.\n");	}
 

Reset the older render buffer.

 	glBindRenderbuffer(GL_RENDERBUFFER, oldRenderBuffer);
 }
 
 /*!*********************************************************************************************************************
 \brief	Releases GL resources previously allocated
 ***********************************************************************************************************************/
 - (void) deInitializeGLState
 {
 	helloAPI->deInitializeGLState();
 	

Release render buffers.

 	EAGLContext *oldContext = [EAGLContext currentContext];
 
 	if (oldContext != _context)
 		[EAGLContext setCurrentContext:_context];
 
 	glDeleteRenderbuffers(1, &m_depthBuffer);
 	m_depthBuffer = 0;
 
 	glDeleteRenderbuffers(1, &m_renderbuffer);
 	m_renderbuffer = 0;
 
 	glDeleteFramebuffers(1, &m_framebuffer);
 
 	m_framebuffer = 0;
 	if (oldContext != _context)
 		[EAGLContext setCurrentContext:oldContext];
 
 	

EAGLContext deinitialisation.

 	[_context release];
 	_context = nil;
 }
 
 /*!*********************************************************************************************************************
 \brief	Class destructor. Calls function to destroy the GL state.
 ***********************************************************************************************************************/
 - (void) dealloc
 {
 	[self deInitializeGLState];
 	[super dealloc];
 }
 
 /*!*********************************************************************************************************************
 \brief	Starts rendering.
 ***********************************************************************************************************************/
 - (void)startAnimation
 {
     if(!m_animating)
     {
         if(m_displayLinkSupported)
         {

CADisplayLink is an API new to iOS SDK 3.1. It's preferred to use it for animation rather than a timer, but fallback to the timer if it is not available.

             m_displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(renderScene)];
             [m_displayLink setFrameInterval:1];		// Fire at 60fps on a 60Hz display
             [m_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
         }
         else
 		{
 			m_timer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / Kfps) target:self selector:@selector(renderScene) userInfo:nil repeats:YES];
 		}
         
         m_animating = TRUE;
     }
 }
 
 /*!*********************************************************************************************************************
 \brief	Stops rendering.
 ***********************************************************************************************************************/
 - (void)stopAnimation
 {
     if(m_animating)
     {
         if(m_displayLinkSupported)
         {
             [m_displayLink invalidate];
             m_displayLink = nil;
         }
         else
         {
             [m_timer invalidate];
             m_timer = nil;
         }
         m_animating = FALSE;
     }
 }
 
 @end
 
 /*!*********************************************************************************************************************
  Class AppController
  ***********************************************************************************************************************/
 @interface AppController : NSObject <UIApplicationDelegate>
 {
 	UIWindow*			window; 	// Our window.
 	EAGLView*			view;		// View.
 }
 @end
 
 @implementation AppController
 
 - (void) applicationDidFinishLaunching:(UIApplication *)application
 {
 

Create GL ES object

     helloAPI = new OpenGLESAPI();
 

Create a fullscreen window that can be used for OpenGL ES output.

 	CGRect frameSize;
 	UIScreen* screen = [UIScreen mainScreen];
 	CGFloat    scale = 1.0;
 	if ([UIScreen instancesRespondToSelector:@selector(scale)])
 	{
 		scale = [screen scale];
 	}
 	CGRect appFrame = [screen bounds];
 	frameSize       = CGRectMake(appFrame.origin.x, appFrame.origin.y, appFrame.size.width, appFrame.size.height);
 
 	window = [[UIWindow alloc] initWithFrame:frameSize];
 	view   = [[EAGLView alloc] initWithFrame:frameSize scale:scale];
         UIViewController* vc = [[UIViewController alloc]initWithNibName:nil bundle:nil];
         window.rootViewController = vc;
 	if(view)
 	{

Add this view to the window and show.

 		[window addSubview: view];
 		[window makeKeyAndVisible];
 		

Setup a timer to redraw the view at a regular interval.

 		[view startAnimation];
 	}
 
 	if(!helloAPI->initializeGLES())
     {
         [view stopAnimation];
     }
 }
 
 - (void) applicationWillResignActive:(UIApplication *)application
 {
 	[view stopAnimation];
 }
 
 - (void) applicationDidBecomeActive:(UIApplication *)application
 {
     [view startAnimation];
 }
 
 - (void) applicationWillTerminate:(UIApplication *)application
 {
 	[view stopAnimation];
 }
 
 - (void) dealloc
 {
     [window release];
     [view release];
     
     [super dealloc];
 }
 
 @end
 
 /*!*********************************************************************************************************************
 \brief	Runs the application.
 ***********************************************************************************************************************/
 int main(int argc, char **argv)
 {
 	NSAutoreleasePool* pool = [NSAutoreleasePool new];
 	UIApplicationMain(argc, argv, nil, @"AppController");
 	[pool release];
 	return 0;
 }