// Written in the D Programming Language /** * The 23rd lesson in the NeHe tutorial series. * Originally written by GB Schmick. * * Authors: GB Schmick * Jeff Molofee * Olli Aalto */ module lesson23; import derelict.opengl.gl; import derelict.opengl.glu; import derelict.sdl.sdl; import tango.stdc.stringz; /// The window title const char[] WINDOW_TITLE = "NeHe & TipTup's Environment Mapping Tutorial (D version)"; /// The main loop flag bool running = true; /// Lighting ON / OFF bool light; bool pageUp; bool pageDown; bool up; bool down; bool right; bool left; bool sp; /// X Rotation GLfloat xrot = 0.0f; /// Y Rotation GLfloat yrot = 0.0f; /// X Rotation Speed GLfloat xspeed = 0.0f; /// Y Rotation Speed GLfloat yspeed = 0.0f; /// Depth Into The Screen GLfloat z = -5.0f; /// Ambient Light Values GLfloat[] lightAmbient = [0.5f, 0.5f, 0.5f, 1.0f]; /// Diffuse Light Values GLfloat[] lightDiffuse = [1.0f, 1.0f, 1.0f, 1.0f]; /// Light Position GLfloat[] lightPosition = [0.0f, 0.0f, 2.0f, 1.0f]; /// Which Filter To Use GLuint filter; /// Storage for 6 textures GLuint[6] textures; /// Storage For Our Quadratic Objects ( NEW ) GLUquadric* quadratic; /// Which Object To Draw (NEW) GLuint obj; /** * 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_EVERYTHING) < 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(); while (running) { processEvents(); drawGLScene(); SDL_GL_SwapBuffers(); SDL_Delay(10); } // Delete The Quadratic To Free System Resources gluDeleteQuadric(quadratic); } /** * Function to load in bitmap as a GL texture. */ void loadGLTextures() { /* Create storage space for the texture */ SDL_Surface*[2] textureImage; /* Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit */ if ((textureImage[0] = SDL_LoadBMP("data/BG.bmp")) !is null && (textureImage[1] = SDL_LoadBMP("data/Reflect.bmp")) !is null) { scope(exit) SDL_FreeSurface(textureImage[0]); scope(exit) SDL_FreeSurface(textureImage[1]); /* Create The Texture */ glGenTextures(6, &textures[0]); for(int loop = 0; loop <= 1; loop++) { /* Create Nearest Filtered Texture */ glBindTexture(GL_TEXTURE_2D, textures[loop]); /* Gen Tex 0 And 1 */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, 3, textureImage[loop].w, textureImage[loop].h, 0, GL_BGR, GL_UNSIGNED_BYTE, textureImage[loop].pixels); /* Create Linear Filtered Texture */ glBindTexture(GL_TEXTURE_2D, textures[loop + 2]); /* Gen Tex 2 And 3 */ 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[loop].w, textureImage[loop].h, 0, GL_BGR, GL_UNSIGNED_BYTE, textureImage[loop].pixels); /* Create MipMapped Texture */ glBindTexture(GL_TEXTURE_2D, textures[loop + 4]); /* Gen Tex 4 and 5 */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, textureImage[loop].w, textureImage[loop].h, GL_BGR, GL_UNSIGNED_BYTE, textureImage[loop].pixels); } return; } throw new Exception("Failed to load textures."); } /** * 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; case SDL_KEYDOWN: keyPressed(event.key.keysym.sym); break; default: break; } } // Is Page Up Being Pressed? if (pageUp) { z -= 0.02f; // If So, Move Into The Screen } // Is Page Down Being Pressed? if (pageDown) { z += 0.02f; // If So, Move Towards The Viewer } // Is Up Arrow Being Pressed? if (up) { xspeed -= 0.01f; // If So, Decrease xspeed } // Is Down Arrow Being Pressed? if (down) { xspeed += 0.01f; // If So, Increase xspeed } // Is Right Arrow Being Pressed? if (right) { yspeed += 0.01f; // If So, Increase yspeed } // Is Left Arrow Being Pressed? if (left) { yspeed -= 0.01f; // If So, Decrease yspeed } } 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; case SDLK_RIGHT: right = true; break; case SDLK_LEFT: left = true; break; default: break; } } /** * Process a key released event. */ void keyReleased(int key) { switch (key) { case SDLK_ESCAPE: running = false; break; case SDLK_l: light = !light; if (!light) { glDisable(GL_LIGHTING); } else { glEnable(GL_LIGHTING); } break; case SDLK_f: filter++; if (filter > 2) { filter = 0; } 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; case SDLK_RIGHT: right = false; break; case SDLK_LEFT: left = false; break; case SDLK_SPACE: obj++; if (obj > 5) { obj = 0; } 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); // 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); // Setup The Ambient Light glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient.ptr); // Setup The Diffuse Light glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse.ptr); // Position The Light glLightfv(GL_LIGHT1, GL_POSITION, lightPosition.ptr); // Enable Light One glEnable(GL_LIGHT1); // Set The Texture Generation Mode For S To Sphere Mapping glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // Set The Texture Generation Mode For T To Sphere Mapping glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // 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); // Create A Pointer To The Quadric Object ( NEW ) quadratic = gluNewQuadric(); // Create Smooth Normals ( NEW ) gluQuadricNormals(quadratic, GLU_SMOOTH); // Create Texture Coords ( NEW ) gluQuadricTexture(quadratic, GL_TRUE); } /** * 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 Current Modelview Matrix glLoadIdentity(); // Translate Into/Out Of The Screen By z glTranslatef(0.0f, 0.0f, z); // Enable Texture Coord Generation For S glEnable(GL_TEXTURE_GEN_S); // Enable Texture Coord Generation For T glEnable(GL_TEXTURE_GEN_T); // Select A Texture Based On filter glBindTexture( GL_TEXTURE_2D, textures[filter * 2 + 1] ); glPushMatrix(); // Rotate On The X Axis By xrot glRotatef(xrot, 1.0f, 0.0f, 0.0f); // Rotate On The Y Axis By yrot glRotatef(yrot, 0.0f, 1.0f, 0.0f); switch (obj) { case 0: glDrawCube(); break; case 1: // Center The Cylinder glTranslatef(0.0f, 0.0f, -1.5f); // A Cylinder With A Radius Of 0.5 And A Height Of 2 gluCylinder(quadratic, 1.0f, 1.0f, 3.0f, 32, 32); break; case 2: // Draw A Disc (CD Shape) With An Inner Radius Of 0.5, And An Outer Radius Of 2. Plus A Lot Of Segments ;) gluDisk(quadratic, 0.5f, 1.5f, 32, 32); break; case 3: // Draw A Sphere With A Radius Of 1 And 16 Longitude And 16 Latitude Segments gluSphere(quadratic, 1.3f, 32, 32); break; case 4: // Center The Cone glTranslatef(0.0f, 0.0f, -1.5f); // A Cone With A Bottom Radius Of .5 And A Height Of 2 gluCylinder(quadratic, 1.0f, 0.0f, 3.0f, 32, 32); break; } glPopMatrix(); // Disable Texture Coord Generation For S glDisable(GL_TEXTURE_GEN_S); // Disable Texture Coord Generation For T glDisable(GL_TEXTURE_GEN_T); // This Will Select The BG Texture glBindTexture(GL_TEXTURE_2D, textures[filter * 2]); glPushMatrix(); glTranslatef(0.0f, 0.0f, -29.0f); glBegin(GL_QUADS); glNormal3f(0.0f, 0.0f, 1.0f); glTexCoord2f(0.0f, 0.0f ); glVertex3f( -13.3f, -10.0f, 10.0f); glTexCoord2f(1.0f, 0.0f ); glVertex3f( 13.3f, -10.0f, 10.0f); glTexCoord2f(1.0f, 1.0f ); glVertex3f( 13.3f, 10.0f, 10.0f); glTexCoord2f(0.0f, 1.0f ); glVertex3f( -13.3f, 10.0f, 10.0f); glEnd(); glPopMatrix(); // Add xspeed To xrot xrot += xspeed; // Add yspeed To yrot yrot += yspeed; } void glDrawCube() { // Start Drawing Quads glBegin(GL_QUADS); { // Front Face // Normal Pointing Towards Viewer glNormal3f(0.0f, 0.0f, 0.5f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Back Face // Normal Pointing Away From Viewer glNormal3f(0.0f, 0.0f, -0.5f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); // Top Face // Normal Pointing Up glNormal3f(0.0f, 0.5f, 0.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); // Bottom Face // Normal Pointing Down glNormal3f(0.0f, -0.5f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Right face // Normal Pointing Right glNormal3f(0.5f, 0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); // Left Face // Normal Pointing Left glNormal3f(-0.5f, 0.0f, 0.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); } glEnd(); // Done Drawing Quads } /** * 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. */ 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()); }