1 /******************************************************************************
2
3 @File OGLES2HelloAPI_Windows.cpp
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
14 2.0, use it for drawing a triangle and terminate it.
15
16 ******************************************************************************/
17 #include <stdio.h>
18 #include <windows.h>
19 #include <TCHAR.h>
20
21 #include <EGL/egl.h>
22 #include <GLES2/gl2.h>
23
24 /******************************************************************************
25 Defines
26 ******************************************************************************/
27 // Windows class name to register
28 #define WINDOW_CLASS _T("PVRShellClass")
29
30 // Width and height of the window
31 #define WINDOW_WIDTH 640
32 #define WINDOW_HEIGHT 480
33
34 // Index to bind the attributes to vertex shaders
35 #define VERTEX_ARRAY 0
36
37 /******************************************************************************
38 Global variables
39 ******************************************************************************/
40
41 // Variable set in the message handler to finish the demo
42 bool g_bDemoDone = false;
43
44 /*!****************************************************************************
45 @Function WndProc
46 @Input hWnd Handle to the window
47 @Input message Specifies the message
48 @Input wParam Additional message information
49 @Input lParam Additional message information
50 @Return LRESULT result code to OS
51 @Description Processes messages for the main window
52 ******************************************************************************/
WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)53 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
54 {
55 switch (message)
56 {
57 /*
58 Here we are handling 2 system messages: screen saving and monitor power.
59 They are especially relevent on mobile devices.
60 */
61 case WM_SYSCOMMAND:
62 {
63 switch (wParam)
64 {
65 case SC_SCREENSAVE: // Screensaver trying to start ?
66 case SC_MONITORPOWER: // Monitor trying to enter powersave ?
67 return 0; // Prevent this from happening
68 }
69 break;
70 }
71
72 // Handles the close message when a user clicks the quit icon of the window
73 case WM_CLOSE:
74 g_bDemoDone = true;
75 PostQuitMessage(0);
76 return 1;
77
78 default:
79 break;
80 }
81
82 // Calls the default window procedure for messages we did not handle
83 return DefWindowProc(hWnd, message, wParam, lParam);
84 }
85
86 /*!****************************************************************************
87 @Function TestEGLError
88 @Input pszLocation location in the program where the error took
89 place. ie: function name
90 @Return bool true if no EGL error was detected
91 @Description Tests for an EGL error and prints it
92 ******************************************************************************/
TestEGLError(HWND hWnd,const char * pszLocation)93 bool TestEGLError(HWND hWnd, const char* pszLocation)
94 {
95 /*
96 eglGetError returns the last error that has happened using egl,
97 not the status of the last called function. The user has to
98 check after every single egl call or at least once every frame.
99 */
100 EGLint iErr = eglGetError();
101 if (iErr != EGL_SUCCESS)
102 {
103 TCHAR pszStr[256];
104 _stprintf(pszStr, _T("%s failed (%d).\n"), pszLocation, iErr);
105 MessageBox(hWnd, pszStr, _T("Error"), MB_OK|MB_ICONEXCLAMATION);
106 return false;
107 }
108
109 return true;
110 }
111
112 /*!****************************************************************************
113 @Function Cleanup
114 @Input eglDisplay Handle to the EGL display
115 @Input hWnd Handle to the window
116 @Input hDC Handle to the device context
117 @Return int result code to OS
118 @Description Clean up before program termination on error or normal exit
119 ******************************************************************************/
Cleanup(EGLDisplay eglDisplay,HWND hWnd,HDC hDC)120 int Cleanup(EGLDisplay eglDisplay, HWND hWnd, HDC hDC)
121 {
122 /*
123 eglTerminate takes care of destroying any context or surface created
124 with this display, so we don't need to call eglDestroySurface or
125 eglDestroyContext here.
126 */
127
128 eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
129 eglTerminate(eglDisplay);
130
131 /*
132 Destroy the eglWindow.
133 This is platform specific and delegated to a separate function.
134 */
135
136 // Release the device context
137 if (hDC) ReleaseDC(hWnd, hDC);
138
139 return 0;
140 }
141
142 /*!****************************************************************************
143 @Function WinMain
144 @Input hInstance Application instance from OS
145 @Input hPrevInstance Always NULL
146 @Input lpCmdLine command line from OS
147 @Input nCmdShow Specifies how the window is to be shown
148 @Return int result code to OS
149 @Description Main function of the program
150 ******************************************************************************/
WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,TCHAR * lpCmdLine,int nCmdShow)151 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, TCHAR *lpCmdLine, int nCmdShow)
152 {
153 // Windows variables
154 HWND hWnd = 0;
155 HDC hDC = 0;
156
157 // EGL variables
158 EGLDisplay eglDisplay = 0;
159 EGLConfig eglConfig = 0;
160 EGLSurface eglSurface = 0;
161 EGLContext eglContext = 0;
162 EGLNativeWindowType eglWindow = 0;
163
164 // Matrix used for projection model view (PMVMatrix)
165 float pfIdentity[] =
166 {
167 1.0f,0.0f,0.0f,0.0f,
168 0.0f,1.0f,0.0f,0.0f,
169 0.0f,0.0f,1.0f,0.0f,
170 0.0f,0.0f,0.0f,1.0f
171 };
172
173 // Fragment and vertex shaders code
174 const char* pszFragShader = "\
175 void main (void)\
176 {\
177 gl_FragColor = vec4(1.0, 1.0, 0.66 ,1.0);\
178 }";
179 const char* pszVertShader = "\
180 attribute highp vec4 myVertex;\
181 uniform mediump mat4 myPMVMatrix;\
182 void main(void)\
183 {\
184 gl_Position = myPMVMatrix * myVertex;\
185 }";
186
187 /*
188 Step 0 - Create a EGLNativeWindowType that we can use for OpenGL ES output
189 */
190
191 // Register the windows class
192 WNDCLASS sWC;
193 sWC.style = CS_HREDRAW | CS_VREDRAW;
194 sWC.lpfnWndProc = WndProc;
195 sWC.cbClsExtra = 0;
196 sWC.cbWndExtra = 0;
197 sWC.hInstance = hInstance;
198 sWC.hIcon = 0;
199 sWC.hCursor = 0;
200 sWC.lpszMenuName = 0;
201 sWC.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
202 sWC.lpszClassName = WINDOW_CLASS;
203 unsigned int nWidth = WINDOW_WIDTH;
204 unsigned int nHeight = WINDOW_HEIGHT;
205
206 ATOM registerClass = RegisterClass(&sWC);
207 if (!registerClass)
208 {
209 MessageBox(0, _T("Failed to register the window class"), _T("Error"), MB_OK | MB_ICONEXCLAMATION);
210 }
211
212 // Create the eglWindow
213 RECT sRect;
214 SetRect(&sRect, 0, 0, nWidth, nHeight);
215 AdjustWindowRectEx(&sRect, WS_CAPTION | WS_SYSMENU, false, 0);
216 hWnd = CreateWindow( WINDOW_CLASS, _T("HelloAPI"), WS_VISIBLE | WS_SYSMENU,
217 0, 0, nWidth, nHeight, NULL, NULL, hInstance, NULL);
218 eglWindow = hWnd;
219
220 // Get the associated device context
221 hDC = GetDC(hWnd);
222 if (!hDC)
223 {
224 MessageBox(0, _T("Failed to create the device context"), _T("Error"), MB_OK|MB_ICONEXCLAMATION);
225 return Cleanup(eglDisplay, hWnd, hDC);
226 }
227
228 /*
229 Step 1 - Get the default display.
230 EGL uses the concept of a "display" which in most environments
231 corresponds to a single physical screen. Since we usually want
232 to draw to the main screen or only have a single screen to begin
233 with, we let EGL pick the default display.
234 Querying other displays is platform specific.
235 */
236 eglDisplay = eglGetDisplay(hDC);
237
238 if(eglDisplay == EGL_NO_DISPLAY)
239 eglDisplay = eglGetDisplay((EGLNativeDisplayType) EGL_DEFAULT_DISPLAY);
240 /*
241 Step 2 - Initialize EGL.
242 EGL has to be initialized with the display obtained in the
243 previous step. We cannot use other EGL functions except
244 eglGetDisplay and eglGetError before eglInitialize has been
245 called.
246 If we're not interested in the EGL version number we can just
247 pass NULL for the second and third parameters.
248 */
249 EGLint iMajorVersion, iMinorVersion;
250 if (!eglInitialize(eglDisplay, &iMajorVersion, &iMinorVersion))
251 {
252 MessageBox(0, _T("eglInitialize() failed."), _T("Error"), MB_OK|MB_ICONEXCLAMATION);
253 return Cleanup(eglDisplay, hWnd, hDC);
254 }
255
256 /*
257 Step 3 - Make OpenGL ES the current API.
258 EGL provides ways to set up OpenGL ES and OpenVG contexts
259 (and possibly other graphics APIs in the future), so we need
260 to specify the "current API".
261 */
262 eglBindAPI(EGL_OPENGL_ES_API);
263 if (!TestEGLError(hWnd, "eglBindAPI"))
264 {
265 return Cleanup(eglDisplay, hWnd, hDC);
266 }
267
268 /*
269 Step 4 - Specify the required configuration attributes.
270 An EGL "configuration" describes the pixel format and type of
271 surfaces that can be used for drawing.
272 For now we just want to use the default Windows surface,
273 i.e. it will be visible on screen. The list
274 has to contain key/value pairs, terminated with EGL_NONE.
275 */
276 const EGLint pi32ConfigAttribs[] =
277 {
278 EGL_LEVEL, 0,
279 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
280 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
281 EGL_NATIVE_RENDERABLE, EGL_FALSE,
282 EGL_DEPTH_SIZE, EGL_DONT_CARE,
283 EGL_NONE
284 };
285
286 /*
287 Step 5 - Find a config that matches all requirements.
288 eglChooseConfig provides a list of all available configurations
289 that meet or exceed the requirements given as the second
290 argument. In most cases we just want the first config that meets
291 all criteria, so we can limit the number of configs returned to 1.
292 */
293 EGLint iConfigs;
294 if (!eglChooseConfig(eglDisplay, pi32ConfigAttribs, &eglConfig, 1, &iConfigs) || (iConfigs != 1))
295 {
296 MessageBox(0, _T("eglChooseConfig() failed."), _T("Error"), MB_OK|MB_ICONEXCLAMATION);
297 return Cleanup(eglDisplay, hWnd, hDC);
298 }
299
300 /*
301 Step 6 - Create a surface to draw to.
302 Use the config picked in the previous step and the native window
303 handle when available to create a window surface. A window surface
304 is one that will be visible on screen inside the native display (or
305 fullscreen if there is no windowing system).
306 Pixmaps and pbuffers are surfaces which only exist in off-screen
307 memory.
308 */
309 eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, eglWindow, NULL);
310
311 if(eglSurface == EGL_NO_SURFACE)
312 {
313 eglGetError(); // Clear error
314 eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, NULL, NULL);
315 }
316
317 if (!TestEGLError(hWnd, "eglCreateWindowSurface"))
318 {
319 return Cleanup(eglDisplay, hWnd, hDC);
320 }
321
322 /*
323 Step 7 - Create a context.
324 EGL has to create a context for OpenGL ES. Our OpenGL ES resources
325 like textures will only be valid inside this context
326 (or shared contexts)
327 */
328 EGLint ai32ContextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
329 eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, ai32ContextAttribs);
330 if (!TestEGLError(hWnd, "eglCreateContext"))
331 {
332 return Cleanup(eglDisplay, hWnd, hDC);
333 }
334
335 /*
336 Step 8 - Bind the context to the current thread and use our
337 window surface for drawing and reading.
338 Contexts are bound to a thread. This means you don't have to
339 worry about other threads and processes interfering with your
340 OpenGL ES application.
341 We need to specify a surface that will be the target of all
342 subsequent drawing operations, and one that will be the source
343 of read operations. They can be the same surface.
344 */
345 eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
346 if (!TestEGLError(hWnd, "eglMakeCurrent"))
347 {
348 return Cleanup(eglDisplay, hWnd, hDC);
349 }
350
351 /*
352 Step 9 - Draw something with OpenGL ES.
353 At this point everything is initialized and we're ready to use
354 OpenGL ES to draw something on the screen.
355 */
356
357 GLuint uiFragShader, uiVertShader; /* Used to hold the fragment and vertex shader handles */
358 GLuint uiProgramObject; /* Used to hold the program handle (made out of the two previous shaders */
359
360 // Create the fragment shader object
361 uiFragShader = glCreateShader(GL_FRAGMENT_SHADER);
362
363 // Load the source code into it
364 glShaderSource(uiFragShader, 1, (const char**)&pszFragShader, NULL);
365
366 // Compile the source code
367 glCompileShader(uiFragShader);
368
369 // Check if compilation succeeded
370 GLint bShaderCompiled;
371 glGetShaderiv(uiFragShader, GL_COMPILE_STATUS, &bShaderCompiled);
372
373 if (!bShaderCompiled)
374 {
375
376 // An error happened, first retrieve the length of the log message
377 int i32InfoLogLength, i32CharsWritten;
378 glGetShaderiv(uiFragShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
379
380 // Allocate enough space for the message and retrieve it
381 char* pszInfoLog = new char[i32InfoLogLength];
382 glGetShaderInfoLog(uiFragShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
383
384 // Displays the error in a dialog box
385 MessageBox(hWnd, i32InfoLogLength ? pszInfoLog : _T(""), _T("Failed to compile fragment shader"), MB_OK|MB_ICONEXCLAMATION);
386 delete[] pszInfoLog;
387
388 return Cleanup(eglDisplay, hWnd, hDC);
389 }
390
391 // Loads the vertex shader in the same way
392 uiVertShader = glCreateShader(GL_VERTEX_SHADER);
393 glShaderSource(uiVertShader, 1, (const char**)&pszVertShader, NULL);
394 glCompileShader(uiVertShader);
395 glGetShaderiv(uiVertShader, GL_COMPILE_STATUS, &bShaderCompiled);
396 if (!bShaderCompiled)
397 {
398
399 int i32InfoLogLength, i32CharsWritten;
400 glGetShaderiv(uiVertShader, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
401 char* pszInfoLog = new char[i32InfoLogLength];
402 glGetShaderInfoLog(uiVertShader, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
403
404 MessageBox(hWnd, i32InfoLogLength ? pszInfoLog : _T(""), _T("Failed to compile vertex shader"), MB_OK|MB_ICONEXCLAMATION);
405
406 delete[] pszInfoLog;
407
408 return Cleanup(eglDisplay, hWnd, hDC);
409 }
410
411 // Create the shader program
412 uiProgramObject = glCreateProgram();
413
414 // Attach the fragment and vertex shaders to it
415 glAttachShader(uiProgramObject, uiFragShader);
416 glAttachShader(uiProgramObject, uiVertShader);
417
418 // Bind the custom vertex attribute "myVertex" to location VERTEX_ARRAY
419 glBindAttribLocation(uiProgramObject, VERTEX_ARRAY, "myVertex");
420
421 // Link the program
422 glLinkProgram(uiProgramObject);
423
424 // Check if linking succeeded in the same way we checked for compilation success
425 GLint bLinked;
426 glGetProgramiv(uiProgramObject, GL_LINK_STATUS, &bLinked);
427 if (!bLinked)
428 {
429 int i32InfoLogLength, i32CharsWritten;
430 glGetProgramiv(uiProgramObject, GL_INFO_LOG_LENGTH, &i32InfoLogLength);
431 char* pszInfoLog = new char[i32InfoLogLength];
432 glGetProgramInfoLog(uiProgramObject, i32InfoLogLength, &i32CharsWritten, pszInfoLog);
433
434 MessageBox(hWnd, i32InfoLogLength ? pszInfoLog : _T(""), _T("Failed to link program"), MB_OK|MB_ICONEXCLAMATION);
435
436 delete[] pszInfoLog;
437 return Cleanup(eglDisplay, hWnd, hDC);
438 }
439
440 // Actually use the created program
441 glUseProgram(uiProgramObject);
442
443 // Sets the clear color.
444 // The colours are passed per channel (red,green,blue,alpha) as float values from 0.0 to 1.0
445 glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
446
447 // Enable culling
448 glEnable(GL_CULL_FACE);
449
450 // We're going to draw a triangle to the screen so create a vertex buffer object for our triangle
451 GLuint ui32Vbo; // Vertex buffer object handle
452
453 // Interleaved vertex data
454 GLfloat afVertices[] = { -0.4f,-0.4f,0.0f, // Position
455 0.4f ,-0.4f,0.0f,
456 0.0f ,0.4f ,0.0f};
457
458 // Generate the vertex buffer object (VBO)
459 glGenBuffers(1, &ui32Vbo);
460
461 // Bind the VBO so we can fill it with data
462 glBindBuffer(GL_ARRAY_BUFFER, ui32Vbo);
463
464 // Set the buffer's data
465 unsigned int uiSize = 3 * (sizeof(GLfloat) * 3); // Calc afVertices size (3 vertices * stride (3 GLfloats per vertex))
466 glBufferData(GL_ARRAY_BUFFER, uiSize, afVertices, GL_STATIC_DRAW);
467
468 // Draws a triangle for 800 frames
469 for(int i = 0; i < 800; ++i)
470 {
471 // Check if the message handler finished the demo
472 if (g_bDemoDone) break;
473
474 /*
475 Clears the color buffer.
476 glClear() can also be used to clear the depth or stencil buffer
477 (GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT)
478 */
479 glClear(GL_COLOR_BUFFER_BIT);
480
481 /*
482 Bind the projection model view matrix (PMVMatrix) to
483 the associated uniform variable in the shader
484 */
485
486 // First gets the location of that variable in the shader using its name
487 int i32Location = glGetUniformLocation(uiProgramObject, "myPMVMatrix");
488
489 // Then passes the matrix to that variable
490 glUniformMatrix4fv( i32Location, 1, GL_FALSE, pfIdentity);
491
492 /*
493 Enable the custom vertex attribute at index VERTEX_ARRAY.
494 We previously binded that index to the variable in our shader "vec4 MyVertex;"
495 */
496 glEnableVertexAttribArray(VERTEX_ARRAY);
497
498 // Sets the vertex data to this attribute index
499 glVertexAttribPointer(VERTEX_ARRAY, 3, GL_FLOAT, GL_FALSE, 0, 0);
500
501 /*
502 Draws a non-indexed triangle array from the pointers previously given.
503 This function allows the use of other primitive types : triangle strips, lines, ...
504 For indexed geometry, use the function glDrawElements() with an index list.
505 */
506 glDrawArrays(GL_TRIANGLES, 0, 3);
507
508 /*
509 Swap Buffers.
510 Brings to the native display the current render surface.
511 */
512 eglSwapBuffers(eglDisplay, eglSurface);
513 if (!TestEGLError(hWnd, "eglSwapBuffers"))
514 {
515 return Cleanup(eglDisplay, hWnd, hDC);
516 }
517
518 // Managing the window messages
519 MSG msg;
520 PeekMessage(&msg, hWnd, NULL, NULL, PM_REMOVE);
521 TranslateMessage(&msg);
522 DispatchMessage(&msg);
523
524 }
525
526 // Frees the OpenGL handles for the program and the 2 shaders
527 glDeleteProgram(uiProgramObject);
528 glDeleteShader(uiFragShader);
529 glDeleteShader(uiVertShader);
530
531 // Delete the VBO as it is no longer needed
532 glDeleteBuffers(1, &ui32Vbo);
533
534 /*
535 Step 10 - Terminate OpenGL ES and destroy the window (if present).
536 */
537 return Cleanup(eglDisplay, hWnd, hDC);
538 }
539
540 /******************************************************************************
541 End of file (OGLES2HelloAPI_Windows.cpp)
542 ******************************************************************************/
543
544