outdated: 43.FreeType Fonts in OpenGL
FreeType一个字体库,主要体现FreeType抗锯齿效果。
图中第一行为ttf字体,第二行为点阵字体,各有各的好处,不过ttf用的比较多。可以看出第一行字体的效果更好。
这是用到的库文件,放到了我的GitHub,下载地址。
配置方法即文件放置位置还是按以前的来,在VS2015中,项目属性->链接器->附加依赖项加入libfreettpe.lib。
在原文中有两个bugfix,链接在这。我也在文中直接修改了。
在原本的代码中还有两个bug且都在FreeType.cpp文件中,为print()函数下(170行)的const char* c和make_dlist()函数下(70行)的glTranslatef(0, bitmap_glyph->top-bitmap.rows + 4, 0)。
这是最后的效果截图,
Main文件中改了几个地方,都标记于双行星号内。代码如下,
#ifndef FREE_TYPE_H #define FREE_TYPE_H #include <ft2build.h> #include <freetype\freetype.h> #include <freetype\ftglyph.h> #include <freetype\ftoutln.h> #include <freetype\fttrigon.h> #include <windows.h> #include <GL\glew.h> #include <GL\glut.h> #include <vector> #include <string> #include <stdexcept> /* * MSVC will spit out all sorts of useless waring if you create vectors of strings, * this pragma gets rid of them. */ #pragma warning(disable: 4786) namespace freetype{ using std::vector; using std::string; class font_data{ public: float h; // The height of the font GLuint* textures; GLuint list_base; // The first display list Id public: void init(const char* fname, unsigned int h); // Create a font void clean(); // Free all the resources associated with the font }; void print(const font_data& ft_font, float x, float y, const char* fmt, ...); } #endif
#include "FreeType.h" namespace freetype { /* * OpenGL textures need to have dimensions that are powers of two, * so we need to pad the font bitmaps created by FreeType to make them a legal size */ inline int next_p2(int a) { int rval = 1; while (rval < a) { rval <<= 1; } return rval; } // Create a displsy list corresponding to the given character void make_dlist(FT_Face face, char ch, GLuint list_base, GLuint* tex_base) { /* The first thing we do is get FreeType to render our character into a bitmap */ // Load the glyph for our character if (FT_Load_Glyph(face, FT_Get_Char_Index(face, ch), FT_LOAD_DEFAULT)) { throw std::runtime_error("FT_Load_Glyph failed"); } // Move the face's glyph into a glyph bject FT_Glyph glyph; if (FT_Get_Glyph(face->glyph, &glyph)) { throw std::runtime_error("FT_Get_Glyph failed"); } // Convert the glyph to a bitmap FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph; // This reference will make accessing the bitmap easier FT_Bitmap& bitmap = bitmap_glyph->bitmap; int width = next_p2(bitmap.width); // Get the width and heigth int height = next_p2(bitmap.rows); // Allocate memory GLubyte* expanded_data = new GLubyte[2 * width * height]; for (int i = 0; i < height; ++i) { for (int j = 0; j < width; ++j) { expanded_data[2 * (j + i*width)] = expanded_data[2 * (j + i*width) + 1] = (j >= bitmap.width || i >= bitmap.rows) ? 0 : bitmap.buffer[j + i*bitmap.width]; // Bugfix // expanded_data[2 * (j + i * width)] = 255; // expanded_data[2 * (j + i * width) + 1] = // (j >= bitmap.width || i >= bitmap.rows) ? 0 : bitmap.buffer[j + bitmap.width * i]; } } // The edges of the text will be slightly translucent glBindTexture(GL_TEXTURE_2D, tex_base[ch]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Using GL_LUMINANCE_ALPHA to indicate that we are using 2 channel data glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data); delete[] expanded_data; // The display list glNewList(list_base + ch, GL_COMPILE); glBindTexture(GL_TEXTURE_2D, tex_base[ch]); glPushMatrix(); glTranslatef(bitmap_glyph->left, 0, 0); glTranslatef(0, bitmap_glyph->top-bitmap.rows + 4, 0); float x = (float)bitmap.width / (float)width; float y = (float)bitmap.rows / (float)height; glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(0, bitmap.rows); glTexCoord2f(0, y); glVertex2f(0, 0); glTexCoord2f(x, y); glVertex2f(bitmap.width, 0); glTexCoord2f(x, 0); glVertex2f(bitmap.width, bitmap.rows); glEnd(); glPopMatrix(); glTranslatef(face->glyph->advance.x >> 6, 0, 0); // Increment the raster position as if we were a bitmap font // glBitmap(0, 0, 0, 0, face->glyph->advance.x >> 6, 0, NULL); glEndList(); // Bugfix FT_Done_Glyph(glyph); } // Initialize void font_data::init(const char* fname, unsigned int h) { textures = new GLuint[128]; this->h = h; // Create and initilize a freeType font library FT_Library library; if (FT_Init_FreeType(&library)) { throw std::runtime_error("FT_Init_FreeType failed"); } FT_Face face; // The object In Which FreeType holds information on a given font if (FT_New_Face(library, fname, 0, &face)) { throw std::runtime_error("FT_New_Face failed (there is probably a problem with your font file)"); } FT_Set_Char_Size(face, h << 6, h << 6, 96, 96); list_base = glGenLists(128); glGenTextures(128, textures); // Create fonts for (unsigned char i = 0; i < 128; ++i) { make_dlist(face, i, list_base, textures); } // Free FT_Done_Face(face); FT_Done_FreeType(library); } // Free void font_data::clean() { glDeleteLists(list_base, 128); glDeleteTextures(128, textures); delete[] textures; } inline void pushScreenCoordinateMatrix() { glPushAttrib(GL_TRANSFORM_BIT); GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(viewport[0], viewport[2], viewport[1], viewport[3]); glPopAttrib(); } inline void pop_projection_matrix() { glPushAttrib(GL_TRANSFORM_BIT); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } void print(const font_data& ft_font, float x, float y, const char* fmt, ...) { pushScreenCoordinateMatrix(); GLuint font = ft_font.list_base; // A little biggeer float h = ft_font.h / 0.63f; char text[256]; // String va_list ap; if (fmt == NULL) { *text = 0; // Do nothing } else { va_start(ap, fmt); // Parses the string for variables vsprintf(text, fmt, ap); // Converts symbols to actual numbers va_end(ap); // Results are stored in text } const char* start_line = text; vector<string> lines; const char* c; for (c = text; *c; ++c) { if (*c == '\n') { string line; for (const char* n = start_line; n < c; n++) { line.append(1, *n); } lines.push_back(line); start_line = c + 1; } } if (start_line) { string line; for (const char* n = start_line; n < c; ++n) { line.append(1, *n); } lines.push_back(line); } glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT); glMatrixMode(GL_MODELVIEW); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glListBase(font); float modelview_matrix[16]; glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix); for (int i = 0; i < lines.size(); ++i) { glPushMatrix(); glLoadIdentity(); glTranslatef(x, y - h * i, 0); glMultMatrixf(modelview_matrix); // If we need to know the length of the text // If we decide to use it make sure to also uncomment the glBitmap command in make_dlist() // glRasterPos2f(0, 0); glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str()); // float rpos[4]; // glGetFloatv(GL_CURRENT_RASTER_POSITION, rpos); // float len = x - rpos[0]; glPopMatrix(); } glPopAttrib(); pop_projection_matrix(); } }
#include <windows.h> #include <stdio.h> #include <math.h> #include <gl/glew.h> #include <gl/glut.h> #include <GL/GLUAX.H> #include "FreeType.h" #pragma comment(lib, "legacy_stdio_definitions.lib") /* * Every OpenGL program is linked to a Rendering Context. * A Rendering Context is what links OpenGL calls to the Device Context. * In order for your program to draw to a Window you need to create a Device Context. * The DC connects the Window to the GDI (Graphics Device Interface). */ HGLRC hRC = NULL; // Permanent rendering context HDC hDC = NULL; // Private GDI device context HWND hWnd = NULL; // Holds our window handle HINSTANCE hInstance; // Holds the instance of the application /* * It's important to make this global so that each procedure knows if * the program is running in fullscreen mode or not. */ bool keys[256]; // Array used for the keyboard routine bool active = TRUE; // Window active flag set to TRUE by default bool fullscreen = TRUE; // Fullscreen flag set to fullscreen mode by default GLuint base; GLfloat cnt1; GLfloat cnt2; /******************************************************************************************************/ /******************************************************************************************************/ freetype::font_data our_font; /******************************************************************************************************/ /******************************************************************************************************/ LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration for WndProc GLvoid BuildFont(GLvoid) { HFONT font; // Windows font ID HFONT oldfont; // Used for good house keeping base = glGenLists(96); // Storage for 95 characters // The CreateFont function creates a logical font with the specified characteristics. // The logical font can subsequently be selected as the font for any device. font = CreateFont(-24, // Height of font 0, // Width of font 0, // Angle of escapement 0, // Orientation angle FW_BOLD, // Font weight FALSE, // Italic FALSE, // Underline FALSE, // Strikeout ANSI_CHARSET, // Character set identifier OUT_TT_PRECIS, // Output precision CLIP_DEFAULT_PRECIS, // Clipping precision ANTIALIASED_QUALITY, // Output quality FF_DONTCARE | DEFAULT_PITCH, // Family and pitch "Courier New"); // Font name // The SelectObject function selects an object into the specified device context (DC). // The new object replaces the previous object of the same type. oldfont = (HFONT)SelectObject(hDC, font); // The wglUseFontBitmaps function creates a set of bitmap display lists // for use in the current OpenGL rendering context. The set of bitmap display lists // is based on the glyphs in the currently selected font in the device context. // You can then use bitmaps to draw characters in an OpenGL image. // The wglUseFontBitmaps function creates count display lists, one for each of // a run of count glyphs that begins with the first glyph in the hdc parameter's selected fonts. wglUseFontBitmaps(hDC, 32, 96, base); // Builds 96 characters starting at character 32 SelectObject(hDC, oldfont); // The DeleteObject function deletes a logical pen, brush, font, bitmap, region, // or palette, freeing all system resources associated with the object. // After the object is deleted, the specified handle is no longer valid. DeleteObject(font); } GLvoid KillFont(GLvoid) { glDeleteLists(base, 96); // Delete all 96 characters } GLvoid glPrint(const char* fmt, ...) { char text[256]; // Holds ours string va_list ap; // Pointer to list of arguments if (fmt == NULL) { return; } va_start(ap, fmt); // Parses the string for variables vsprintf(text, fmt, ap); // And converts symbols to actual numbers va_end(ap); // Results are stored in text glPushAttrib(GL_LIST_BIT); // Pushes the display list bits glListBase(base - 32); // Sets th base charcter to 32 glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);// Draws the display list text glPopAttrib(); // Pops the display list bits } GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize and initialize the GL window { if (height == 0) { // Prevent a divide by zero by height = 1; // Making height equal one } glViewport(0, 0, width, height); // Reset the current viewport /* * The following lines set the screen up for a perspective view. * Meaning things in the distance get smaller. This creates a realistic looking scene. * The perspective is calculated with a 45 degree viewing angle based on * the windows width and height. The 0.1f, 100.0f is the starting point and * ending point for how deep we can draw into the screen. * * The projection matrix is responsible for adding perspective to our scene. * glLoadIdentity() restores the selected matrix to it's original state. * The modelview matrix is where our object information is stored. * Lastly we reset the modelview matrix. */ glMatrixMode(GL_PROJECTION); // Select the projection matrix glLoadIdentity(); // Reset the projection matrix // Calculate the aspect ratio of the window gluPerspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f); glMatrixMode(GL_MODELVIEW); // Seclet the modelview matrix glLoadIdentity(); // Reset the modelview matrix } int InitGL(GLvoid) // All setup for OpenGL goes here { /* * Smooth shading blends colors nicely across a polygon, and smoothes out lighting. */ glShadeModel(GL_SMOOTH); // Enables smooth shading glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black background glClearDepth(1.0f); // Depth buffer setup glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really nice perspective calculations BuildFont(); /******************************************************************************************************/ /******************************************************************************************************/ our_font.init("Test.ttf", 16); // Build the FreeType font /******************************************************************************************************/ /******************************************************************************************************/ return TRUE; } /* * For now all we will do is clear the screen to the color we previously decided on, * clear the depth buffer and reset the scene. We wont draw anything yet. */ int DrawGLScene(GLvoid) // Here's where we do all the drawing { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear the screen and the depth buffer glLoadIdentity(); glTranslatef(0.0f, 0.0f, -1.0f); /******************************************************************************************************/ /******************************************************************************************************/ // Blue text glColor3ub(0, 0, 0xff); // Pulsing colors based on text position // glColor3f(1.0f * float(cos(cnt1)), 1.0f * float(sin(cnt2)), 1.0f - 0.5f * float(cos(cnt1 + cnt2))); // Position the text on the screen // glRasterPos2f(-0.45f + 0.05f * float(cos(cnt1)), 0.35f * float(sin(cnt2))); glRasterPos2f(-0.40f, 0.35f); glPrint("Active WGL Bitmap Text - %7.2f", cnt1); // Red text glColor3ub(0xff, 0, 0); glPushMatrix(); glLoadIdentity(); glRotatef(cnt1, 0, 0, 1); glScalef(1, 0.8 + 0.3 * cos(cnt1 / 5), 1); glTranslatef(-180.0f, 0, 0); freetype::print(our_font, 320, 240, "Active FreeType Text - %7.2f", cnt1); glPopMatrix(); /******************************************************************************************************/ /******************************************************************************************************/ cnt1 += 0.051f; cnt2 += 0.005f; return TRUE; // everthing went OK } /* * The job of KillGLWindow() is to release the Rendering Context, * the Device Context and finally the Window Handle. */ GLvoid KillGLWindow(GLvoid) // Properly kill the window { if (fullscreen) { // Are we in fullscreen mode /* * We use ChangeDisplaySettings(NULL,0) to return us to our original desktop. * After we've switched back to the desktop we make the cursor visible again. */ ChangeDisplaySettings(NULL, 0); // if so switch back to the desktop ShowCursor(TRUE); // Show mouse pointer } if (hRC) { // Do we have a rendering context if (!wglMakeCurrent(NULL, NULL)) { // Are we able to release the DC and RC contexts MessageBox(NULL, "Release of DC and RC failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); } if (!wglDeleteContext(hRC)) { // Are we able to delete the RC MessageBox(NULL, "Release rendering context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); hRC = NULL; // Set RC to NULL } if (hDC && !ReleaseDC(hWnd, hDC)) { // Are we able to release the DC MessageBox(NULL, "Release device context failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); hDC = NULL; // Set DC to NULL } if (hWnd && !DestroyWindow(hWnd)) { // Are we able to destroy the window MessageBox(NULL, "Could not release hWnd.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); hWnd = NULL; // Set hWnd to NULL } if (!UnregisterClass("OpenGL", hInstance)) { // Are we able to unregister class MessageBox(NULL, "Could not register class.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); hInstance = NULL; // Set hInstance to NULL } } KillFont(); /******************************************************************************************************/ /******************************************************************************************************/ our_font.clean(); /******************************************************************************************************/ /******************************************************************************************************/ } /* * The next section of code creates our OpenGL Window. */ BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag) { /* * Find a pixel format that matches the one we want */ GLuint PixelFormat; // Holds the result after serching for a match /* * Before you create a window, you MUST register a Class for the window */ WNDCLASS wc; // Windows class structure /* * dwExStyle and dwStyle will store the Extended and normal Window Style Information. */ DWORD dwExStyle; // Window extend style DWORD dwStyle; // Window style RECT WindowRect; // Grabs rectangle upper left/lower right values WindowRect.left = (long)0; // Set left value to 0 WindowRect.right = (long)width; // Set right value to requested width WindowRect.top = (long)0; // Set top value to 0 WindowRect.bottom = (long)height; // Set bottom value to requested height fullscreen = fullscreenflag; // Set the global fullscreen flag /* * The style CS_HREDRAW and CS_VREDRAW force the Window to redraw whenever it is resized. * CS_OWNDC creates a private DC for the Window. Meaning the DC is not shared across applications. * WndProc is the procedure that watches for messages in our program. * No extra Window data is used so we zero the two fields. Then we set the instance. * Next we set hIcon to NULL meaning we don't want an ICON in the Window, * and for a mouse pointer we use the standard arrow. The background color doesn't matter * (we set that in GL). We don't want a menu in this Window so we set it to NULL, * and the class name can be any name you want. I'll use "OpenGL" for simplicity. */ hInstance = GetModuleHandle(NULL); // Grab an instance for our window wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraw on move, and own DC for window wc.lpfnWndProc = (WNDPROC)WndProc; // WndProc handles message wc.cbClsExtra = 0; // No extra window date wc.cbWndExtra = 0; // No extra window date wc.hInstance = hInstance; // set the instance wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load the default icon wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load the arrow pointer wc.hbrBackground = NULL; // No background requried for GL wc.lpszMenuName = NULL; // We don't want a menu wc.lpszClassName = "OpenGL"; // set the class name if (!RegisterClass(&wc)) { // Attempt to register the window class MessageBox(NULL, "Failed to register the window class.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Exit and return false } if (fullscreen) { // attempt fullsreen model /* * There are a few very important things you should keep in mind when switching to full screen mode. * Make sure the width and height that you use in fullscreen mode is the same as * the width and height you plan to use for your window, and most importantly, * set fullscreen mode BEFORE you create your window. */ DEVMODE dmScreenSettings; // Device mode memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); // Make sure memory's cleared dmScreenSettings.dmSize = sizeof(dmScreenSettings); // Size of devmode structure dmScreenSettings.dmPelsWidth = width; // Select window width dmScreenSettings.dmPelsHeight = height; // Select window height dmScreenSettings.dmBitsPerPel = bits; // Select bits per pixel dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; /* * In the line below ChangeDisplaySettings tries to switch to a mode that matches * what we stored in dmScreenSettings. I use the parameter CDS_FULLSCREEN when switching modes, * because it's supposed to remove the start bar at the bottom of the screen, * plus it doesn't move or resize the windows on your desktop when you switch to * fullscreen mode and back. */ //Try to set selected mode and get results. Note: CDS_FULLSCREEN gets rid of start bar if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { //If the mode fails, offer two options. Quit or run in a window if (MessageBox(NULL, "The requested fullscreen mode is not supported by\n your video card. Use" "windowed mode instead?", "GL", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) { fullscreen = FALSE; // Select windowed mode (fullscreen=FLASE) } else { // Pop up a message box letting user know the programe is closing. MessageBox(NULL, "Program will now close.", "ERROR", MB_OK | MB_ICONSTOP); return FALSE; // Exit and return FALSE } } } if (fullscreen) { // Are we still in fullscreen mode /* * If we are still in fullscreen mode we'll set the extended style to WS_EX_APPWINDOW, * which force a top level window down to the taskbar once our window is visible. * For the window style we'll create a WS_POPUP window. * This type of window has no border around it, making it perfect for fullscreen mode. * Finally, we disable the mouse pointer. If your program is not interactive, * it's usually nice to disable the mouse pointer when in fullscreen mode. It's up to you though. */ dwExStyle = WS_EX_APPWINDOW; // Window extended style dwStyle = WS_POPUP; // Window style ShowCursor(FALSE); // Hide mosue pointer } else { /* * If we're using a window instead of fullscreen mode, * we'll add WS_EX_WINDOWEDGE to the extended style. This gives the window a more 3D look. * For style we'll use WS_OVERLAPPEDWINDOW instead of WS_POPUP. * WS_OVERLAPPEDWINDOW creates a window with a title bar, sizing border, * window menu, and minimize / maximize buttons. */ dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window extended style dwStyle = WS_OVERLAPPEDWINDOW; // Window style } /* * By using the AdjustWindowRectEx command none of our OpenGL scene will be covered up by the borders, * instead, the window will be made larger to account for the pixels needed to draw the window border. * In fullscreen mode, this command has no effect. */ AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // Adjust window to true resqusted /* * WS_CLIPSIBLINGS and WS_CLIPCHILDREN are both REQUIRED for OpenGL to work properly. * These styles prevent other windows from drawing over or into our OpenGL Window. */ if (!(hWnd = CreateWindowEx(dwExStyle, // Extended style for the window "OpenGL", // Class name title, // Window title WS_CLIPSIBLINGS | // Requried window style WS_CLIPCHILDREN | // Requried window style dwStyle, // Select window style 0, 0, // Window position WindowRect.right - WindowRect.left, // Calculate adjusted window width WindowRect.bottom - WindowRect.top, // Calculate adjusted window height NULL, // No parent window NULL, // No menu hInstance, // Instance NULL))) // Don't pass anything to WM_CREATE { KillGLWindow(); //Reset the display MessageBox(NULL, "Window creation error.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Retrurn FALSE; } /* * aside from the stencil buffer and the (slow) accumulation buffer */ static PIXELFORMATDESCRIPTOR pfd = // pfd tells windows how we want things to be { sizeof(PIXELFORMATDESCRIPTOR), // Size of this pixel format descriptor 1, // Version number PFD_DRAW_TO_WINDOW | // Format must support window PFD_SUPPORT_OPENGL | // Format must support OpenGL PFD_DOUBLEBUFFER, // Must support double buffer PFD_TYPE_RGBA, // Request an RGBA format bits, // Select our color depth 0, 0, 0, 0, 0, 0, // Color bits ignored 0, // No alpha buffer 0, // shift bit ignored 0, // No accumulation buffer 0, 0, 0, 0, // Accumulation bits ignored 16, // 16Bits Z_Buffer (depth buffer) 0, // No stencil buffer 0, // No auxiliary buffer PFD_MAIN_PLANE, // Main drawing layer 0, // Reserved 0, 0, 0 // Layer makes ignored }; if (!(hDC = GetDC(hWnd))) { // Did we get a device context KillGLWindow(); // Reset the display MessageBox(NULL, "Can't create a GL device context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Return FALSE } if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) { // Did window find a matching pixel format KillGLWindow(); // Reset the display MessageBox(NULL, "Can't find a suitable pixelformat.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Return FALSE; } if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { // Are we able to set the pixel format KillGLWindow(); // Reset the display MessageBox(NULL, "Can't set the pixelformat.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Return FALSE; } if (!(hRC = wglCreateContext(hDC))) { // Are we able to rendering context KillGLWindow(); // Reset the display MessageBox(NULL, "Can't create a GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Return FASLE; } if (!wglMakeCurrent(hDC, hRC)) { // Try to activate the rendering context KillGLWindow(); // Reset the display MessageBox(NULL, "Can't activate the GL rendering context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Return FALSE } /* * ReSizeGLScene passing the screen width and height to set up our perspective OpenGL screen. */ ShowWindow(hWnd, SW_SHOW); // Show the window SetForegroundWindow(hWnd); // slightly higher priority SetFocus(hWnd); // Sets keyboard focus to the window ReSizeGLScene(width, height); // Set up our perspective GL screen /* * we can set up lighting, textures, and anything else that needs to be setup in InitGL(). */ if (!InitGL()) { // Initialize our newly created GL window KillGLWindow(); // Reset the display MessageBox(NULL, "Initialize Failed.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; // Return FALSE } return TRUE; } LRESULT CALLBACK WndProc(HWND hWnd, // Handle for this window UINT uMsg, // Message for this window WPARAM wParam, // Additional message information LPARAM lParam) // Additional message information { switch (uMsg) { // Check for window message case WM_ACTIVATE: { // Check minimization state if (!HIWORD(wParam)) { active = TRUE; // Program is active } else { active = FALSE; // Program is no longer active } return 0; // Return to the message loop } case WM_SYSCOMMAND: { // Intercept system commands switch (wParam) { // Check system calls case SC_SCREENSAVE: // Screensaver trying to start case SC_MONITORPOWER: // Monitor trying to enter powersave return 0; // Prevent form happening } break; // Exit } case WM_CLOSE: { // Did we receive a close message PostQuitMessage(0); // Send a quit message return 0; } case WM_KEYDOWN: { // Is a key being held down keys[wParam] = TRUE; // if so, mark it as TRUE return 0; // Jump back } case WM_KEYUP: { // Has a key been released keys[wParam] = FALSE; // if so, mark it as FALSE return 0; // Jump back } case WM_SIZE: { // Resize the OpenGL window ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); // LoWord = width HiWord = height return 0; // Jump back } } return DefWindowProc(hWnd, uMsg, wParam, lParam); // Pass all unhandled message to DefWindwProc } int WINAPI WinMain(HINSTANCE hInstance, // Instance HINSTANCE hPrevInstance, // Previous instance LPSTR lpCmdLine, // Command line parameters int nCmdShow) // Window show state { MSG msg; // Window message structure BOOL done = FALSE; // Bool variable to exit loop try { // Use exception handling // Ask the user which screen mode they prefer if (MessageBox(NULL, "Would you like to run in fullscreen mode?", "Start fullscreen?", MB_YESNO | MB_ICONQUESTION) == IDNO) { fullscreen = FALSE; // Window mode } // Create our OpenGL window if (!CreateGLWindow("3D Shapes", 640, 480, 16, fullscreen)) { // (Modified) return 0; // Quit if window was not create } while (!done) { // Loop that runs until donw = TRUE if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Is there a message wating if (msg.message == WM_QUIT) { // Havw we received a quit message done = TRUE; // if so done = TRUE } else { // If not, deal with window message TranslateMessage(&msg); // Translate message DispatchMessage(&msg); // Dispatch message } } else { // Draw the scene. Watch for ESC key and quit message from DrawGLScene() if (active) { // Program active if (keys[VK_ESCAPE]) { // Was ESC pressed done = TRUE; // ESC signalled a quit } else { // Not time to quit, update screen DrawGLScene(); // Draw scene SwapBuffers(hDC); // Swap buffers (double buffering) } } /* * It allows us to press the F1 key to switch from fullscreen mode to * windowed mode or windowed mode to fullscreen mode. */ if (keys[VK_F1]) { // Is F1 being pressed keys[VK_F1] = FALSE; // If so make key FASLE KillGLWindow(); // Kill our current window fullscreen = !fullscreen; // Toggle fullscreen / window mode //Recreate our OpenGL window(modified) if (!CreateGLWindow("3D Shapes", 640, 480, 16, fullscreen)) { return 0; // Quit if window was not create } } } } // Shutdown /******************************************************************************************************/ /******************************************************************************************************/ KillGLWindow(); } catch (std::exception& e) { MessageBox(NULL, e.what(), "CAUGHT AN EXCEPTION", MB_OK | MB_ICONINFORMATION); } return (msg.wParam); /******************************************************************************************************/ /******************************************************************************************************/ }
Thanks for Nehe's tutorials, this is his home page.