Mercurial > projects > mde
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; +}