// Written in the D Programming Language /** * The 20th lesson in the NeHe tutorial series. * Originally written by Jeff Molofee. * * Authors: Jeff Molofee * Olli Aalto */ module lesson20; import derelict.opengl.gl; import derelict.opengl.glu; import derelict.sdl.sdl; import tango.stdc.stringz; /// The window title const char[] WINDOW_TITLE = "NeHe's Masking Tutorial (D version)"; /// The main loop flag bool running; /// Masking On/Off bool masking = true; /// Which Scene To Draw bool scene; /// Storage For Our Five Textures GLuint[5] textures; /// Rolling Texture GLfloat roll = 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, 640, 480, 16, fullScreen); initGL(); running = true; while (running) { processEvents(); drawGLScene(); SDL_GL_SwapBuffers(); SDL_Delay(10); } } /** * 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; case SDLK_SPACE: scene = !scene; break; case SDLK_m: masking = !masking; break; default: break; } } /** * Load all the textures used in this tutorial. */ void loadTextures() { // Create The Textures glGenTextures(5, &textures[0]); loadTexture("data/Logo.bmp", 0); loadTexture("data/Mask1.bmp", 1); loadTexture("data/Image1.bmp", 2); loadTexture("data/Mask2.bmp", 3); loadTexture("data/Image2.bmp", 4); } /** * Load a texture from the given texture name. */ void loadTexture(char[] imageName, GLuint textureIndex) { SDL_Surface* textureImage; // Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit if ((textureImage = SDL_LoadBMP(toStringz(imageName))) !is null) { // Free the surface when exiting the scope scope(exit) SDL_FreeSurface(textureImage); // Create Linear Filtered Texture glBindTexture(GL_TEXTURE_2D, textures[textureIndex]); 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, textureImage.w, textureImage.h, 0, GL_BGR, GL_UNSIGNED_BYTE, textureImage.pixels); return; } throw new Exception("Failed to load texture image: " ~ imageName); } /** * Resize and initialize the OpenGL 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(); } /** * Initialize OpenGL. */ void initGL() { // Load the textures loadTextures(); // Enable Texture Mapping glEnable(GL_TEXTURE_2D); // Enables Smooth Shading glShadeModel(GL_SMOOTH); // Black Background glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Depth Buffer Setup glClearDepth(1.0f); // Enables Depth Testing glEnable(GL_DEPTH_TEST); // The Type Of Depth Test To Do glDepthFunc(GL_LEQUAL); // Really Nice Perspective Calculations glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 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); } /** * The drawing function. */ void drawGLScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glLoadIdentity(); // Reset The Modelview Matrix glTranslatef(0.0f, 0.0f, -2.0f); // Move Into The Screen 5 Units glBindTexture(GL_TEXTURE_2D, textures[0]); // Select Our Logo Texture glBegin(GL_QUADS); // Start Drawing A Textured Quad glTexCoord2f(0.0f, -roll + 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left glTexCoord2f(3.0f, -roll + 0.0f); glVertex3f(1.1f, -1.1f, 0.0f); // Bottom Right glTexCoord2f(3.0f, -roll + 3.0f); glVertex3f(1.1f, 1.1f, 0.0f); // Top Right glTexCoord2f(0.0f, -roll + 3.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left glEnd(); // Done Drawing The Quad glEnable(GL_BLEND); // Enable Blending glDisable(GL_DEPTH_TEST); // Disable Depth Testing if (masking) // Is Masking Enabled? { glBlendFunc(GL_DST_COLOR, GL_ZERO); // Blend Screen Color With Zero (Black) } if (scene) // Are We Drawing The Second Scene? { glTranslatef(0.0f, 0.0f, -1.0f); // Translate Into The Screen One Unit glRotatef(roll * 360, 0.0f, 0.0f, 1.0f); // Rotate On The Z Axis 360 Degrees. if (masking) // Is Masking On? { glBindTexture(GL_TEXTURE_2D, textures[3]); // Select The Second Mask Texture glBegin(GL_QUADS); // Start Drawing A Textured Quad glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left glTexCoord2f(1.0f, 0.0f); glVertex3f(1.1f, -1.1f, 0.0f); // Bottom Right glTexCoord2f(1.0f, 1.0f); glVertex3f(1.1f, 1.1f, 0.0f); // Top Right glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left glEnd(); // Done Drawing The Quad } glBlendFunc(GL_ONE, GL_ONE); // Copy Image 2 Color To The Screen glBindTexture(GL_TEXTURE_2D, textures[4]); // Select The Second Image Texture glBegin(GL_QUADS); // Start Drawing A Textured Quad glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left glTexCoord2f(1.0f, 0.0f); glVertex3f(1.1f, -1.1f, 0.0f); // Bottom Right glTexCoord2f(1.0f, 1.0f); glVertex3f(1.1f, 1.1f, 0.0f); // Top Right glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left glEnd(); // Done Drawing The Quad } else // Otherwise { if (masking) // Is Masking On? { glBindTexture(GL_TEXTURE_2D, textures[1]); // Select The First Mask Texture glBegin(GL_QUADS); // Start Drawing A Textured Quad glTexCoord2f(roll + 0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left glTexCoord2f(roll + 4.0f, 0.0f); glVertex3f(1.1f, -1.1f, 0.0f); // Bottom Right glTexCoord2f(roll + 4.0f, 4.0f); glVertex3f(1.1f, 1.1f, 0.0f); // Top Right glTexCoord2f(roll + 0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left glEnd(); // Done Drawing The Quad } glBlendFunc(GL_ONE, GL_ONE); // Copy Image 1 Color To The Screen glBindTexture(GL_TEXTURE_2D, textures[2]); // Select The First Image Texture glBegin(GL_QUADS); // Start Drawing A Textured Quad glTexCoord2f(roll + 0.0f, 0.0f); glVertex3f(-1.1f, -1.1f, 0.0f); // Bottom Left glTexCoord2f(roll + 4.0f, 0.0f); glVertex3f(1.1f, -1.1f, 0.0f); // Bottom Right glTexCoord2f(roll + 4.0f, 4.0f); glVertex3f(1.1f, 1.1f, 0.0f); // Top Right glTexCoord2f(roll + 0.0f, 4.0f); glVertex3f(-1.1f, 1.1f, 0.0f); // Top Left glEnd(); // Done Drawing The Quad } glEnable(GL_DEPTH_TEST); // Enable Depth Testing glDisable(GL_BLEND); // Disable Blending roll += 0.002f; // Increase Our Texture Roll Variable if (roll > 1.0f) // Is Roll Greater Than One { roll -= 1.0f; // Subtract 1 From Roll } } /** * Initializes and opens the SDL window. */ 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. (See above.) 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()); }