comparison 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
comparison
equal deleted inserted replaced
84:e0f1ec7fe73a 85:56c0ddd90193
1 /* LICENSE BLOCK
2 Part of mde: a Modular D game-oriented Engine
3 Copyright © 2007-2008 Diggory Hardy
4
5 This program is free software: you can redistribute it and/or modify it under the terms
6 of the GNU General Public License as published by the Free Software Foundation, either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
10 without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 See the GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>. */
15
16 /** Screen: SDL & OpenGL setup/cleanup, drawing.
17 *
18 * Has an interface by which other code can hook in for drawing and resize notifications. */
19 module mde.setup.Screen;
20
21 import mde.setup.exception;
22 import mde.lookup.Options;
23 import imde = mde.imde;
24 debug (drawGlyphCache) import mde.font.font;
25
26 import tango.util.log.Log : Log, Logger;
27 import tango.stdc.stringz;
28 import tango.time.Time; // TimeSpan (type only; unused)
29
30 import derelict.sdl.sdl;
31 import derelict.opengl.gl; // for loading a later gl version
32 import derelict.util.exception;
33
34 /** Currently just used as a namespace. Potential for multiple screen support later? */
35 struct Screen {
36 // TYPES (these mustn't be static):
37 /** Interface for anything hooking into the screen for drawing, etc. */
38 interface Drawable {
39 /** Called on window creation and whenever the window manager resizes the window, before
40 * the new size is set. The new size is passed.
41 *
42 * The parameters were passed as ref to allow the called function to change them, but SDL
43 * didn't appear able to resize the window (on X11), so this was dropped. */
44 void sizeEvent (int w, int h);
45
46 /** Called you guess when :-) */
47 void draw ();
48 }
49
50 /** All video options. */
51 class OptionsVideo : Options {
52 mixin (impl!("bool fullscreen,hardware,resizable,noFrame; int screenW,screenH,windowW,windowH;"));
53 }
54
55 static:
56 /** Init function to initialize SDL. */
57 StageState init () { // init func
58 // Initialise SDL
59 if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK /+| SDL_INIT_EVENTTHREAD+/)) {
60 logger.fatal ("SDL initialisation failed:");
61 char* msg = SDL_GetError ();
62 logger.fatal (msg ? fromStringz(msg) : "no reason available");
63
64 throw new InitException ("SDL Initialization failed");
65 }
66 debug logger.trace ("SDL initialised");
67 return StageState.ACTIVE;
68 }
69 /** SDL shutdown */
70 StageState cleanup () {
71 SDL_Quit();
72 return StageState.INACTIVE;
73 }
74 /** Init function to set up a window with OpenGL support. */
75 StageState initWindow () {
76 //BEGIN Create window and initialize OpenGL
77 // Window creation flags and size
78 flags = SDL_OPENGL;
79 if (vidOpts.hardware) flags |= SDL_HWSURFACE | SDL_DOUBLEBUF;
80 else flags |= SDL_SWSURFACE;
81 int w, h;
82 if (vidOpts.fullscreen) {
83 flags |= SDL_FULLSCREEN;
84 w = vidOpts.screenW;
85 h = vidOpts.screenH;
86 }
87 else {
88 if (vidOpts.resizable) flags |= SDL_RESIZABLE;
89 if (vidOpts.noFrame) flags |= SDL_NOFRAME;
90 w = vidOpts.windowW;
91 h = vidOpts.windowH;
92 }
93
94 // OpenGL attributes
95 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
96 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
97 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
98 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
99 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
100
101 // Open a window
102 debug logger.trace ("Opening a window (this can crash if the libraries are messed up)");
103 if (setWindow (w, h)) {
104 throw new InitException ("Failed to open a window");
105 }
106
107 /* Now (must be done after GL context is created) we can try to load later version.
108 * The initial loading provides opengl 1.1 features.
109 *
110 * 1.4 is now used for glBlendColor (coloured text).
111 *
112 * Currently the latest version used is 1.3; adjust this as necessary. However, before using
113 * features from any OpenGL version > 1.1 a check must be made on what was loaded by calling
114 * DerelictGL.availableVersion(). Note that availableVersion() could be used instead to load
115 * the highest supported version but this way we know what we're getting. */
116 if (DerelictGL.availableVersion < GLVersion.Version13) {
117 throw new InitException ("Required at least OpenGL 1.3; didn't get this.");
118 }
119 /+try {
120 DerelictGL.loadVersions(GLVersion.Version14);
121 } catch (SharedLibProcLoadException e) {
122 logger.warn ("Loading OpenGL version 1.4 failed:");
123 logger.warn (e.msg);
124
125 setInitFailure ();
126 return;
127 }+/
128
129 // OpenGL stuff:
130 glDisable(GL_LIGHTING);
131 glDisable(GL_DEPTH_TEST);
132 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
133 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
134 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
135 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
136 glEnable(GL_TEXTURE_2D);
137 glShadeModel(GL_SMOOTH);
138
139 glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
140
141 glMatrixMode(GL_MODELVIEW);
142 glLoadIdentity();
143
144 // Used for font rendering:
145 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
146 glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
147 //NOTE: wrap mode may have an effect, but shouldn't be noticed...
148
149 // Window-manager settings
150 SDL_WM_SetCaption (toStringz ("mde"), null);
151 // SDL_WM_GrabInput (use later)
152 //END Create window and initialize OpenGL
153 return StageState.ACTIVE;
154 }
155
156
157 /** Called when a resize event occurs (when the window manager resizes the window). */
158 void resizeEvent (int w, int h) {
159 // Save new size to config
160 if (vidOpts.fullscreen) { // probably resizeEvent only called when not fullscreen
161 vidOpts.set!(int) ("screenW", w);
162 vidOpts.set!(int) ("screenH", h);
163 } else {
164 vidOpts.set!(int) ("windowW", w);
165 vidOpts.set!(int) ("windowH", h);
166 }
167
168 if (setWindow (w,h))
169 imde.run = false;
170 }
171
172 /** Add a drawable element to the screen (see Drawable interface).
173 *
174 * Should be called before Init to get initial size. Currently no means to remove drawables,
175 * and not really designed for more than one. */
176 void addDrawable (Drawable d) {
177 drawables ~= d;
178 }
179
180 /** Drawing function */
181 void draw (TimeSpan) {
182 glClear(GL_COLOR_BUFFER_BIT);
183
184 foreach (Drawable d; drawables)
185 d.draw;
186
187 debug (drawGlyphCache) {
188 logger.trace ("Drawing font texture");
189 FontStyle.drawTexture;
190 }
191
192 // Error check:
193 GLenum err = glGetError();
194 while (err != GL_NO_ERROR) {
195 logger.error ("GL error: {}", err);
196 err = glGetError();
197 }
198
199 glFinish(); // Use Finish rather than Flush to make sure gl is ready to swap buffers
200 SDL_GL_SwapBuffers();
201 }
202
203 /** Set a new window size. Returns true on failure due to the different ways this must be
204 * handled. */
205 private bool setWindow (int w, int h) {
206 foreach (d; drawables) // Tell all drawables the new window size.
207 d.sizeEvent (w,h);
208
209 debug logger.trace ("Setting video mode {}x{}, 32-bit, flags: {}", w,h,flags);
210 if (SDL_SetVideoMode (w, h, 32, flags) is null) {
211 logger.fatal ("Unable to set video mode:");
212 char* msg = SDL_GetError ();
213 logger.fatal (msg ? fromStringz(msg) : "no reason available");
214
215 // Print a load of info:
216 logger.info ("Available video modes:");
217 SDL_Rect** modes = SDL_ListModes (null, SDL_FULLSCREEN);
218 if (modes is null) logger.info ("None!");
219 else if (modes is cast(SDL_Rect**) -1) logger.info ("All modes are available");
220 else {
221 for (uint i = 0; modes[i] !is null; ++i) {
222 logger.info ("\t{}x{}", modes[i].w, modes[i].h);
223 }
224 }
225
226 SDL_VideoInfo* vi = SDL_GetVideoInfo ();
227 if (vi !is null) {
228 logger.info ("Video info:");
229 logger.info ("Hardware surface support: "~ (vi.flags & SDL_HWSURFACE ? "yes" : "no"));
230 logger.info ("Video memory: {}", vi.video_mem);
231
232 if (vi.vfmt !is null) {
233 logger.info ("Best video mode:");
234 logger.info ("Bits per pixel: {}", vi.vfmt.BitsPerPixel);
235 }
236 }
237
238 return true;
239 }
240
241 // Reset the projection and viewport
242 glMatrixMode (GL_PROJECTION);
243 glLoadIdentity ();
244
245 glViewport (0,0,w,h);
246
247 // Make the top-left the origin (see gui/GUI notes.txt):
248 // Note that this only affects vertex operations − direct rasterisation operations are
249 // unaffected!
250 glOrtho (0.0,w, h,0.0, -1.0, 1.0);
251
252 glMatrixMode(GL_MODELVIEW);
253 return false;
254 }
255
256 static this() {
257 logger = Log.getLogger ("mde.setup.Screen");
258
259 vidOpts = new OptionsVideo;
260 Options.addOptionsClass (vidOpts, "video");
261 }
262
263 // DATA:
264 private:
265 uint flags = 0;
266 Drawable[] drawables;
267 Logger logger;
268 OptionsVideo vidOpts;
269 }