1 /******************************************************************************************************************************************* 2 3 @File OGLES2HelloAPI_OSX.mm 4 5 @Title OpenGL ES 2.0 HelloAPI Tutorial 6 7 @Version 8 9 @Copyright Copyright (c) Imagination Technologies Limited. 10 11 @Platform 12 13 @Description Basic Tutorial that shows step-by-step how to initialize OpenGL ES 2.0, use it for drawing a triangle and terminate it. 14 Entry Point: main 15 16 *******************************************************************************************************************************************/ 17 /******************************************************************************************************************************************* 18 Include Files 19 *******************************************************************************************************************************************/ 20 #include <EGL/egl.h> 21 #include <GLES2/gl2.h> 22 #import <AppKit/NSApplication.h> 23 #import <AppKit/NSWindow.h> 24 #import <AppKit/NSScreen.h> 25 #import <AppKit/NSView.h> 26 27 /******************************************************************************************************************************************* 28 Defines 29 *******************************************************************************************************************************************/ 30 // Index to bind the attributes to vertex shaders 31 #define VERTEX_ARRAY 0 32 #define KFPS 120.0 33 34 // Width and height of the window 35 #define WIDTH 800 36 #define HEIGHT 600 37 38 /*!***************************************************************************************************************************************** 39 Class AppController 40 *******************************************************************************************************************************************/ 41 @class AppController; 42 43 @interface AppController : NSObject <NSApplicationDelegate> 44 { 45 @private 46 NSTimer* m_timer; // timer for rendering our OpenGL content 47 NSWindow* m_window; // Our window 48 NSView* m_view; // Our view 49 50 // Shaders 51 GLuint m_fragShader; 52 GLuint m_vertexShader; 53 GLuint m_program; 54 55 // Vertex buffer objects 56 GLuint m_vertexBuffer; 57 58 // EGL variables 59 EGLDisplay m_Display; 60 EGLSurface m_Surface; 61 EGLContext m_Context; 62 } 63 @end 64 65 @implementation AppController 66 67 /*!***************************************************************************************************************************************** 68 @Function testGLError 69 @Input functionLastCalled Function which triggered the error 70 @Return True if no GL error was detected 71 @Description Tests for an GL error and prints it in a message box. 72 *******************************************************************************************************************************************/ 73 - (BOOL) testGLError:(const char *)functionLastCalled 74 { 75 /* glGetError returns the last error that occurred using OpenGL ES, not necessarily the status of the last called function. The user 76 has to check after every single OpenGL ES call or at least once every frame. Usually this would be for debugging only, but for this 77 example it is enabled always 78 */ 79 GLenum lastError = glGetError(); 80 if (lastError != GL_NO_ERROR) 81 { 82 NSLog(@"%s failed (%x).\n", functionLastCalled, lastError); 83 return FALSE; 84 } 85 86 return TRUE; 87 } 88 89 /*!***************************************************************************************************************************************** 90 @Function testEGLError 91 @Input functionLastCalled Function which triggered the error 92 @Return True if no EGL error was detected 93 @Description Tests for an EGL error and prints it. 94 *******************************************************************************************************************************************/ 95 - (BOOL) testEGLError:(const char *)functionLastCalled 96 { 97 /* eglGetError returns the last error that occurred using EGL, not necessarily the status of the last called function. The user has to 98 check after every single EGL call or at least once every frame. Usually this would be for debugging only, but for this example 99 it is enabled always. 100 */ 101 EGLint lastError = eglGetError(); 102 if (lastError != EGL_SUCCESS) 103 { 104 NSLog(@"%s failed (%x).\n", functionLastCalled, lastError); 105 return FALSE; 106 } 107 108 return TRUE; 109 } 110 111 /*!***************************************************************************************************************************************** 112 @Function createEGLDisplay 113 @Output eglDisplay EGLDisplay created 114 @Return Whether the function succeeded or not. 115 @Description Creates an EGLDisplay and initialises it. 116 *******************************************************************************************************************************************/ 117 - (BOOL) createEGLDisplay:(EGLDisplay &)eglDisplay 118 { 119 /* Get an EGL display. 120 EGL uses the concept of a "display" which in most environments corresponds to a single physical screen. After creating a native 121 display for a given windowing system, EGL can use this handle to get a corresponding EGLDisplay handle to it for use in rendering. 122 Should this fail, EGL is usually able to provide access to a default display. For Null window systems, there is no display so NULL 123 is passed the this function. 124 */ 125 eglDisplay = eglGetDisplay((EGLNativeDisplayType)0); 126 if (eglDisplay == EGL_NO_DISPLAY) 127 { 128 printf("Failed to get an EGLDisplay"); 129 return FALSE; 130 } 131 132 /* Initialize EGL. 133 EGL has to be initialized with the display obtained in the previous step. All EGL functions other than eglGetDisplay 134 and eglGetError need an initialised EGLDisplay. 135 If an application is not interested in the EGL version number it can just pass NULL for the second and third parameters, but they 136 are queried here for illustration purposes. 137 */ 138 EGLint eglMajorVersion, eglMinorVersion; 139 if (!eglInitialize(eglDisplay, &eglMajorVersion, &eglMinorVersion)) 140 { 141 printf("Failed to initialise the EGLDisplay"); 142 return FALSE; 143 } 144 145 return TRUE; 146 } 147 148 /*!***************************************************************************************************************************************** 149 @Function chooseEGLConfig 150 @Output eglConfig The EGLConfig chosen by the function 151 @Input eglDisplay The EGLDisplay used by the application 152 @Return Whether the function succeeded or not. 153 @Description Chooses an appropriate EGLConfig and return it. 154 *******************************************************************************************************************************************/ 155 - (BOOL) chooseEGLConfig:(EGLConfig &)eglConfig fromDisplay:(EGLDisplay)eglDisplay 156 { 157 /* Specify the required configuration attributes. 158 An EGL "configuration" describes the capabilities an application requires and the type of surfaces that can be used for drawing. 159 Each implementation exposes a number of different configurations, and an application needs to describe to EGL what capabilities it 160 requires so that an appropriate one can be chosen. The first step in doing this is to create an attribute list, which is an array 161 of key/value pairs which describe particular capabilities requested. In this application nothing special is required so we can query 162 the minimum of needing it to render to a window, and being OpenGL ES 2.0 capable. 163 */ 164 const EGLint configurationAttributes[] = 165 { 166 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 167 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 168 EGL_NONE 169 }; 170 171 /* Find a suitable EGLConfig 172 eglChooseConfig is provided by EGL to provide an easy way to select an appropriate configuration. It takes in the capabilities 173 specified in the attribute list, and returns a list of available configurations that match or exceed the capabilities requested. 174 Details of all the possible attributes and how they are selected for by this function are available in the EGL reference pages here: 175 http://www.khronos.org/registry/egl/sdk/docs/man/xhtml/eglChooseConfig.html 176 It is also possible to simply get the entire list of configurations and use a custom algorithm to choose a suitable one, as many 177 advanced applications choose to do. For this application however, taking the first EGLConfig that the function returns suits 178 its needs perfectly, so we limit it to returning a single EGLConfig. 179 */ 180 EGLint configsReturned; 181 if (!eglChooseConfig(eglDisplay, configurationAttributes, &eglConfig, 1, &configsReturned) || (configsReturned != 1)) 182 { 183 printf("Failed to choose a suitable config."); 184 return FALSE; 185 } 186 187 return TRUE; 188 } 189 190 /*!***************************************************************************************************************************************** 191 @Function createEGLSurface 192 @Output eglSurface The EGLSurface created 193 @Input eglDisplay The EGLDisplay used by the application 194 @Input eglConfig An EGLConfig chosen by the application 195 @Input view The NSView to render to 196 @Return Whether the function succeeds or not. 197 @Description Creates an EGLSurface for the screen 198 *******************************************************************************************************************************************/ 199 - (BOOL) createEGLSurface:(EGLSurface &)eglSurface fromDisplay:(EGLDisplay)eglDisplay withConfig:(EGLConfig)eglConfig 200 withView:(NSView *)view 201 { 202 /* Create an EGLSurface for rendering. 203 Using a native window created earlier and a suitable eglConfig, a surface is created that can be used to render OpenGL ES calls to. 204 There are three main surface types in EGL, which can all be used in the same way once created but work slightly differently: 205 - Window Surfaces - These are created from a native window and are drawn to the screen. 206 - Pixmap Surfaces - These are created from a native windowing system as well, but are offscreen and are not displayed to the user. 207 - PBuffer Surfaces - These are created directly within EGL, and like Pixmap Surfaces are offscreen and thus not displayed. 208 The offscreen surfaces are useful for non-rendering contexts and in certain other scenarios, but for most applications the main 209 surface used will be a window surface as performed below. For NULL window systems, there are no actual windows, so NULL is passed 210 to this function. 211 */ 212 eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)view, NULL); 213 if (![self testEGLError:"eglCreateWindowSurface"]) 214 { 215 return FALSE; 216 } 217 218 return TRUE; 219 } 220 221 /*!***************************************************************************************************************************************** 222 @Function setupEGLContext 223 @Output eglContext The EGLContext created by this function 224 @Input eglDisplay The EGLDisplay used by the application 225 @Input eglConfig An EGLConfig chosen by the application 226 @Input eglSurface The EGLSurface created by the application 227 @Return Whether the function succeeds or not. 228 @Description Sets up the EGLContext, creating it and then installing it to the current thread. 229 *******************************************************************************************************************************************/ 230 - (BOOL) setupEGLContext:(EGLContext &)eglContext fromDisplay:(EGLDisplay)eglDisplay withConfig:(EGLConfig)eglConfig 231 withSurface:(EGLSurface)eglSurface 232 { 233 /* Make OpenGL ES the current API. 234 EGL needs a way to know that any subsequent EGL calls are going to be affecting OpenGL ES, 235 rather than any other API (such as OpenVG). 236 */ 237 eglBindAPI(EGL_OPENGL_ES_API); 238 if (![self testEGLError:"eglBindAPI"]) 239 { 240 return FALSE; 241 } 242 243 /* Create a context. 244 EGL 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 245 resources and state. What appear to be "global" functions in OpenGL actually only operate on the current context. A context 246 is required for any operations in OpenGL ES. 247 Similar to an EGLConfig, a context takes in a list of attributes specifying some of its capabilities. However in most cases this 248 is limited to just requiring the version of the OpenGL ES context required - In this case, OpenGL ES 2.0. 249 */ 250 EGLint contextAttributes[] = 251 { 252 EGL_CONTEXT_CLIENT_VERSION, 2, 253 EGL_NONE 254 }; 255 256 // Create the context with the context attributes supplied 257 eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, contextAttributes); 258 if (![self testEGLError:"eglCreateContext"]) 259 { 260 return FALSE; 261 } 262 263 /* Bind the context to the current thread. 264 Due to the way OpenGL uses global functions, contexts need to be made current so that any function call can operate on the correct 265 context. Specifically, make current will bind the context to the thread it's called from, and unbind it from any others. To use 266 multiple contexts at the same time, users should use multiple threads and synchronise between them. 267 */ 268 eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); 269 if (![self testEGLError:"eglMakeCurrent"]) 270 { 271 return FALSE; 272 } 273 274 return TRUE; 275 } 276 277 /*!***************************************************************************************************************************************** 278 @Function initialiseBuffer 279 @Output vertexBuffer Handle to a vertex buffer object 280 @Return Whether the function succeeds or not. 281 @Description Initialises shaders, buffers and other state required to begin rendering with OpenGL ES 282 *******************************************************************************************************************************************/ 283 - (BOOL) initialiseBuffer:(GLuint &)vertexBuffer 284 { 285 /* Concept: Vertices 286 When rendering a polygon or model to screen, OpenGL ES has to be told where to draw the object, and more fundamentally what shape 287 it is. The data used to do this is referred to as vertices, points in 3D space which are usually collected into groups of three 288 to render as triangles. Fundamentally, any advanced 3D shape in OpenGL ES is constructed from a series of these vertices - each 289 vertex representing one corner of a polygon. 290 */ 291 292 /* Concept: Buffer Objects 293 To operate on any data, OpenGL first needs to be able to access it. The GPU maintains a separate pool of memory it uses independent 294 of the CPU. Whilst on many embedded systems these are in the same physical memory, the distinction exists so that they can use and 295 allocate memory without having to worry about synchronising with any other processors in the device. 296 To this end, data needs to be uploaded into buffers, which are essentially a reserved bit of memory for the GPU to use. By creating 297 a buffer and giving it some data we can tell the GPU how to render a triangle. 298 */ 299 300 // Vertex data containing the positions of each point of the triangle 301 GLfloat vertexData[] = {-0.4f,-0.4f, 0.0f, // Bottom Left 302 0.4f,-0.4f, 0.0f, // Bottom Right 303 0.0f, 0.4f, 0.0f}; // Top Middle 304 305 // Generate a buffer object 306 glGenBuffers(1, &vertexBuffer); 307 308 // Bind buffer as an vertex buffer so we can fill it with data 309 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); 310 311 /* Set the buffer's size, data and usage 312 Note the last argument - GL_STATIC_DRAW. This tells the driver that we intend to read from the buffer on the GPU, and don't intend 313 to modify the data until we're done with it. 314 */ 315 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW); 316 317 if (![self testGLError:"glBufferData"]) 318 { 319 return FALSE; 320 } 321 322 return TRUE; 323 } 324 325 /*!***************************************************************************************************************************************** 326 @Function initialiseShaders 327 @Output fragmentShader Handle to a fragment shader 328 @Output vertexShader Handle to a vertex shader 329 @Output shaderProgram Handle to a shader program containing the fragment and vertex shader 330 @Return Whether the function succeeds or not. 331 @Description Initialises shaders, buffers and other state required to begin rendering with OpenGL ES 332 *******************************************************************************************************************************************/ 333 -(BOOL) initialiseFragmentShader:(GLuint &)fragmentShader andVertexShader:(GLuint &)vertexShader withProgram:(GLuint &)shaderProgram 334 { 335 /* Concept: Shaders 336 OpenGL ES 2.0 uses what are known as shaders to determine how to draw objects on the screen. Instead of the fixed function 337 pipeline in early OpenGL or OpenGL ES 1.x, users can now programmatically define how vertices are transformed on screen, what 338 data is used where, and how each pixel on the screen is coloured. 339 These shaders are written in GL Shading Language ES: http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf 340 which is usually abbreviated to simply "GLSL ES". 341 Each shader is compiled on-device and then linked into a shader program, which combines a vertex and fragment shader into a form 342 that the OpenGL ES implementation can execute. 343 */ 344 345 /* Concept: Fragment Shaders 346 In a final buffer of image data, each individual point is referred to as a pixel. Fragment shaders are the part of the pipeline 347 which determine how these final pixels are coloured when drawn to the framebuffer. When data is passed through here, the positions 348 of these pixels is already set, all that's left to do is set the final colour based on any defined inputs. 349 The reason these are called "fragment" shaders instead of "pixel" shaders is due to a small technical difference between the two 350 concepts. When you colour a fragment, it may not be the final colour which ends up on screen. This is particularly true when 351 performing blending, where multiple fragments can contribute to the final pixel colour. 352 */ 353 const char* const fragmentShaderSource = "\ 354 void main (void)\ 355 {\ 356 gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\ 357 }"; 358 359 // Create a fragment shader object 360 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 361 362 // Load the source code into it 363 glShaderSource(fragmentShader, 1, (const char**)&fragmentShaderSource, NULL); 364 365 // Compile the source code 366 glCompileShader(fragmentShader); 367 368 // Check that the shader compiled 369 GLint isShaderCompiled; 370 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &isShaderCompiled); 371 if (!isShaderCompiled) 372 { 373 // If an error happened, first retrieve the length of the log message 374 int infoLogLength, charactersWritten; 375 glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &infoLogLength); 376 377 // Allocate enough space for the message and retrieve it 378 char* infoLog = new char[infoLogLength]; 379 glGetShaderInfoLog(fragmentShader, infoLogLength, &charactersWritten, infoLog); 380 381 // Display the error in a dialog box 382 infoLogLength>1 ? printf("%s", infoLog) : printf("Failed to compile fragment shader."); 383 384 delete[] infoLog; 385 return FALSE; 386 } 387 388 /* Concept: Vertex Shaders 389 Vertex shaders primarily exist to allow a developer to express how to orient vertices in 3D space, through transformations like 390 Scaling, Translation or Rotation. Using the same basic layout and structure as a fragment shader, these take in vertex data and 391 output a fully transformed set of positions. Other inputs are also able to be used such as normals or texture coordinates, and can 392 also be transformed and output alongside the position data. 393 */ 394 // Vertex shader code 395 const char* const vertexShaderSource = "\ 396 attribute highp vec4 myVertex;\ 397 uniform mediump mat4 transformationMatrix;\ 398 void main(void)\ 399 {\ 400 gl_Position = transformationMatrix * myVertex;\ 401 }"; 402 403 // Create a vertex shader object 404 vertexShader = glCreateShader(GL_VERTEX_SHADER); 405 406 // Load the source code into the shader 407 glShaderSource(vertexShader, 1, (const char**)&vertexShaderSource, NULL); 408 409 // Compile the shader 410 glCompileShader(vertexShader); 411 412 // Check the shader has compiled 413 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &isShaderCompiled); 414 if (!isShaderCompiled) 415 { 416 // If an error happened, first retrieve the length of the log message 417 int infoLogLength, charactersWritten; 418 glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &infoLogLength); 419 420 // Allocate enough space for the message and retrieve it 421 char* infoLog = new char[infoLogLength]; 422 glGetShaderInfoLog(vertexShader, infoLogLength, &charactersWritten, infoLog); 423 424 // Display the error in a dialog box 425 infoLogLength>1 ? printf("%s", infoLog) : printf("Failed to compile vertex shader."); 426 427 delete[] infoLog; 428 return FALSE; 429 } 430 431 // Create the shader program 432 shaderProgram = glCreateProgram(); 433 434 // Attach the fragment and vertex shaders to it 435 glAttachShader(shaderProgram, fragmentShader); 436 glAttachShader(shaderProgram, vertexShader); 437 438 // Bind the vertex attribute "myVertex" to location VERTEX_ARRAY (0) 439 glBindAttribLocation(shaderProgram, VERTEX_ARRAY, "myVertex"); 440 441 // Link the program 442 glLinkProgram(shaderProgram); 443 444 // Check if linking succeeded in the same way we checked for compilation success 445 GLint isLinked; 446 glGetProgramiv(shaderProgram, GL_LINK_STATUS, &isLinked); 447 if (!isLinked) 448 { 449 // If an error happened, first retrieve the length of the log message 450 int infoLogLength, charactersWritten; 451 glGetProgramiv(shaderProgram, GL_INFO_LOG_LENGTH, &infoLogLength); 452 453 // Allocate enough space for the message and retrieve it 454 char* infoLog = new char[infoLogLength]; 455 glGetProgramInfoLog(shaderProgram, infoLogLength, &charactersWritten, infoLog); 456 457 // Display the error in a dialog box 458 infoLogLength>1 ? printf("%s", infoLog) : printf("Failed to link shader program."); 459 460 delete[] infoLog; 461 return FALSE; 462 } 463 464 /* Use the Program 465 Calling glUseProgram tells OpenGL ES that the application intends to use this program for rendering. Now that it's installed into 466 the current state, any further glDraw* calls will use the shaders contained within it to process scene data. Only one program can 467 be active at once, so in a multi-program application this function would be called in the render loop. Since this application only 468 uses one program it can be installed in the current state and left there. 469 */ 470 glUseProgram(shaderProgram); 471 472 if (![self testGLError:"glUseProgram"]) 473 { 474 return FALSE; 475 } 476 477 return TRUE; 478 } 479 480 /*!***************************************************************************************************************************************** 481 @Function applicationDidFinishLaunching 482 @Input notification 483 @Description Called when the application has finished launching. 484 *******************************************************************************************************************************************/ 485 - (void) applicationDidFinishLaunching:(NSNotification *)notification 486 { 487 // Create our window 488 NSRect frame = NSMakeRect(0,0,WIDTH, HEIGHT); 489 m_window = [[NSWindow alloc] initWithContentRect:frame styleMask:NSMiniaturizableWindowMask | NSTitledWindowMask | NSClosableWindowMask 490 backing:NSBackingStoreBuffered defer:NO]; 491 492 if(!m_window) 493 { 494 NSLog(@"Failed to allocated the window."); 495 [self terminateApp]; 496 } 497 498 [m_window setTitle:@"OGLES2HelloAPI"]; 499 500 // Create our view 501 m_view = [[NSView alloc] initWithFrame:frame]; 502 503 // Now we have a view, add it to our window 504 [m_window setContentView:m_view]; 505 [m_window makeKeyAndOrderFront:nil]; 506 507 // Add an observer so when our window is closed we terminate the app 508 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(terminateApp) name:NSWindowWillCloseNotification 509 object:m_window]; 510 511 EGLConfig config; 512 513 // Create and Initialise an EGLDisplay 514 if(![self createEGLDisplay:m_Display]) 515 { 516 [self terminateApp]; 517 } 518 519 // Choose an EGLConfig for the application, used when setting up the rendering surface and EGLContext 520 if(![self chooseEGLConfig:config fromDisplay:m_Display]) 521 { 522 [self terminateApp]; 523 } 524 525 // Create an EGLSurface for rendering 526 if(![self createEGLSurface:m_Surface fromDisplay:m_Display withConfig:config withView:m_view]) 527 { 528 [self terminateApp]; 529 } 530 531 // Setup the EGL Context from the other EGL constructs created so far, so that the application is ready to submit OpenGL ES commands 532 if(![self setupEGLContext:m_Context fromDisplay:m_Display withConfig:config withSurface:m_Surface]) 533 { 534 [self terminateApp]; 535 } 536 537 // Initialise the vertex data in the application 538 if(![self initialiseBuffer:m_vertexBuffer]) 539 { 540 [self terminateApp]; 541 } 542 543 // Initialise the fragment and vertex shaders used in the application 544 if(![self initialiseFragmentShader:m_fragShader andVertexShader:m_vertexBuffer withProgram:m_program]) 545 { 546 [self terminateApp]; 547 } 548 549 // Setup a timer to redraw the view at a regular interval 550 m_timer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / KFPS) target:self selector:@selector(renderScene) userInfo:nil repeats:YES]; 551 } 552 553 /*!***************************************************************************************************************************************** 554 @Function RenderScene 555 @Input eglDisplay The EGLDisplay used by the application 556 @Input eglSurface The EGLSurface created by the application 557 @Return Whether the function succeeds or not. 558 @Description Renders the scene to the framebuffer. Usually called within a loop. 559 *******************************************************************************************************************************************/ 560 - (BOOL) renderScene 561 { 562 /* Set the clear color 563 At the start of a frame, generally you clear the image to tell OpenGL ES that you're done with whatever was there before and want to 564 draw a new frame. In order to do that however, OpenGL ES needs to know what colour to set in the image's place. glClearColor 565 sets this value as 4 floating point values between 0.0 and 1.0, as the Red, Green, Blue and Alpha channels. Each value represents 566 the intensity of the particular channel, with all 0.0 being transparent black, and all 1.0 being opaque white. Subsequent calls to 567 glClear with the colour bit will clear the frame buffer to this value. 568 The functions glClearDepth and glClearStencil allow an application to do the same with depth and stencil values respectively. 569 */ 570 glClearColor(0.6f, 0.8f, 1.0f, 1.0f); 571 572 /* Clears the color buffer. 573 glClear is used here with the Colour Buffer to clear the colour. It can also be used to clear the depth or stencil buffer using 574 GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT, respectively. 575 */ 576 glClear(GL_COLOR_BUFFER_BIT); 577 578 // Get the location of the transformation matrix in the shader using its name 579 int matrixLocation = glGetUniformLocation(m_program, "transformationMatrix"); 580 581 // Matrix used to specify the orientation of the triangle on screen. 582 const float transformationMatrix[] = 583 { 584 1.0f,0.0f,0.0f,0.0f, 585 0.0f,1.0f,0.0f,0.0f, 586 0.0f,0.0f,1.0f,0.0f, 587 0.0f,0.0f,0.0f,1.0f 588 }; 589 590 // Pass the transformationMatrix to the shader using its location 591 glUniformMatrix4fv( matrixLocation, 1, GL_FALSE, transformationMatrix); 592 if (![self testGLError:"glUniformMatrix4fv"]) 593 { 594 return FALSE; 595 } 596 597 // Enable the user-defined vertex array 598 glEnableVertexAttribArray(VERTEX_ARRAY); 599 600 // Sets the vertex data to this attribute index, with the number of floats in each position 601 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, 0); 602 if (![self testGLError:"glVertexAttribPointer"]) 603 { 604 return FALSE; 605 } 606 607 /* Draw the triangle 608 glDrawArrays is a draw call, and executes the shader program using the vertices and other state set by the user. Draw calls are the 609 functions which tell OpenGL ES when to actually draw something to the framebuffer given the current state. 610 glDrawArrays causes the vertices to be submitted sequentially from the position given by the "first" argument until it has processed 611 "count" vertices. Other draw calls exist, notably glDrawElements which also accepts index data to allow the user to specify that 612 some vertices are accessed multiple times, without copying the vertex multiple times. 613 Others include versions of the above that allow the user to draw the same object multiple times with slightly different data, and 614 a version of glDrawElements which allows a user to restrict the actual indices accessed. 615 */ 616 glDrawArrays(GL_TRIANGLES, 0, 3); 617 if (![self testGLError:"glDrawArrays"]) 618 { 619 return FALSE; 620 } 621 622 /* Present the display data to the screen. 623 When rendering to a Window surface, OpenGL ES is double buffered. This means that OpenGL ES renders directly to one frame buffer, 624 known as the back buffer, whilst the display reads from another - the front buffer. eglSwapBuffers signals to the windowing system 625 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 626 time, the front buffer is made available for OpenGL ES 2.0 to start rendering to. In effect, this call swaps the front and back 627 buffers. 628 */ 629 if (!eglSwapBuffers(m_Display, m_Surface) ) 630 { 631 [self testGLError:"eglSwapBuffers"]; 632 return FALSE; 633 } 634 635 return TRUE; 636 } 637 638 /*!***************************************************************************************************************************************** 639 @Function deInitialiseGLState 640 @Description Releases the resources 641 *******************************************************************************************************************************************/ 642 - (void) deInitialiseGLState 643 { 644 // Frees the OpenGL handles for the program and the 2 shaders 645 glDeleteShader(m_fragShader); 646 glDeleteShader(m_vertexShader); 647 glDeleteProgram(m_program); 648 649 // Delete the VBO as it is no longer needed 650 glDeleteBuffers(1, &m_vertexBuffer); 651 } 652 653 /*!***************************************************************************************************************************************** 654 @Function releaseEGLState 655 @Input eglDisplay The EGLDisplay used by the application 656 @Description Releases all resources allocated by EGL 657 *******************************************************************************************************************************************/ 658 - (void) releaseEGLState:(EGLDisplay)eglDisplay 659 { 660 // To release the resources in the context, first the context has to be released from its binding with the current thread. 661 eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 662 663 // Terminate the display, and any resources associated with it (including the EGLContext) 664 eglTerminate(eglDisplay); 665 } 666 667 /*!***************************************************************************************************************************************** 668 @Function applicationWillTerminate 669 @Description Called when the app is about to terminate. 670 *******************************************************************************************************************************************/ 671 - (void) applicationWillTerminate:(NSNotification *)notification 672 { 673 // Release our timer 674 [m_timer invalidate]; 675 676 [self deInitialiseGLState]; 677 [self releaseEGLState:m_Display]; 678 679 // Release our view and window 680 [m_view release]; 681 m_view = nil; 682 683 [m_window release]; 684 m_window = nil; 685 } 686 687 /*!***************************************************************************************************************************************** 688 @Function terminateApp 689 @Description Attempts to immediately terminate the application. 690 *******************************************************************************************************************************************/ 691 - (void) terminateApp 692 { 693 [NSApp terminate:nil]; 694 } 695 696 @end 697 698 /*!***************************************************************************************************************************************** 699 @Function main 700 @Input argc Number of arguments passed to the application, ignored. 701 @Input argv Command line strings passed to the application, ignored. 702 @Return Result code to send to the Operating System 703 @Description Main function of the program, executes other functions. 704 *******************************************************************************************************************************************/ 705 int main(int argc, char **argv) 706 { 707 return NSApplicationMain(argc, (const char **)argv); 708 } 709 710