36e626a519712ddc37f107701cea40667956c15c
[neverball] / share / video.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include "video.h"
16 #include "vec3.h"
17 #include "glext.h"
18 #include "config.h"
19 #include "syswm.h"
20 #include "sync.h"
21
22 /*---------------------------------------------------------------------------*/
23
24 int video_init(const char *title, const char *icon)
25 {
26     SDL_QuitSubSystem(SDL_INIT_VIDEO);
27
28     if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1)
29     {
30         fprintf(stderr, "%s\n", SDL_GetError());
31         return 0;
32     }
33
34     /* This has to happen before mode setting... */
35
36     set_SDL_icon(icon);
37
38     /* Initialize the video. */
39
40     if (!video_mode(config_get_d(CONFIG_FULLSCREEN),
41                     config_get_d(CONFIG_WIDTH),
42                     config_get_d(CONFIG_HEIGHT)))
43     {
44         fprintf(stderr, "%s\n", SDL_GetError());
45         return 0;
46     }
47
48     /* ...and this has to happen after it. */
49
50     set_EWMH_icon(icon);
51
52     SDL_WM_SetCaption(title, title);
53
54     return 1;
55 }
56
57 /*---------------------------------------------------------------------------*/
58
59 int check_extension(const char *needle)
60 {
61     const GLubyte *haystack, *c;
62
63     /* Search for the given string in the OpenGL extension strings. */
64
65     for (haystack = glGetString(GL_EXTENSIONS); *haystack; haystack++)
66     {
67         for (c = (const GLubyte *) needle; *c && *haystack; c++, haystack++)
68             if (*c != *haystack)
69                 break;
70
71         if ((*c == 0) && (*haystack == ' ' || *haystack == '\0'))
72             return 1;
73     }
74
75     return 0;
76 }
77
78 int video_mode(int f, int w, int h)
79 {
80     int stereo  = config_get_d(CONFIG_STEREO)      ? 1 : 0;
81     int stencil = config_get_d(CONFIG_REFLECTION)  ? 1 : 0;
82     int buffers = config_get_d(CONFIG_MULTISAMPLE) ? 1 : 0;
83     int samples = config_get_d(CONFIG_MULTISAMPLE);
84     int vsync   = config_get_d(CONFIG_VSYNC)       ? 1 : 0;
85
86     SDL_GL_SetAttribute(SDL_GL_STEREO,             stereo);
87     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,       stencil);
88     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, buffers);
89     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
90     SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL,       vsync);
91
92     /* Require 16-bit double buffer with 16-bit depth buffer. */
93
94     SDL_GL_SetAttribute(SDL_GL_RED_SIZE,     5);
95     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,   5);
96     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,    5);
97     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,  16);
98     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
99
100     /* Try to set the currently specified mode. */
101
102     if (SDL_SetVideoMode(w, h, 0, SDL_OPENGL | (f ? SDL_FULLSCREEN : 0)))
103     {
104         config_set_d(CONFIG_FULLSCREEN, f);
105         config_set_d(CONFIG_WIDTH,      w);
106         config_set_d(CONFIG_HEIGHT,     h);
107
108         glViewport(0, 0, w, h);
109         glClearColor(0.0f, 0.0f, 0.1f, 0.0f);
110
111         glEnable(GL_NORMALIZE);
112         glEnable(GL_CULL_FACE);
113         glEnable(GL_DEPTH_TEST);
114         glEnable(GL_TEXTURE_2D);
115         glEnable(GL_LIGHTING);
116         glEnable(GL_BLEND);
117
118         glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,
119                       GL_SEPARATE_SPECULAR_COLOR);
120         glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
121
122         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
123         glDepthFunc(GL_LEQUAL);
124
125         /* If GL supports multisample, and SDL got a multisample buffer... */
126
127 #ifdef GL_ARB_multisample
128         if (check_extension("ARB_multisample"))
129         {
130             SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
131             if (buffers)
132                 glEnable(GL_MULTISAMPLE_ARB);
133         }
134 #endif
135
136         glReadBuffer(GL_FRONT);
137
138         /* Attempt manual swap control if SDL's is broken. */
139
140         if (vsync && SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &vsync) == -1)
141             sync_init();
142
143         return 1;
144     }
145
146     /* If the mode failed, try it without stereo. */
147
148     else if (stereo)
149     {
150         config_set_d(CONFIG_STEREO, 0);
151         return video_mode(f, w, h);
152     }
153
154     /* If the mode failed, try decreasing the level of multisampling. */
155
156     else if (buffers)
157     {
158         config_set_d(CONFIG_MULTISAMPLE, samples / 2);
159         return video_mode(f, w, h);
160     }
161
162     /* If that mode failed, try it without reflections. */
163
164     else if (stencil)
165     {
166         config_set_d(CONFIG_REFLECTION, 0);
167         return video_mode(f, w, h);
168     }
169
170     /* If THAT mode failed, punt. */
171
172     return 0;
173 }
174
175 /*---------------------------------------------------------------------------*/
176
177 static float ms     = 0;
178 static int   fps    = 0;
179 static int   last   = 0;
180 static int   ticks  = 0;
181 static int   frames = 0;
182
183 int  video_perf(void)
184 {
185     return fps;
186 }
187
188 void video_swap(void)
189 {
190     int dt;
191
192     SDL_GL_SwapBuffers();
193
194     /* Accumulate time passed and frames rendered. */
195
196     dt = (int) SDL_GetTicks() - last;
197
198     frames +=  1;
199     ticks  += dt;
200     last   += dt;
201
202     /* Average over 250ms. */
203
204     if (ticks > 1000)
205     {
206         /* Round the frames-per-second value to the nearest integer. */
207
208         double k = 1000.0 * frames / ticks;
209         double f = floor(k);
210         double c = ceil (k);
211
212         /* Compute frame time and frames-per-second stats. */
213
214         fps = (int) ((c - k < k - f) ? c : f);
215         ms  = (float) ticks / (float) frames;
216
217         /* Reset the counters for the next update. */
218
219         frames = 0;
220         ticks  = 0;
221
222         /* Output statistics if configured. */
223
224         if (config_get_d(CONFIG_STATS))
225             fprintf(stdout, "%4d %8.4f\n", fps, ms);
226     }
227 }
228
229 /*---------------------------------------------------------------------------*/
230
231 static int grabbed = 0;
232
233 void video_set_grab(int w)
234 {
235     if (w)
236     {
237         SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
238
239         SDL_WarpMouse(config_get_d(CONFIG_WIDTH)  / 2,
240                       config_get_d(CONFIG_HEIGHT) / 2);
241
242         SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
243     }
244
245     SDL_WM_GrabInput(SDL_GRAB_ON);
246     SDL_ShowCursor(SDL_DISABLE);
247
248     grabbed = 1;
249 }
250
251 void video_clr_grab(void)
252 {
253     SDL_WM_GrabInput(SDL_GRAB_OFF);
254     SDL_ShowCursor(SDL_ENABLE);
255     grabbed = 0;
256 }
257
258 int  video_get_grab(void)
259 {
260     return grabbed;
261 }
262
263 /*---------------------------------------------------------------------------*/
264
265 void video_push_persp(float fov, float n, float f)
266 {
267     GLdouble m[4][4];
268
269     GLdouble r = fov / 2 * V_PI / 180;
270     GLdouble s = sin(r);
271     GLdouble c = cos(r) / s;
272
273     GLdouble a = ((GLdouble) config_get_d(CONFIG_WIDTH) /
274                   (GLdouble) config_get_d(CONFIG_HEIGHT));
275
276     glMatrixMode(GL_PROJECTION);
277     {
278         glPushMatrix();
279         glLoadIdentity();
280
281         m[0][0] =  c/a;
282         m[0][1] =  0.0;
283         m[0][2] =  0.0;
284         m[0][3] =  0.0;
285         m[1][0] =  0.0;
286         m[1][1] =    c;
287         m[1][2] =  0.0;
288         m[1][3] =  0.0;
289         m[2][0] =  0.0;
290         m[2][1] =  0.0;
291         m[2][2] = -(f + n) / (f - n);
292         m[2][3] = -1.0;
293         m[3][0] =  0.0;
294         m[3][1] =  0.0;
295         m[3][2] = -2.0 * n * f / (f - n);
296         m[3][3] =  0.0;
297
298         glMultMatrixd(&m[0][0]);
299     }
300     glMatrixMode(GL_MODELVIEW);
301 }
302
303 void video_push_ortho(void)
304 {
305     GLdouble w = (GLdouble) config_get_d(CONFIG_WIDTH);
306     GLdouble h = (GLdouble) config_get_d(CONFIG_HEIGHT);
307
308     glMatrixMode(GL_PROJECTION);
309     {
310         glPushMatrix();
311         glLoadIdentity();
312         glOrtho(0.0, w, 0.0, h, -1.0, +1.0);
313     }
314     glMatrixMode(GL_MODELVIEW);
315 }
316
317 void video_pop_matrix(void)
318 {
319     glMatrixMode(GL_PROJECTION);
320     {
321         glPopMatrix();
322     }
323     glMatrixMode(GL_MODELVIEW);
324 }
325
326 void video_clear(void)
327 {
328     if (config_get_d(CONFIG_REFLECTION))
329         glClear(GL_COLOR_BUFFER_BIT |
330                 GL_DEPTH_BUFFER_BIT |
331                 GL_STENCIL_BUFFER_BIT);
332     else
333         glClear(GL_COLOR_BUFFER_BIT |
334                 GL_DEPTH_BUFFER_BIT);
335 }
336
337 /*---------------------------------------------------------------------------*/