• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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