//***********************************************************************// // // // - "Talk to me like I'm a 3 year old!" Programming Lessons - // // // // $Author: Ben Humphrey digiben@gametutorilas.com // // // // $Program: Triangle // // // // $Description: Init OpenGL and Draw a triangle to the screen // // // // $Date: 3/3/01 // // // //***********************************************************************// using Tao.Sdl.Sdl; using Tao.OpenGl.Gl; using Tao.OpenGl.Glu; using Nemerle.IO; module Triangle { SCREEN_WIDTH : int = 800; // We want our screen width 800 pixels SCREEN_HEIGHT : int = 600; // We want our screen height 600 pixels SCREEN_DEPTH : int = 0; // We want 16 bits per pixel mutable VideoFlags : int; // Video Flags for the Create Window function mutable MainWindow : System.IntPtr; // drawing surface on the SDL window /////////////////////////////////// TOGGLE FULL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* /////// /////// This function TOGGLES between FULLSCREEN and WINDOWED mode /////// /////////////////////////////////// TOGGLE FULL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ToggleFullScreen() : void { when (SDL_WM_ToggleFullScreen (MainWindow) == 0) // try to toggle fullscreen mode for window 'MainWindow' { printf ("Failed to Toggle Fullscreen mode : %s\n", SDL_GetError()); // report error in case toggle fails Quit(0); } } /////////////////////////////// CREATE MY WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* //////// //////// This function CREATES our WINDOW for drawing the GL stuff //////// /////////////////////////////// CREATE MY WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* CreateMyWindow(strWindowName : string, width : int, height : int, VideoFlags : int) : void { // SCREEN_DEPTH is const for bits per pixel MainWindow = SDL_SetVideoMode(width, height, SCREEN_DEPTH, VideoFlags); when ( MainWindow == System.IntPtr.Zero ) // if window creation failed { printf ("Failed to Create Window : %s\n", SDL_GetError()); // report error Quit(0); } SDL_WM_SetCaption(strWindowName, strWindowName); // set the window caption (first argument) and icon caption (2nd arg) } ///////////////////////////// SETUP PIXEL FORMAT \\\\\\\\\\\\\\\\\\\\\\\\\\\\* /////// /////// Sets the pixel format for openGL and video flags for SDL /////// ///////////////////////////// SETUP PIXEL FORMAT \\\\\\\\\\\\\\\\\\\\\\\\\\\\* SetupPixelFormat() : void { //////// SURFACE IS THE DRAWABLE PORTION OF AN SDL WINDOW \\\\\\\\* ///////////// we set the common flags here VideoFlags = SDL_OPENGL; // it's an openGL window VideoFlags |= SDL_HWPALETTE; // exclusive access to hardware colour palette VideoFlags |= SDL_RESIZABLE; // the window must be resizeable def VideoInfo = SDL_GetVideoInfo(); // query SDL for information about our video hardware ///////////// we set the system dependant flags here if(VideoInfo.hw_available != 0) // is it a hardware surface VideoFlags |= SDL_HWSURFACE; else VideoFlags |= SDL_SWSURFACE; // Blitting is fast copying / moving /swapping of contiguous sections of memory // for more about blitting check out : // http://www.csc.liv.ac.uk/~fish/HTML/blitzman/bm_blitter.html when (VideoInfo.blit_hw != 0) // is hardware blitting available VideoFlags |= SDL_HWACCEL; // tell SDL that the GL drawing is going to be double buffered def _ = SDL_GL_SetAttribute( SDL_GLattr.SDL_GL_DOUBLEBUFFER, 1 ); // size of depth buffer def _ = SDL_GL_SetAttribute( SDL_GLattr.SDL_GL_DEPTH_SIZE, SCREEN_DEPTH); // we aren't going to use the stencil buffer def _ = SDL_GL_SetAttribute( SDL_GLattr.SDL_GL_STENCIL_SIZE, 0); // this and the next three lines set the bits allocated per pixel - def _ = SDL_GL_SetAttribute( SDL_GLattr.SDL_GL_ACCUM_RED_SIZE, 0); // - for the accumulation buffer to 0 def _ = SDL_GL_SetAttribute( SDL_GLattr.SDL_GL_ACCUM_GREEN_SIZE, 0); def _ = SDL_GL_SetAttribute( SDL_GLattr.SDL_GL_ACCUM_BLUE_SIZE, 0); def _ = SDL_GL_SetAttribute( SDL_GLattr.SDL_GL_ACCUM_ALPHA_SIZE, 0); () } //////////////////////////// RESIZE OPENGL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function resizes the viewport for OpenGL. ///// //////////////////////////// RESIZE OPENGL SCREEN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* SizeOpenGLScreen(width : int, height : int) : void // Initialize The GL Window { def height = // Prevent A Divide By Zero error if (height == 0) 1 else height; // Make the Height Equal One glViewport(0, 0, width, height); // Make our viewport the whole window // We could make the view smaller inside // Our window if we wanted too. // The glViewport takes (x, y, width, height) // This basically means, what our drawing boundries glMatrixMode(GL_PROJECTION); // Select The Projection Matrix glLoadIdentity(); // Reset The Projection Matrix // Calculate The Aspect Ratio Of The Window // The parameters are: // (view angle, aspect ration of the width to the height, // The closest distance to the camera before it clips, // FOV // Ratio // The farthest distance before it stops drawing) gluPerspective(45.0, (width :> double) / (height :> double), 1.0, 150.0); // * Note * - The farthest distance should be at least 1 if you don't want some // funny artifacts when dealing with lighting and distance polygons. This is a special // thing that not many people know about. If it's less than 1 it creates little flashes // on far away polygons when lighting is enabled. glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix glLoadIdentity(); // Reset The Modelview Matrix } //////////////////////////////// INITIALIZE GL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function handles all the initialization for openGL ///// //////////////////////////////// INITIALIZE GL \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* InitializeOpenGL(width : int, height : int) : void { SizeOpenGLScreen(width, height); // resize the OpenGL Viewport to the given height and width } ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function initializes the game window. ///// ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* Init() : void { InitializeOpenGL(SCREEN_WIDTH,SCREEN_HEIGHT); // Initialize openGL // *Hint* We will put all our game init stuff here // Some things include loading models, textures and network initialization } /////////////////// HANDLE KEY PRESS EVENT \\\\\\\\\\\\\\\\\\\\\\\ ////// ////// This function handles the keypress events generated when the user presses a key ////// /////////////////// HANDLE KEY PRESS EVENT \\\\\\\\\\\\\\\\\\\\\\\\ HandleKeyPressEvent(keysym : SDL_keysym) : void { def sym = (keysym.sym :> SDLKey); // which key have we got when (sym == SDLKey.SDLK_F1) // if it is F1 ToggleFullScreen(); // toggle between fullscreen and windowed mode when (sym == SDLKey.SDLK_ESCAPE) // if it is ESCAPE Quit(0); // quit after cleaning up } ////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\* ////// ////// This function handles the main game loop ////// ////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\* MainLoop() : void { mutable done = false; // is our job done ? not yet ! mutable even = SDL_Event (); while(!done) // as long as our job's not done { while( SDL_PollEvent (out even) != 0 ) // look for events (like keystrokes, resizing etc.) { def ty = (even.@type :> int); // what kind of event have we got ? when (ty == SDL_QUIT) // if user wishes to quit done = true; // this implies our job is done when (ty == SDL_KEYDOWN) // if the user has pressed a key HandleKeyPressEvent( even.key.keysym ); // callback for handling keystrokes, arg is key pressed when (ty == SDL_VIDEORESIZE) { // if there is a resize event // request SDL to resize the window to the size and depth etc. that we specify MainWindow = SDL_SetVideoMode(even.resize.w, even.resize.h, SCREEN_DEPTH, VideoFlags ); SizeOpenGLScreen(even.resize.w, even.resize.h); // now resize the OpenGL viewport when (MainWindow == System.IntPtr.Zero) // if window resize has failed { printf ("Failed resizing SDL window : %s\n", SDL_GetError()); // report error Quit(0); } } } // while( SDL_ ... RenderScene(); // draw our OpenGL scene } // while( ! done) } ////////////////////////////// MAIN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ////// ////// create the window and calling the initialization functions ////// ////////////////////////////// MAIN \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* Main () : void { // print user instructions printf (" Hit the F1 key to Toggle between Fullscreen and windowed mode\n"); printf (" Hit ESC to quit\n"); if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) // try to initialize SDL video module // report error if it fails printf ("Failed initializing SDL Video : %s\n", SDL_GetError()); else { // Set up the format for the pixels of the OpenGL drawing surface SetupPixelFormat(); // Create our window, we pass caption for the window, // the width, height and video flags required CreateMyWindow("www.GameTutorials.com - First OpenGL Program", SCREEN_WIDTH, SCREEN_HEIGHT, VideoFlags); // Initializes our OpenGL drawing surface Init(); // Run our message loop MainLoop(); } } ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function renders the entire scene. ///// ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* RenderScene() : void { glClear(GL_COLOR_BUFFER_BIT %| GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer glLoadIdentity(); // Reset The View // Position View Up Vector gluLookAt(0.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // This determines where the camera's position and view is // The position has an X Y and Z. Right now, we are standing at (0, 0, 6) // The view also has an X Y and Z. We are looking at the center of the axis (0, 0, 0) // The up vector is 3D too, so it has an X Y and Z. We say that up is (0, 1, 0) // Unless you are making a game like Descent(TM), the up vector can stay the same. // Below we say that we want to draw triangles glBegin (GL_TRIANGLES); // This is our BEGIN to draw glVertex3f(0.0f, 1.0f, 0.0f); // Here is the top point of the triangle glVertex3f(-1.0f, 0.0f, 0.0f); glVertex3f(1.0f, 0.0f, 0.0f); // Here are the left and right points of the triangle glEnd(); // This is the END of drawing // I arranged the functions like that in code so you could visualize better // where they will be on the screen. Usually they would each be on their own line // The code above draws a triangle to those points and fills it in. // You can have as many points inside the BEGIN and END, but it must be in three's. // Try GL_LINES or GL_QUADS. Lines are done in 2's and Quads done in 4's. SDL_GL_SwapBuffers(); // Swap the backbuffers to the foreground } ////////////////////////////// QUIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ////// ////// This will shutdown SDL and quit the program ////// ////////////////////////////// QUIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* Quit(ret_val : int) : void { SDL_Quit(); // shuts down SDL stuff System.Environment.Exit(ret_val); // quit the program } } ///////////////////////////////////////////////////////////////////////////////// // // * QUICK NOTES * // // That's all there is to initializing openGL! // Now that you can display a triangle to the screen, // you can go onto many other cool things from here. // A lot of this (most of it) might be confusing the first time. // The only stuff you should REALLY understand is the RenderScene() function. // This function is what draws everything. The rest of the stuff is init junk // that you will very rarely ever look at again. You might add a line here // and there, but mostly it stays the same. In the next tutorials, we will // eventually just stick it in another .cpp file so we can just focus on the // important stuff that we will most likely be changing a lot. This will take // the stress of being overwhelmed from the other stuff. Like I said, // if you can understand that you have a camera, and it points in a direction, // and that you plot points to make a triangle, you are good for now :) // // Here are the basic steps to initializing OpenGL. // // 1) If you want to go full screen, give the option too. Granted, the Full // screen code doesn't have anything to do with OpenGL, but I would always // encourage giving a full screen option. It goes faster when it's in full // screen mode which helps out on slower computers. // // 2) Create our window with the full screen information. We need to check if // the user wants full screen first because our window properties depend on it. // // 3) Next, we need to setup the pixel format. This allows us to use openGL in // our window. We can also specify double buffering, if we want to draw to the // window, or a bitmap, and if we want to allow GDI function calls. This is // necessary if we want to do anything with OpenGL. You will most likely never // need to change the code in SetupPixelFormat(). It's a one time coded thing :) // // 4) Lastly, we need to setup our screen translations. If we didn't use OpenGL // this part would be complicated and tricky. Luckily, OpenGL keeps us from // all the math needed to do the translations. We just tell OpenGL how large // our viewport is (usually the whole screen) and which perspective we want to look // at out world. After that, we are all set and we just need to DRAW SOMETHING! // // We plotted 3 points in this tutorial. 3 points make up a triangle. // We used the GL_TRIANGLES flag to tell OpenGL to render triangles. // // Like so (cleaner): // // glBegin (GL_TRIANGLES); // glVertex3f(0, 1, 0); // Top point // glVertex3f(-1, 0, 0); // Bottom right point // glVertex3f(1, 0, 0); // Bottom left point // glEnd(); // // If we wanted 2 triangles, we could say: // // glBegin (GL_TRIANGLES); // glVertex3f(0, 1, 0); // Top point // glVertex3f(-1, 0, 0); // Bottom right point // glVertex3f(1, 0, 0); // Bottom left point // // glVertex3f(0, 1, 1); // Top point // glVertex3f(-1, 0, 1); // Bottom right point // glVertex3f(1, 0, 1); // Bottom left point // glEnd(); // // Here is a horrible attempt to draw the 3D axis's // // Y // | // | // |________ X // \ // \ // Z // // This is drawn in a rotated manner so you can see the Z axis. // Otherwise it would be coming out of the screen and you couldn't see it. // Positive Z comes out of the screen towards us, where -z goes into the screen // Positive X is on the right, negative X is on the left // Positive Y goes straight up, where negative Y goes straight down // // So here is what our triangle looks like with the axis // Y // | // /|\ // /_|_\______ X // \ // \ // Z // // As you can see, our triangle is half on the negative X side // and half on the positive X side. It is also 0 on the Z axis, // so really, it's really seen as 2D more than 3D because we // aren't using the third dimension, which is Z. // // The next tutorial will be a lot smaller in comments because I will // chop off all the stuff that I already explain, so then we can just focus // on the RenderScene() function, rather than all the Init stuff. (Blah!) // // Once again, if it seems overwhelming.. it will get easier and MUCH more fun! // // As for messing around with this tutorial, try changing camera positions // to see how it effects your view. Also, try plotting in other points and // other triangles/lines/Quads (rectangles) to see what you can create so far. // // One last note about this code. It might be a good idea, if you don't // understand anything about 3D or matrices (matrix math) to go on the internet // And do a little investigating and learning. It couldn't hurt. I will teach you // all the math you will need to know, but the more versed you are in math, the better. // If you are not good at math, or at least you don't know anything over algebra/geometry, // you are going to get a little frustrated. You will want to be pretty comfortable // with cosine and sine, as well as plane and vector math. You will want to know what // a vector is, and what purpose it serves. On my site I will most likely have a page // dedicated to teaching 3D concepts, but if not when you read this, use the internet. // // Don't get scared if you are not a math person, I wasn't really when I first started // doing 3D programming, but you learn REALLY fast, and it's fun when you use math // to do something cool. Yah, you heard me, math can be fun! // // 3D programming is by FAR the coolest thing to program. // // // Ben Humphrey (DigiBen) // Game Programmer // DigiBen@GameTutorials.com // Co-Web Host of www.GameTutorials.com // //