| // | |
| // Book: OpenGL(R) ES 2.0 Programming Guide | |
| // Authors: Aaftab Munshi, Dan Ginsburg, Dave Shreiner | |
| // ISBN-10: 0321502795 | |
| // ISBN-13: 9780321502797 | |
| // Publisher: Addison-Wesley Professional | |
| // URLs: http://safari.informit.com/9780321563835 | |
| // http://www.opengles-book.com | |
| // | |
| // ESUtil.c | |
| // | |
| // A utility library for OpenGL ES. This library provides a | |
| // basic common framework for the example applications in the | |
| // OpenGL ES 2.0 Programming Guide. | |
| // | |
| /// | |
| // Includes | |
| // | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #include <sys/time.h> | |
| #include <GLES2/gl2.h> | |
| #include <EGL/egl.h> | |
| #include "esUtil.h" | |
| #include <X11/Xlib.h> | |
| #include <X11/Xatom.h> | |
| #include <X11/Xutil.h> | |
| // X11 related local variables | |
| static Display *x_display = NULL; | |
| /// | |
| // CreateEGLContext() | |
| // | |
| // Creates an EGL rendering context and all associated elements | |
| // | |
| EGLBoolean CreateEGLContext ( EGLNativeWindowType hWnd, EGLDisplay* eglDisplay, | |
| EGLContext* eglContext, EGLSurface* eglSurface, | |
| EGLint attribList[]) | |
| { | |
| EGLint numConfigs; | |
| EGLint majorVersion; | |
| EGLint minorVersion; | |
| EGLDisplay display; | |
| EGLContext context; | |
| EGLSurface surface; | |
| EGLConfig config; | |
| EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE, EGL_NONE }; | |
| // Get Display | |
| display = eglGetDisplay((EGLNativeDisplayType)x_display); | |
| if ( display == EGL_NO_DISPLAY ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| // Initialize EGL | |
| if ( !eglInitialize(display, &majorVersion, &minorVersion) ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| // Get configs | |
| if ( !eglGetConfigs(display, NULL, 0, &numConfigs) ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| // Choose config | |
| if ( !eglChooseConfig(display, attribList, &config, 1, &numConfigs) ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| // Create a surface | |
| surface = eglCreateWindowSurface(display, config, (EGLNativeWindowType)hWnd, NULL); | |
| if ( surface == EGL_NO_SURFACE ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| // Create a GL context | |
| context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs ); | |
| if ( context == EGL_NO_CONTEXT ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| // Make the context current | |
| if ( !eglMakeCurrent(display, surface, surface, context) ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| *eglDisplay = display; | |
| *eglSurface = surface; | |
| *eglContext = context; | |
| return EGL_TRUE; | |
| } | |
| /// | |
| // WinCreate() | |
| // | |
| // This function initialized the native X11 display and window for EGL | |
| // | |
| EGLBoolean WinCreate(ESContext *esContext, const char *title) | |
| { | |
| Window root; | |
| XSetWindowAttributes swa; | |
| XSetWindowAttributes xattr; | |
| Atom wm_state; | |
| XWMHints hints; | |
| XEvent xev; | |
| EGLConfig ecfg; | |
| EGLint num_config; | |
| Window win; | |
| /* | |
| * X11 native display initialization | |
| */ | |
| x_display = XOpenDisplay(NULL); | |
| if ( x_display == NULL ) | |
| { | |
| return EGL_FALSE; | |
| } | |
| root = DefaultRootWindow(x_display); | |
| swa.event_mask = ExposureMask | PointerMotionMask | KeyPressMask; | |
| win = XCreateWindow( | |
| x_display, root, | |
| 0, 0, esContext->width, esContext->height, 0, | |
| CopyFromParent, InputOutput, | |
| CopyFromParent, CWEventMask, | |
| &swa ); | |
| xattr.override_redirect = FALSE; | |
| XChangeWindowAttributes ( x_display, win, CWOverrideRedirect, &xattr ); | |
| hints.input = TRUE; | |
| hints.flags = InputHint; | |
| XSetWMHints(x_display, win, &hints); | |
| // make the window visible on the screen | |
| XMapWindow (x_display, win); | |
| XStoreName (x_display, win, title); | |
| // get identifiers for the provided atom name strings | |
| wm_state = XInternAtom (x_display, "_NET_WM_STATE", FALSE); | |
| memset ( &xev, 0, sizeof(xev) ); | |
| xev.type = ClientMessage; | |
| xev.xclient.window = win; | |
| xev.xclient.message_type = wm_state; | |
| xev.xclient.format = 32; | |
| xev.xclient.data.l[0] = 1; | |
| xev.xclient.data.l[1] = FALSE; | |
| XSendEvent ( | |
| x_display, | |
| DefaultRootWindow ( x_display ), | |
| FALSE, | |
| SubstructureNotifyMask, | |
| &xev ); | |
| esContext->hWnd = (EGLNativeWindowType) win; | |
| return EGL_TRUE; | |
| } | |
| /// | |
| // userInterrupt() | |
| // | |
| // Reads from X11 event loop and interrupt program if there is a keypress, or | |
| // window close action. | |
| // | |
| GLboolean userInterrupt(ESContext *esContext) | |
| { | |
| XEvent xev; | |
| KeySym key; | |
| GLboolean userinterrupt = GL_FALSE; | |
| char text; | |
| // Pump all messages from X server. Keypresses are directed to keyfunc (if defined) | |
| while ( XPending ( x_display ) ) | |
| { | |
| XNextEvent( x_display, &xev ); | |
| if ( xev.type == KeyPress ) | |
| { | |
| if (XLookupString(&xev.xkey,&text,1,&key,0)==1) | |
| { | |
| if (esContext->keyFunc != NULL) | |
| esContext->keyFunc(esContext, text, 0, 0); | |
| } | |
| } | |
| if ( xev.type == DestroyNotify ) | |
| userinterrupt = GL_TRUE; | |
| } | |
| return userinterrupt; | |
| } | |
| ////////////////////////////////////////////////////////////////// | |
| // | |
| // Public Functions | |
| // | |
| // | |
| /// | |
| // esInitContext() | |
| // | |
| // Initialize ES utility context. This must be called before calling any other | |
| // functions. | |
| // | |
| void ESUTIL_API esInitContext ( ESContext *esContext ) | |
| { | |
| if ( esContext != NULL ) | |
| { | |
| memset( esContext, 0, sizeof( ESContext) ); | |
| } | |
| } | |
| /// | |
| // esCreateWindow() | |
| // | |
| // title - name for title bar of window | |
| // width - width of window to create | |
| // height - height of window to create | |
| // flags - bitwise or of window creation flags | |
| // ES_WINDOW_ALPHA - specifies that the framebuffer should have alpha | |
| // ES_WINDOW_DEPTH - specifies that a depth buffer should be created | |
| // ES_WINDOW_STENCIL - specifies that a stencil buffer should be created | |
| // ES_WINDOW_MULTISAMPLE - specifies that a multi-sample buffer should be created | |
| // | |
| GLboolean ESUTIL_API esCreateWindow ( ESContext *esContext, const char* title, GLint width, GLint height, GLuint flags ) | |
| { | |
| EGLint attribList[] = | |
| { | |
| EGL_RED_SIZE, 5, | |
| EGL_GREEN_SIZE, 6, | |
| EGL_BLUE_SIZE, 5, | |
| EGL_ALPHA_SIZE, (flags & ES_WINDOW_ALPHA) ? 8 : EGL_DONT_CARE, | |
| EGL_DEPTH_SIZE, (flags & ES_WINDOW_DEPTH) ? 8 : EGL_DONT_CARE, | |
| EGL_STENCIL_SIZE, (flags & ES_WINDOW_STENCIL) ? 8 : EGL_DONT_CARE, | |
| EGL_SAMPLE_BUFFERS, (flags & ES_WINDOW_MULTISAMPLE) ? 1 : 0, | |
| EGL_NONE | |
| }; | |
| if ( esContext == NULL ) | |
| { | |
| return GL_FALSE; | |
| } | |
| esContext->width = width; | |
| esContext->height = height; | |
| if ( !WinCreate ( esContext, title) ) | |
| { | |
| return GL_FALSE; | |
| } | |
| if ( !CreateEGLContext ( esContext->hWnd, | |
| &esContext->eglDisplay, | |
| &esContext->eglContext, | |
| &esContext->eglSurface, | |
| attribList) ) | |
| { | |
| return GL_FALSE; | |
| } | |
| return GL_TRUE; | |
| } | |
| /// | |
| // esMainLoop() | |
| // | |
| // Start the main loop for the OpenGL ES application | |
| // | |
| void ESUTIL_API esMainLoop ( ESContext *esContext ) | |
| { | |
| struct timeval t1, t2; | |
| struct timezone tz; | |
| float deltatime; | |
| float totaltime = 0.0f; | |
| unsigned int frames = 0; | |
| gettimeofday ( &t1 , &tz ); | |
| // Just one iteration! while(userInterrupt(esContext) == GL_FALSE) | |
| { | |
| gettimeofday(&t2, &tz); | |
| deltatime = (float)(t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec) * 1e-6); | |
| t1 = t2; | |
| if (esContext->updateFunc != NULL) | |
| esContext->updateFunc(esContext, deltatime); | |
| if (esContext->drawFunc != NULL) | |
| esContext->drawFunc(esContext); | |
| eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface); | |
| totaltime += deltatime; | |
| frames++; | |
| if (totaltime > 2.0f) | |
| { | |
| printf("%4d frames rendered in %1.4f seconds -> FPS=%3.4f\n", frames, totaltime, frames/totaltime); | |
| totaltime -= 2.0f; | |
| frames = 0; | |
| } | |
| } | |
| } | |
| /// | |
| // esRegisterDrawFunc() | |
| // | |
| void ESUTIL_API esRegisterDrawFunc ( ESContext *esContext, void (ESCALLBACK *drawFunc) (ESContext* ) ) | |
| { | |
| esContext->drawFunc = drawFunc; | |
| } | |
| /// | |
| // esRegisterUpdateFunc() | |
| // | |
| void ESUTIL_API esRegisterUpdateFunc ( ESContext *esContext, void (ESCALLBACK *updateFunc) ( ESContext*, float ) ) | |
| { | |
| esContext->updateFunc = updateFunc; | |
| } | |
| /// | |
| // esRegisterKeyFunc() | |
| // | |
| void ESUTIL_API esRegisterKeyFunc ( ESContext *esContext, | |
| void (ESCALLBACK *keyFunc) (ESContext*, unsigned char, int, int ) ) | |
| { | |
| esContext->keyFunc = keyFunc; | |
| } | |
| /// | |
| // esLogMessage() | |
| // | |
| // Log an error message to the debug output for the platform | |
| // | |
| void ESUTIL_API esLogMessage ( const char *formatStr, ... ) | |
| { | |
| va_list params; | |
| char buf[BUFSIZ]; | |
| va_start ( params, formatStr ); | |
| vsprintf ( buf, formatStr, params ); | |
| printf ( "%s", buf ); | |
| va_end ( params ); | |
| } | |
| /// | |
| // esLoadTGA() | |
| // | |
| // Loads a 24-bit TGA image from a file. This is probably the simplest TGA loader ever. | |
| // Does not support loading of compressed TGAs nor TGAa with alpha channel. But for the | |
| // sake of the examples, this is sufficient. | |
| // | |
| char* ESUTIL_API esLoadTGA ( char *fileName, int *width, int *height ) | |
| { | |
| char *buffer = NULL; | |
| FILE *f; | |
| unsigned char tgaheader[12]; | |
| unsigned char attributes[6]; | |
| unsigned int imagesize; | |
| f = fopen(fileName, "rb"); | |
| if(f == NULL) return NULL; | |
| if(fread(&tgaheader, sizeof(tgaheader), 1, f) == 0) | |
| { | |
| fclose(f); | |
| return NULL; | |
| } | |
| if(fread(attributes, sizeof(attributes), 1, f) == 0) | |
| { | |
| fclose(f); | |
| return 0; | |
| } | |
| *width = attributes[1] * 256 + attributes[0]; | |
| *height = attributes[3] * 256 + attributes[2]; | |
| imagesize = attributes[4] / 8 * *width * *height; | |
| buffer = malloc(imagesize); | |
| if (buffer == NULL) | |
| { | |
| fclose(f); | |
| return 0; | |
| } | |
| if(fread(buffer, 1, imagesize, f) != imagesize) | |
| { | |
| free(buffer); | |
| return NULL; | |
| } | |
| fclose(f); | |
| return buffer; | |
| } |