// Written in the D Programming Language /** * The 11th lesson in the NeHe tutorial series. * Originally written by Bosco and Jeff Molofee. * * Authors: Bosco * Jeff Molofee * Olli Aalto */ module lesson11; import derelict.opengl.gl; import derelict.opengl.glu; import derelict.sdl.sdl; import tango.stdc.stringz; import Math = tango.math.Math; /// The window title const char[] WINDOW_TITLE = "Bosco & NeHe's Waving Texture Tutorial (D version)"; /// The main loop flag bool running = true; /// X Rotation GLfloat xrot = 0.0f; /// Y Rotation GLfloat yrot = 0.0f; /// Z Rotation GLfloat zrot = 0.0f; /// Storage For One Texture GLuint texture; /// The Array For The Points On The Grid Of Our "Wave" GLfloat[][][] points; /// Counter Used To Control How Fast Flag Waves GLint wiggleCount; /** * 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(); 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 if ((textureImage = SDL_LoadBMP("data/Tim.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_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; } } /** * 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); // 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); // Back face is filled in glPolygonMode(GL_BACK, GL_FILL); // Front face is drawn with lines glPolygonMode(GL_FRONT, GL_LINE); // Nice and simple way to initialize a multi-dimensional array points = new GLfloat[][][] (45, 45, 3); // Loop Through The X Plane for (int x = 0; x < 45; x++) { // Loop Through The Y Plane for (int y = 0; y < 45; y++) { // Apply The Wave To Our Mesh points[x][y][0] = cast(float) ((x / 5.0f) - 4.5f); points[x][y][1] = cast(float) ((y / 5.0f) - 4.5f); points[x][y][2] = cast(float) (Math.sin((((x / 5.0f) * 40.0f) / 360.0f) * Math.PI * 2.0f)); } } } /** * Here's Where We Do All The Drawing. */ void drawGLScene() { // Clear The Screen And Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Reset The Current Matrix glLoadIdentity(); // Translate 12 Units Into The Screen glTranslatef(0.0f, 0.0f, -12.0f); // Rotate On The X Axis glRotatef(xrot, 1.0f, 0.0f, 0.0f); // Rotate On The Y Axis glRotatef(yrot, 0.0f, 1.0f, 0.0f); // Rotate On The Z Axis glRotatef(zrot, 0.0f, 0.0f, 1.0f); // Select Our Texture glBindTexture(GL_TEXTURE_2D, texture); // Start Drawing Our Quads glBegin(GL_QUADS); // Loop Through The X Plane (44 Points) for (int i = 0; i < 44; i++) { // Loop Through The Y Plane (44 Points) for (int j = 0; j < 44; j++) { // Create A Floating Point X Value float x = i / 44.0f; // Create A Floating Point Y Value float y = j / 44.0f; // Create A Floating Point Y Value + 0.0227f float xb = (i + 1) / 44.0f; // Create A Floating Point Y Value + 0.0227f float yb = (j + 1) / 44.0f; // First Texture Coordinate (Bottom Left) glTexCoord2f(x, y); glVertex3f(points[i][j][0], points[i][j][1], points[i][j][2]); // Second Texture Coordinate (Top Left) glTexCoord2f(x, yb); glVertex3f(points[i][j + 1][0], points[i][j + 1][1], points[i][j + 1][2]); // Third Texture Coordinate (Top Right) glTexCoord2f(xb, yb); glVertex3f(points[i + 1][j + 1][0], points[i + 1][j + 1][1], points[i + 1][j + 1][2]); // Fourth Texture Coordinate (Bottom Right) glTexCoord2f(xb, y); glVertex3f(points[i + 1][j][0], points[i + 1][j][1], points[i + 1][j][2]); } } // Done Drawing Our Quads glEnd(); // Used To Slow Down The Wave (Every 2nd Frame Only) if (wiggleCount == 2) { // Loop Through The Y Plane for (int y = 0; y < 45; y++) { // Store Current Value One Left Side Of Wave float hold = points[0][y][2]; // Loop Through The X Plane for (int x = 0; x < 44; x++) { // Current Wave Value Equals Value To The Right points[x][y][2] = points[x + 1][y][2]; } // Last Value Becomes The Far Left Stored Value points[$ - 1][y][2] = hold; } // Set Counter Back To Zero wiggleCount = 0; } // Increase The Counter wiggleCount++; // Increase The X Rotation Variable xrot += 0.3f; // Increase The Y Rotation Variable yrot += 0.2f; // Increase The Z Rotation Variable zrot += 0.4f; } 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()); }