//~~~~~~App.cs~~~~~~ using System; using System.Threading; using System.Runtime.InteropServices; using Tao.OpenGl; using Tao.Sdl; /* App sets up the SDL window and a timer, as well as basic openGL state. Much of the code has been inspired from a German page: http://www.mono-project.de/artikel/entwickler/tao-tutorial/seite/3/. Unfortunately I cannot read German and am unable to better attribute the code. Author Stephen Jones */ namespace Game{ public class App{ //App sets up external libraries. GameClass handles events and game code private GameClass game; public static Sdl.SDL_Surface surface;//holds the current framebuffer private Sdl.SDL_VideoInfo vidInfo; private int w=1024; private int h=768; private int bpp=16; private int flags=Sdl.SDL_OPENGL | Sdl.SDL_GL_DOUBLEBUFFER | Sdl.SDL_NOFRAME | Sdl.SDL_HWSURFACE;// | Sdl.SDL_FULLSCREEN; private int timeCatcher=0;//used for the timing mechanism public App (){ //SDL is comprised of 8 subsystems. Here we initialize the video if(Sdl.SDL_Init(Sdl.SDL_INIT_VIDEO)<0){ Console.WriteLine("Error initializing SDL"); Sdl.SDL_Quit(); return; } /* Rather than set the video properties up in the constructor, I set them in setVideo. The reason for this is that 2 pointers are used to interact with SDL structures. Once used they convert their handles into vidInfo and surface tamer variables. That this occurs inside the function means the pointers will release their memory on function exit. */ setSDLVideo(); /* openGL is not part of SDL, rather it runs in a window handled by SDL. here we set up some openGL state */ setOpenGL(); //initialize game game=new GameClass(); /* finP is the property get/setter for the boolean fin in the game class. It is held in the game class because the game class handles events. When escape is pressed, fin is set to true and the following loop terminates. The function tick is called every loop. It is passed the time taken for each loop */ while(!game.finP){ tick(Sdl.SDL_GetTicks()-timeCatcher);//updates the game object timeCatcher=Sdl.SDL_GetTicks();//stores the current time Sdl.SDL_Delay(1);//release the thread } /* When the loop ends the code drops down to here. SDL_Quit shuts down the SDL subsystems initialized by SDL_Init */ Sdl.SDL_Quit(); return; } private void setSDLVideo(){ /* To center a non-fullscreen window we need to set an environment variable */ Sdl.SDL_putenv("SDL_VIDEO_CENTERED=center"); /* the video info structure contains the current video mode. Prior to calling setVideoMode, it contains the best available mode for your system. Post setting the video mode, it contains whatever values you set the video mode with. First we point at the SDL structure, then test to see that the point is right. Then we copy the data from the structure to the safer vidInfo variable. */ IntPtr ptr=IntPtr.Zero; ptr=Sdl.SDL_GetVideoInfo(); if(ptr==IntPtr.Zero){ Console.WriteLine("Error querying video info"); Sdl.SDL_Quit(); return; } vidInfo=(Sdl.SDL_VideoInfo)Marshal.PtrToStructure(ptr, typeof(Sdl.SDLVideoInfo)); /* according to the SDL documentaion, the flags parameter passed to setVideoMode affects only the 2D SDL surface, not the openGL. To set their properties use the syntax below. We enable vsync because we are running the loop unfettered and we don't want the loop redrawing the buffer while it is being written to screen */ Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_SWAP_CONTROL, 1);//enable vsync Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_RED_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_GREEN_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_BLUE_SIZE, 8); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_DEPTH_SIZE, 16); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_MULTISAMPLEBUFFERS, 1); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_MULTISAMPLESAMPLES, 2); Sdl.SDL_GL_SetAttribute(Sdl.SDL_GL_DOUBLEBUFFER, 1); /* the setVideoMode function returns the current frame buffer as an SDL_Surface. Again, we grab a pointer to it, then place its content into the non pointery surface variable. I say 'non-pointery', but this SDL variable must have a pointer in it because it can access the current pixels in the framebuffer. */ ptr=IntPtr.Zero; ptr=Sdl.SDL_SetVideoMode(w, h, bpp, flags); if(ptr==IntPtr.Zero){ Console.WriteLine("Error qsetting the video mode"); Sdl.SDL_Quit(); return; } surface=(Sdl.SDL_Surface)Marshal.PtrToStructure(ptr, typeof(Sdl.SDL_Surface)); } /* The initial openGL state is set in the following two functions. The resize function handles setup of the projection properties. The setOpenGL calls resize, then sets model view properties */ private void setOpenGL(){ resize(w, h); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); Gl.glEnable(Gl.GL_DOUBLEBUFFER); Gl.glEnable(Gl.GL_DEPTH_TEST); Gl.glShadeModel(Gl.GL_SMOOTH); Gl.glClearColor(0.1f, 0.1f, 0.1f, 1.0f); Gl.glClearDepth(1.0f); Gl.glDepthFunc(Gl.GL_LEQUAL); Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST); } //called from setOpenGL private void resize(int width, int height){ if(height==0)height=1;//avoid divide by zero /* For some reason my system is squashing the cube. To rectify the situation I have altered the width/height ratio by 0.09. I think the problem is either that I am running dual monitors with different resolutions, or Tao.SDL is playing funny. The same nehe cube runs true when run SDL on C++. */ float ratio=(float)(width/height)+0.09f; Gl.glViewport(0, 0, width, height); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); Glu.gluPerspective(55.0f, ratio, 0.1f, 50.0f); Gl.glMatrixMode(Gl.GL_MODELVIEW); Gl.glLoadIdentity(); } /* tick is called once every loop. Here it paints the screen the glClearColor defined in the setOpenGL function then calls the game class. The game class decides what will be drawn and gets the drawing done. All the drawing is done into the back buffer. Once the drawing is finished the buffers are swapped. */ private void tick(long delay){ Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT); Gl.glLoadIdentity(); game.tick(delay); Gl.glFlush(); Sdl.SDL_GL_SwapBuffers(); } [STAThread] public static void Main(){ App app=new App(); } } }