//***********************************************************************// // // // - "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; [Record] class Vector { public mutable x : float; public mutable y : float; public mutable z : float; public static @- (v1 : Vector, v2 : Vector) : Vector { Vector (v1.x - v2.x, v1.y - v2.y, v1.z - v2.z) } public Multiple (c : float) : void { x *= c; y *= c; z *= c; } public Add (v : Vector) : void { x += v.x; y += v.y; z += v.z; } } class Camera { public mutable Position : Vector; public mutable View : Vector; public mutable UpVector : Vector; public this () { Position = Vector (0.0f, 0.0f, 0.0f); View = Vector (0.0f, 1.0f, 0.5f); UpVector = Vector (0.0f, 0.0f, 1.0f); } public PositionCamera (posX : float, posY : float, posZ : float, viewX : float, viewY : float, viewZ : float, upX : float, upY : float, upZ : float) : void { Position = Vector (posX, posY, posZ); View = Vector (viewX, viewY, viewZ); UpVector = Vector (upX, upY, upZ); } public MoveCamera (speed : float) : void { def delta = View - Position; delta.Multiple (speed); View.Add (delta); Position.Add (delta); } } module Game { internal observer : Camera; this () { observer = Camera (); } ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function initializes the game window. ///// ///////////////////////////////// INIT GAME WINDOW \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* internal Init() : void { Init.InitializeOpenGL(Init.SCREEN_WIDTH, Init.SCREEN_HEIGHT); // Initialize openGL when (SDL_EnableKeyRepeat (100, SDL_DEFAULT_REPEAT_INTERVAL) != 0) printf ("Failed enabling key repeat\n"); observer.PositionCamera (.0f, 0.5f, 6.0f, .0f, 0.5f, .0f, .0f, 1.0f, .0f); } ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ///// ///// This function renders the entire scene. ///// ///////////////////////////////// RENDER SCENE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* internal 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((observer.Position.x :> double), (observer.Position.y :> double), (observer.Position.z :> double), (observer.View.x :> double), (observer.View.y :> double), (observer.View.z :> double), (observer.UpVector.x :> double), (observer.UpVector.y :> double), (observer.UpVector.z :> double)); // This determines where the camera's position and view is glBegin (GL_TRIANGLES); // This is our BEGIN to draw glColor3ub (255b, 0b, 0b); // Make the top vertex RED glVertex3f (0.0f, 1.0f, 0.0f); // Here is the top point of the triangle glColor3ub (0b, 255b, 0b); // Make the left vertex GREEN glVertex3f (-1.0f, 0.0f, 0.0f); // Here is the right point of the triangle glColor3ub (0b, 0b, 255b); // Make the right vertex BLUE glVertex3f (1.0f, 0.0f, 0.0f); // Here is the left point of the triangle glEnd(); // This is the END of drawing SDL_GL_SwapBuffers(); // Swap the backbuffers to the foreground } ////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\* ////// ////// This function handles the main game loop ////// ////////////////////////////// MAIN GAME LOOP \\\\\\\\\\\\\\\\\\\\\\\\\\\\* internal 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 Init.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 Init.MainWindow = SDL_SetVideoMode(even.resize.w, even.resize.h, Init.SCREEN_DEPTH, Init.VideoFlags ); Init.SizeOpenGLScreen(even.resize.w, even.resize.h); // now resize the OpenGL viewport when (Init.MainWindow == System.IntPtr.Zero) // if window resize has failed { printf ("Failed resizing SDL window : %s\n", SDL_GetError()); // report error Init.Quit(0); } } } // while( SDL_ ... RenderScene(); // draw our OpenGL scene } // while( ! done) } } module Init { public SCREEN_WIDTH : int = 800; // We want our screen width 800 pixels public SCREEN_HEIGHT : int = 600; // We want our screen height 600 pixels public SCREEN_DEPTH : int = 24; // We want 16 bits per pixel public CAMERA_SPEED : float = 0.03f; mutable internal VideoFlags : int; // Video Flags for the Create Window function mutable internal 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* internal 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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* internal InitializeOpenGL(width : int, height : int) : void { glEnable (GL_DEPTH_TEST); SizeOpenGLScreen (width, height); // resize the OpenGL Viewport to the given height and width } /////////////////// HANDLE KEY PRESS EVENT \\\\\\\\\\\\\\\\\\\\\\\ ////// ////// This function handles the keypress events generated when the user presses a key ////// /////////////////// HANDLE KEY PRESS EVENT \\\\\\\\\\\\\\\\\\\\\\\\ internal 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 when (sym == SDLKey.SDLK_UP) { Game.observer.MoveCamera (CAMERA_SPEED); Game.RenderScene () } when (sym == SDLKey.SDLK_DOWN) { Game.observer.MoveCamera (-CAMERA_SPEED); Game.RenderScene () } } ////////////////////////////// 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 Game.Init(); // Run our message loop Game.MainLoop(); } } ////////////////////////////// QUIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* ////// ////// This will shutdown SDL and quit the program ////// ////////////////////////////// QUIT \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\* internal Quit(ret_val : int) : void { SDL_Quit(); // shuts down SDL stuff System.Environment.Exit(ret_val); // quit the program } }