diff mde/setup/Screen.d @ 85:56c0ddd90193

Intermediate commit (not stable). Changes to init system.
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 11 Sep 2008 11:33:51 +0100
parents mde/setup/sdl.d@ea58f277f487
children 79d816b3e2d2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mde/setup/Screen.d	Thu Sep 11 11:33:51 2008 +0100
@@ -0,0 +1,269 @@
+/* LICENSE BLOCK
+Part of mde: a Modular D game-oriented Engine
+Copyright © 2007-2008 Diggory Hardy
+
+This program is free software: you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation, either
+version 2 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+/** Screen: SDL & OpenGL setup/cleanup, drawing.
+ *
+ * Has an interface by which other code can hook in for drawing and resize notifications. */
+module mde.setup.Screen;
+
+import mde.setup.exception;
+import mde.lookup.Options;
+import imde = mde.imde;
+debug (drawGlyphCache) import mde.font.font;
+
+import tango.util.log.Log : Log, Logger;
+import tango.stdc.stringz;
+import tango.time.Time;     // TimeSpan (type only; unused)
+
+import derelict.sdl.sdl;
+import derelict.opengl.gl;	// for loading a later gl version
+import derelict.util.exception;
+
+/** Currently just used as a namespace. Potential for multiple screen support later? */
+struct Screen {
+    // TYPES (these mustn't be static):
+    /** Interface for anything hooking into the screen for drawing, etc. */
+    interface Drawable {
+        /** Called on window creation and whenever the window manager resizes the window, before
+         * the new size is set. The new size is passed.
+         *
+         * The parameters were passed as ref to allow the called function to change them, but SDL
+         * didn't appear able to resize the window (on X11), so this was dropped. */
+        void sizeEvent (int w, int h);
+        
+        /** Called you guess when :-) */
+        void draw ();
+    }
+    
+    /** All video options. */
+    class OptionsVideo : Options {
+        mixin (impl!("bool fullscreen,hardware,resizable,noFrame; int screenW,screenH,windowW,windowH;"));
+    }
+    
+static:
+    /** Init function to initialize SDL. */
+    StageState init () {      // init func
+        // Initialise SDL
+        if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK /+| SDL_INIT_EVENTTHREAD+/)) {
+            logger.fatal ("SDL initialisation failed:");
+            char* msg = SDL_GetError ();
+            logger.fatal (msg ? fromStringz(msg) : "no reason available");
+            
+            throw new InitException ("SDL Initialization failed");
+        }
+        debug logger.trace ("SDL initialised");
+        return StageState.ACTIVE;
+    }
+    /** SDL shutdown */
+    StageState cleanup () {
+        SDL_Quit();
+        return StageState.INACTIVE;
+    }
+    /** Init function to set up a window with OpenGL support. */
+    StageState initWindow () {
+        //BEGIN Create window and initialize OpenGL
+        // Window creation flags and size
+        flags = SDL_OPENGL;
+        if (vidOpts.hardware) flags |= SDL_HWSURFACE | SDL_DOUBLEBUF;
+        else flags |= SDL_SWSURFACE;
+        int w, h;
+        if (vidOpts.fullscreen) {
+            flags |= SDL_FULLSCREEN;
+            w = vidOpts.screenW;
+            h = vidOpts.screenH;
+        }
+        else {
+            if (vidOpts.resizable) flags |= SDL_RESIZABLE;
+            if (vidOpts.noFrame) flags |= SDL_NOFRAME;
+            w = vidOpts.windowW;
+            h = vidOpts.windowH;
+        }
+        
+        // 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);
+        
+        // Open a window
+        debug logger.trace ("Opening a window (this can crash if the libraries are messed up)");
+        if (setWindow (w, h)) {
+            throw new InitException ("Failed to open a window");
+        }
+        
+        /* Now (must be done after GL context is created) we can try to load later version.
+         * The initial loading provides opengl 1.1 features.
+         *
+         * 1.4 is now used for glBlendColor (coloured text).
+         *
+         * Currently the latest version used is 1.3; adjust this as necessary. However, before using
+         * features from any OpenGL version > 1.1 a check must be made on what was loaded by calling
+         * DerelictGL.availableVersion(). Note that availableVersion() could be used instead to load
+         * the highest supported version but this way we know what we're getting. */
+        if (DerelictGL.availableVersion < GLVersion.Version13) {
+            throw new InitException ("Required at least OpenGL 1.3; didn't get this.");
+        }
+        /+try {
+            DerelictGL.loadVersions(GLVersion.Version14);
+        } catch (SharedLibProcLoadException e) {
+            logger.warn ("Loading OpenGL version 1.4 failed:");
+            logger.warn (e.msg);
+            
+            setInitFailure ();
+            return;
+        }+/
+        
+        // OpenGL stuff:
+        glDisable(GL_LIGHTING);
+        glDisable(GL_DEPTH_TEST);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glEnable(GL_TEXTURE_2D);
+        glShadeModel(GL_SMOOTH);
+        
+        glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
+        
+        glMatrixMode(GL_MODELVIEW);
+        glLoadIdentity();
+        
+        // Used for font rendering:
+        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        //NOTE: wrap mode may have an effect, but shouldn't be noticed...
+        
+        // Window-manager settings
+        SDL_WM_SetCaption (toStringz ("mde"), null);
+        // SDL_WM_GrabInput (use later)
+        //END Create window and initialize OpenGL
+        return StageState.ACTIVE;
+    }
+    
+    
+    /** Called when a resize event occurs (when the window manager resizes the window). */
+    void resizeEvent (int w, int h) {
+        // Save new size to config
+        if (vidOpts.fullscreen) {       // probably resizeEvent only called when not fullscreen
+            vidOpts.set!(int) ("screenW", w);
+            vidOpts.set!(int) ("screenH", h);
+        } else {
+            vidOpts.set!(int) ("windowW", w);
+            vidOpts.set!(int) ("windowH", h);
+        }
+        
+        if (setWindow (w,h))
+            imde.run = false;
+    }
+    
+    /** Add a drawable element to the screen (see Drawable interface).
+     *
+     * Should be called before Init to get initial size. Currently no means to remove drawables,
+     * and not really designed for more than one. */
+    void addDrawable (Drawable d) {
+        drawables ~= d;
+    }
+    
+    /** Drawing function */
+    void draw (TimeSpan) {
+        glClear(GL_COLOR_BUFFER_BIT);
+        
+        foreach (Drawable d; drawables)
+            d.draw;
+        
+        debug (drawGlyphCache) {
+            logger.trace ("Drawing font texture");
+            FontStyle.drawTexture;
+        }
+        
+        // Error check:
+        GLenum err = glGetError();
+        while (err != GL_NO_ERROR) {
+            logger.error ("GL error: {}", err);
+            err = glGetError();
+        }
+        
+        glFinish();         // Use Finish rather than Flush to make sure gl is ready to swap buffers
+        SDL_GL_SwapBuffers();
+    }
+    
+    /** Set a new window size. Returns true on failure due to the different ways this must be
+     * handled. */
+    private bool setWindow (int w, int h) {
+        foreach (d; drawables)  // Tell all drawables the new window size.
+            d.sizeEvent (w,h);
+        
+        debug logger.trace ("Setting video mode {}x{}, 32-bit, flags: {}", w,h,flags);
+        if (SDL_SetVideoMode (w, h, 32, flags) is null) {
+            logger.fatal ("Unable to set video mode:");
+            char* msg = SDL_GetError ();
+            logger.fatal (msg ? fromStringz(msg) : "no reason available");
+            
+            // Print a load of info:
+            logger.info ("Available video modes:");
+            SDL_Rect** modes = SDL_ListModes (null, SDL_FULLSCREEN);
+            if (modes is null) logger.info ("None!");
+            else if (modes is cast(SDL_Rect**) -1) logger.info ("All modes are available");
+            else {
+                for (uint i = 0; modes[i] !is null; ++i) {
+                    logger.info ("\t{}x{}", modes[i].w, modes[i].h);
+                }
+            }
+            
+            SDL_VideoInfo* vi = SDL_GetVideoInfo ();
+            if (vi !is null) {
+                logger.info ("Video info:");
+                logger.info ("Hardware surface support: "~ (vi.flags & SDL_HWSURFACE ? "yes" : "no"));
+                logger.info ("Video memory: {}", vi.video_mem);
+                
+                if (vi.vfmt !is null) {
+                    logger.info ("Best video mode:");
+                    logger.info ("Bits per pixel: {}", vi.vfmt.BitsPerPixel);
+                }
+            }
+            
+            return true;
+        }
+        
+        // Reset the projection and viewport
+        glMatrixMode (GL_PROJECTION);
+        glLoadIdentity ();
+        
+        glViewport (0,0,w,h);
+        
+        // Make the top-left the origin (see gui/GUI notes.txt):
+        // Note that this only affects vertex operations − direct rasterisation operations are
+        // unaffected!
+        glOrtho (0.0,w, h,0.0, -1.0, 1.0);
+        
+        glMatrixMode(GL_MODELVIEW);
+        return false;
+    }
+    
+    static this() {
+        logger = Log.getLogger ("mde.setup.Screen");
+        
+        vidOpts = new OptionsVideo;
+        Options.addOptionsClass (vidOpts, "video");
+    }
+    
+    // DATA:
+private:
+    uint flags = 0;
+    Drawable[] drawables;
+    Logger logger;
+    OptionsVideo vidOpts;
+}