// Written in the D Programming Language /** * The 9th lesson in the NeHe tutorial series. * Originally written by Jeff Molofee. * * Authors: Jeff Molofee * Olli Aalto */ module lesson09; import derelict.opengl.gl; import derelict.opengl.glu; import derelict.sdl.sdl; import tango.stdc.stringz; import tango.math.Random; /// The window title const char[] WINDOW_TITLE = "NeHe's Animated Blended Textures Tutorial (D version)"; /// The main loop flag bool running = true; /// Twinkling Stars ON / OFF bool twinkle; /// The random number generator. Random rand; bool pageUp; bool pageDown; bool up; bool down; /// Create A Struct For Star struct Star { /// Stars Colors int r, g, b; /// Stars Distance From Center GLfloat dist; /// Stars Current Angle GLfloat angle; } /// Make 'stars' Array Of 10 Using Info From The Struct 'Star' Star stars[10]; /// Viewing Distance Away From Stars GLfloat zoom = -15.0f; /// Tilt The View GLfloat tilt = 90.0f; /// Spin Twinkling Stars GLfloat spin = 0.0f; /// Storage For One Texture GLuint texture; /** * 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()); } // Initialize the random number generator rand = new Random(); // Create A Loop That Goes Through All The Stars // Here we need to remember to use 'inout', otherwise the settings // don't stay. foreach (i, inout star; stars) { // Start All The Stars At Angle Zero star.angle = 0.0f; // Calculate Distance From The Center star.dist = cast(float) i / stars.length * 5.0f; // Give star A Random Red Intensity star.r = rand.next() % 256; // Give star A Random Green Intensity star.g = rand.next() % 256; // Give star A Random Blue Intensity star.b = rand.next() % 256; } } /** * 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(); while (running) { processEvents(); drawGLScene(); SDL_GL_SwapBuffers(); SDL_Delay(10); } } /** * function to load in bitmap as a GL texture. */ void loadGLTextures() { /* Create storage space for the texture */ SDL_Surface* textureImage; /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */ if ((textureImage = SDL_LoadBMP("data/Star.bmp")) !is null) { // Free the surface when exiting the scope scope(exit) SDL_FreeSurface(textureImage); /* Create The Texture */ glGenTextures(1, &texture); /* Typical Texture Generation Using Data From The Bitmap */ glBindTexture(GL_TEXTURE_2D, texture); /* Linear Filtering */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* Generate The Texture */ 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."); } /** * 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_KEYDOWN: keyPressed(event.key.keysym.sym); break; case SDL_QUIT: running = false; break; default: break; } } if (up) // Is Up Arrow Being Pressed { tilt -= 0.5f; // Tilt The Screen Up } if (down) // Is Down Arrow Being Pressed { tilt += 0.5f; // Tilt The Screen Down } if (pageUp) // Is Page Up Being Pressed { zoom -= 0.2f; // Zoom Out } if (pageDown) // Is Page Down Being Pressed { zoom += 0.2f; // Zoom In } } void keyPressed(int key) { switch (key) { case SDLK_PAGEUP: pageUp = true; break; case SDLK_PAGEDOWN: pageDown = true; break; case SDLK_UP: up = true; break; case SDLK_DOWN: down = true; break; default: break; } } void keyReleased(int key) { switch (key) { case SDLK_ESCAPE: running = false; break; case SDLK_t: twinkle = !twinkle; break; case SDLK_PAGEUP: pageUp = false; break; case SDLK_PAGEDOWN: pageDown = false; break; case SDLK_UP: up = false; break; case SDLK_DOWN: down = false; break; default: break; } } /** * 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(); // Enable Texture Mapping glEnable(GL_TEXTURE_2D); // Enable Smooth Shading glShadeModel(GL_SMOOTH); // Black Background glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Depth Buffer Setup glClearDepth(1.0f); // Really Nice Perspective Calculations glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Set The Blending Function For Translucency glBlendFunc(GL_SRC_ALPHA, GL_ONE); // Enable Blending glEnable(GL_BLEND); // 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); // Select Our Texture glBindTexture(GL_TEXTURE_2D, texture); // Loop Through All The Stars // Here too we need to use the 'inout' because we change the values // of the star foreach (i, inout star; stars) { // Reset The View Before We Draw Each Star glLoadIdentity(); // Zoom Into The Screen (Using The Value In 'zoom') glTranslatef(0.0f, 0.0f, zoom); // Tilt The View (Using The Value In 'tilt') glRotatef(tilt, 1.0f, 0.0f, 0.0f); // Rotate To The Current Stars Angle glRotatef(star.angle, 0.0f, 1.0f, 0.0f); // Move Forward On The X Plane glTranslatef(star.dist, 0.0f, 0.0f); // Cancel The Current Stars Angle glRotatef(-star.angle, 0.0f, 1.0f, 0.0f); // Cancel The Screen Tilt glRotatef(-tilt, 1.0f, 0.0f, 0.0f); if (twinkle) // Twinkling Stars Enabled { int other = stars.length - i - 1; // Assign A Color Using Bytes glColor4ub(stars[other].r, stars[other].g, stars[other].b, 255); glBegin(GL_QUADS); // Begin Drawing The Textured Quad glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); glEnd(); // Done Drawing The Textured Quad } // Rotate The Star On The Z Axis glRotatef(spin, 0.0f, 0.0f, 1.0f); // Assign A Color Using Bytes glColor4ub(star.r, star.g, star.b, 255); glBegin(GL_QUADS); // Begin Drawing The Textured Quad glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f); glEnd(); // Done Drawing The Textured Quad // Used To Spin The Stars spin += 0.01f; // Changes The Angle Of A Star star.angle += cast(float) i / stars.length; // Changes The Distance Of A Star star.dist -= 0.01f; // Is The Star In The Middle Yet if (star.dist < 0.0f) { // Move The Star 5 Units From The Center star.dist += 5.0f; // Give It A New Red Value star.r = rand.next() % 256; // Give It A New Green Value star.g = rand.next() % 256; // Give It A New Blue Value star.b = rand.next() % 256; } } } 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()); }