// Written in the D Programming Language /** * The 17th lesson in the NeHe tutorial series. * Originally written by Giuseppe D'Agata. * * Authors: Giuseppe D'Agata * Jeff Molofee * Olli Aalto */ module lesson17; import derelict.opengl.gl; import derelict.opengl.glu; import derelict.sdl.sdl; import tango.stdc.stringz; import Math = tango.math.Math; const WINDOW_WIDTH = 640; const WINDOW_HEIGHT = 480; /// The window title const char[] WINDOW_TITLE = "NeHe & Giuseppe D'Agata's 2D Font Tutorial (D version)"; /// The main loop flag bool running = true; /// Base Display List For The Font GLuint base; /// Storage For Our Font Textures GLuint[2] textures; /// Generic Loop Variable GLuint loop; /// 1st Counter Used To Move Text & For Coloring GLfloat cnt1 = 0.0f; /// 2nd Counter Used To Move Text & For Coloring GLfloat cnt2 = 0.0f; /** * Module constructor. Here we load the GL, GLU and SDL shared libraries, * and the initialize SDL. */ static this() { DerelictGL.load(); DerelictGLU.load(); DerelictSDL.load(); if (SDL_Init(SDL_INIT_VIDEO) < 0) { throw new Exception("Failed to initialize SDL: " ~ getSDLError()); } } /** * Module destructor. SDL_Quit must be called somewhere, and as we initialized * it in the module constructor so the module destructor should be a suitable * place. */ static ~this() { SDL_Quit(); } /** * The main function. This is where the fun begins. The first order of business * is the check the command line arguments if the user wanted to start in * fullscreen mode. Then the window is created and OpenGL is initialized with * basic settings. Finally the the function starts the main loop which will live * for the duration of the application. * * Params: * args = the command line arguments */ void main(char[][] args) { bool fullScreen = false; if (args.length > 1) { fullScreen = args[1] == "-fullscreen"; } createGLWindow(WINDOW_TITLE, WINDOW_WIDTH, WINDOW_HEIGHT, 16, fullScreen); initGL(); while (running) { processEvents(); drawGLScene(); SDL_GL_SwapBuffers(); SDL_Delay(10); } killFont(); } /** * Process all the pending events. */ void processEvents() { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_KEYUP: keyReleased(event.key.keysym.sym); break; case SDL_QUIT: running = false; break; default: break; } } } /** * Process a key released event. */ void keyReleased(int key) { switch (key) { case SDLK_ESCAPE: running = false; break; default: break; } } /** * function to load in bitmap as a GL texture. */ void loadGLTextures() { // Create storage space for the texture SDL_Surface* fontImage; SDL_Surface* bumpImage; // Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit if ((fontImage = SDL_LoadBMP("data/Font.bmp")) !is null && (bumpImage = SDL_LoadBMP( "data/Bumps.bmp")) !is null) { // Free the surfaces when we exit the scope scope(exit) SDL_FreeSurface(fontImage); scope(exit) SDL_FreeSurface(bumpImage); glGenTextures(2, &textures[0]); glBindTexture(GL_TEXTURE_2D, textures[0]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, fontImage.w, fontImage.h, 0, GL_BGR, GL_UNSIGNED_BYTE, fontImage.pixels); glBindTexture(GL_TEXTURE_2D, textures[1]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, 3, bumpImage.w, bumpImage.h, 0, GL_BGR, GL_UNSIGNED_BYTE, bumpImage.pixels); return; } throw new Exception("Failed to load textures."); } /** * Build Our Font Display List. */ void buildFont() { // Holds Our X Character Coord float cx = 0.0f; // Holds Our Y Character Coord float cy = 0.0f; // Creating 256 Display Lists base = glGenLists(256); // Select Our Font Texture glBindTexture(GL_TEXTURE_2D, textures[0]); // Loop Through All 256 Lists for (int loop = 0; loop < 256; loop++) { // X Position Of Current Character cx = cast(float) (loop % 16) / 16.0f; // Y Position Of Current Character cy = cast(float) (loop / 16) / 16.0f; // Start Building A List glNewList(base + loop, GL_COMPILE); { // Use A Quad For Each Character glBegin(GL_QUADS); { // Texture Coord (Bottom Left) glTexCoord2f(cx, 1 - cy - 0.0625f); // Vertex Coord (Bottom Left) glVertex2i(0, 0); // Texture Coord (Bottom Right) glTexCoord2f(cx + 0.0625f, 1 - cy - 0.0625f); // Vertex Coord (Bottom Right) glVertex2i(16, 0); // Texture Coord (Top Right) glTexCoord2f(cx + 0.0625f, 1 - cy); // Vertex Coord (Top Right) glVertex2i(16, 16); // Texture Coord (Top Left) glTexCoord2f(cx, 1 - cy); // Vertex Coord (Top Left) glVertex2i(0, 16); } // Done Building Our Quad (Character) glEnd(); // Move To The Right Of The Character glTranslated(10, 0, 0); } // Done Building The Display List glEndList(); } } /** * Delete The Font From Memory */ void killFont() { // Delete All 256 Display Lists glDeleteLists(base, 256); } /** * This Is Where The Printing Happens. * * Params: * x = the x coordinate of the text * y = the y coordinate of the text * string = the text to be printed * set = font set */ void glPrint(GLint x, GLint y, char[] string, int set) { if (set > 1) { set = 1; } // Select Our Font Texture glBindTexture(GL_TEXTURE_2D, textures[0]); // Disables Depth Testing glDisable(GL_DEPTH_TEST); // Select The Projection Matrix glMatrixMode(GL_PROJECTION); // Store The Projection Matrix glPushMatrix(); // Reset The Projection Matrix glLoadIdentity(); // Set Up An Ortho Screen glOrtho(0, WINDOW_WIDTH, 0, WINDOW_HEIGHT, -1, 1); // Select The Modelview Matrix glMatrixMode(GL_MODELVIEW); // Store The Modelview Matrix glPushMatrix(); // Reset The Modelview Matrix glLoadIdentity(); // Position The Text (0,0 - Bottom Left) glTranslated(x, y, 0); // Choose The Font Set (0 or 1) glListBase(base - 32 + (128 * set)); // Write The Text To The Screen glCallLists(string.length, GL_UNSIGNED_BYTE, toStringz(string)); // Select The Projection Matrix glMatrixMode(GL_PROJECTION); // Restore The Old Projection Matrix glPopMatrix(); // Select The Modelview Matrix glMatrixMode(GL_MODELVIEW); // Restore The Old Projection Matrix glPopMatrix(); // Enables Depth Testing glEnable(GL_DEPTH_TEST); } /** * Resize And Initialize The GL Window. */ void resizeGLScene(GLsizei width, GLsizei height) { if (height == 0) { height = 1; } // Reset The Current Viewport glViewport(0, 0, width, height); // Select The Projection Matrix glMatrixMode(GL_PROJECTION); // Reset The Projection Matrix glLoadIdentity(); // Calculate The Aspect Ratio Of The Window gluPerspective(45.0f, cast(GLfloat) width / cast(GLfloat) height, 0.1f, 100.0f); // Select The Modelview Matrix glMatrixMode(GL_MODELVIEW); // Reset The Modelview Matrix glLoadIdentity(); } /** * All Setup For OpenGL Goes Here. */ void initGL() { loadGLTextures(); buildFont(); // Clear The Background Color To Black glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Enables Clearing Of The Depth Buffer glClearDepth(1.0); // The Type Of Depth Test To Do glDepthFunc(GL_LEQUAL); // Select The Type Of Blending glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Enables Smooth Color Shading glShadeModel(GL_SMOOTH); // Enable 2D Texture Mapping glEnable(GL_TEXTURE_2D); // Change to texture matrix and flip and rotate the texture glMatrixMode(GL_TEXTURE); glRotatef(180.0f, 0.0f, 0.0f, 1.0f); glScalef(-1.0f, 1.0f, 1.0f); // Back to normal glMatrixMode(GL_MODELVIEW); } /** * Here's Where We Do All The Drawing. */ void drawGLScene() { // Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Reset The Modelview Matrix glLoadIdentity(); // Select Our Second Texture glBindTexture(GL_TEXTURE_2D, textures[1]); // Move Into The Screen 5 Units glTranslatef(0.0f, 0.0f, -5.0f); // Rotate On The Z Axis 45 Degrees (Clockwise) glRotatef(45.0f, 0.0f, 0.0f, 1.0f); // Rotate On The X & Y Axis By cnt1 (Left To Right) glRotatef(cnt1 * 30.0f, 1.0f, 1.0f, 0.0f); // Disable Blending Before We Draw In 3D glDisable(GL_BLEND); // Bright White glColor3f(1.0f, 1.0f, 1.0f); // Draw Our First Texture Mapped Quad glBegin(GL_QUADS); { glTexCoord2d(0.0f, 0.0f); glVertex2f(-1.0f, 1.0f); glTexCoord2d(1.0f, 0.0f); glVertex2f(1.0f, 1.0f); glTexCoord2d(1.0f, 1.0f); glVertex2f(1.0f, -1.0f); glTexCoord2d(0.0f, 1.0f); glVertex2f(-1.0f, -1.0f); } glEnd(); // Rotate On The X & Y Axis By 90 Degrees (Left To Right) glRotatef(90.0f, 1.0f, 1.0f, 0.0f); // Draw Our Second Texture Mapped Quad glBegin(GL_QUADS); { glTexCoord2d(0.0f, 0.0f); glVertex2f(-1.0f, 1.0f); glTexCoord2d(1.0f, 0.0f); glVertex2f(1.0f, 1.0f); glTexCoord2d(1.0f, 1.0f); glVertex2f(1.0f, -1.0f); glTexCoord2d(0.0f, 1.0f); glVertex2f(-1.0f, -1.0f); } glEnd(); // Enable Blending glEnable(GL_BLEND); // Reset The View glLoadIdentity(); // Pulsing Colors Based On Text Position glColor3f(1.0f * cast(float) (Math.cos(cnt1)), 1.0f * cast(float) (Math.sin(cnt2)), 1.0f - 0.5f * cast(float) (Math.cos(cnt1 + cnt2))); // Print GL Text To The Screen glPrint(cast(int) ((280 + 250 * Math.cos(cnt1))), cast(int) (235 + 200 * Math.sin(cnt2)), "NeHe", 0); glColor3f(1.0f * cast(float) (Math.sin(cnt2)), 1.0f - 0.5f * cast(float) (Math.cos(cnt1 + cnt2)), 1.0f * cast(float) (Math.cos(cnt1))); // Print GL Text To The Screen glPrint(cast(int) ((280 + 230 * Math.cos(cnt2))), cast(int) (235 + 200 * Math.sin(cnt1)), "OpenGL", 1); // Set Color To Blue glColor3f(0.0f, 0.0f, 1.0f); glPrint(cast(int) (240 + 200 * Math.cos((cnt2 + cnt1) / 5)), 2, "Giuseppe D'Agata", 0); // Set Color To White glColor3f(1.0f, 1.0f, 1.0f); glPrint(cast(int) (242 + 200 * Math.cos((cnt2 + cnt1) / 5)), 2, "Giuseppe D'Agata", 0); // Increase The First Counter cnt1 += 0.01f; // Increase The Second Counter} cnt2 += 0.0081f; } void createGLWindow(char[] title, int width, int height, int bits, bool fullScreen) { // Set the OpenGL attributes SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); // Set the window title SDL_WM_SetCaption(toStringz(title), null); /* Note the SDL_DOUBLEBUF flag is not required to enable double * buffering when setting an OpenGL video mode. * Double buffering is enabled or disabled using the * SDL_GL_DOUBLEBUFFER attribute. */ int mode = SDL_OPENGL; if (fullScreen) { mode |= SDL_FULLSCREEN; } // Now open a SDL OpenGL window with the given parameters if (SDL_SetVideoMode(width, height, bits, mode) is null) { throw new Exception("Failed to open OpenGL window: " ~ getSDLError()); } resizeGLScene(width, height); } /** * Get the SDL error as a D string. * * Returns: A D string containing the current SDL error. */ char[] getSDLError() { return fromStringz(SDL_GetError()); }