Flattened the alpha of the background gradients to allow the elimination of color...
[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 PFNGLACTIVETEXTUREARBPROC glActiveTexture_;
60
61 int check_extension(const char *needle)
62 {
63     const GLubyte *haystack, *c;
64
65     /* Search for the given string in the OpenGL extension strings. */
66
67     for (haystack = glGetString(GL_EXTENSIONS); *haystack; haystack++)
68     {
69         for (c = (const GLubyte *) needle; *c && *haystack; c++, haystack++)
70             if (*c != *haystack)
71                 break;
72
73         if ((*c == 0) && (*haystack == ' ' || *haystack == '\0'))
74             return 1;
75     }
76
77     return 0;
78 }
79
80 int video_mode(int f, int w, int h)
81 {
82     int stereo  = config_get_d(CONFIG_STEREO)      ? 1 : 0;
83     int stencil = config_get_d(CONFIG_REFLECTION)  ? 1 : 0;
84     int buffers = config_get_d(CONFIG_MULTISAMPLE) ? 1 : 0;
85     int samples = config_get_d(CONFIG_MULTISAMPLE);
86     int vsync   = config_get_d(CONFIG_VSYNC)       ? 1 : 0;
87
88     SDL_GL_SetAttribute(SDL_GL_STEREO,             stereo);
89     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE,       stencil);
90     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, buffers);
91     SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
92     SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL,       vsync);
93
94     /* Require 16-bit double buffer with 16-bit depth buffer. */
95
96     SDL_GL_SetAttribute(SDL_GL_RED_SIZE,     5);
97     SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,   5);
98     SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,    5);
99     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,  16);
100     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
101
102     /* Try to set the currently specified mode. */
103
104     if (SDL_SetVideoMode(w, h, 0, SDL_OPENGL | (f ? SDL_FULLSCREEN : 0)))
105     {
106         config_set_d(CONFIG_FULLSCREEN, f);
107         config_set_d(CONFIG_WIDTH,      w);
108         config_set_d(CONFIG_HEIGHT,     h);
109
110         glViewport(0, 0, w, h);
111         glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
112
113         glEnable(GL_NORMALIZE);
114         glEnable(GL_CULL_FACE);
115         glEnable(GL_DEPTH_TEST);
116         glEnable(GL_TEXTURE_2D);
117         glEnable(GL_LIGHTING);
118         glEnable(GL_BLEND);
119
120         glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,
121                       GL_SEPARATE_SPECULAR_COLOR);
122
123         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
124         glDepthFunc(GL_LEQUAL);
125
126         /* If GL supports multisample, and SDL got a multisample buffer... */
127
128 #ifdef GL_ARB_multisample
129         if (check_extension("ARB_multisample"))
130         {
131             SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &buffers);
132             if (buffers)
133                 glEnable(GL_MULTISAMPLE_ARB);
134         }
135 #endif
136
137         if (check_extension("ARB_multitexture"))
138         {
139             union
140             {
141                 void *ob;
142                 PFNGLACTIVETEXTUREARBPROC fn;
143             } cast;
144
145             cast.ob = SDL_GL_GetProcAddress("glActiveTextureARB");
146             glActiveTexture_ = cast.fn;
147         }
148
149         /* Attempt manual swap control if SDL's is broken. */
150
151         if (vsync && SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &vsync) == -1)
152             sync_init();
153
154         return 1;
155     }
156
157     /* If the mode failed, try it without stereo. */
158
159     else if (stereo)
160     {
161         config_set_d(CONFIG_STEREO, 0);
162         return video_mode(f, w, h);
163     }
164
165     /* If the mode failed, try decreasing the level of multisampling. */
166
167     else if (buffers)
168     {
169         config_set_d(CONFIG_MULTISAMPLE, samples / 2);
170         return video_mode(f, w, h);
171     }
172
173     /* If that mode failed, try it without reflections. */
174
175     else if (stencil)
176     {
177         config_set_d(CONFIG_REFLECTION, 0);
178         return video_mode(f, w, h);
179     }
180
181     /* If THAT mode failed, punt. */
182
183     return 0;
184 }
185
186 /*---------------------------------------------------------------------------*/
187
188 static float ms     = 0;
189 static int   fps    = 0;
190 static int   last   = 0;
191 static int   ticks  = 0;
192 static int   frames = 0;
193
194 int  video_perf(void)
195 {
196     return fps;
197 }
198
199 void video_swap(void)
200 {
201     int dt;
202
203     SDL_GL_SwapBuffers();
204
205     /* Accumulate time passed and frames rendered. */
206
207     dt = (int) SDL_GetTicks() - last;
208
209     frames +=  1;
210     ticks  += dt;
211     last   += dt;
212
213     /* Average over 250ms. */
214
215     if (ticks > 1000)
216     {
217         /* Round the frames-per-second value to the nearest integer. */
218
219         double k = 1000.0 * frames / ticks;
220         double f = floor(k);
221         double c = ceil (k);
222
223         /* Compute frame time and frames-per-second stats. */
224
225         fps = (int) ((c - k < k - f) ? c : f);
226         ms  = (float) ticks / (float) frames;
227
228         /* Reset the counters for the next update. */
229
230         frames = 0;
231         ticks  = 0;
232
233         /* Output statistics if configured. */
234
235         if (config_get_d(CONFIG_STATS))
236             fprintf(stdout, "%4d %8.4f\n", fps, ms);
237     }
238 }
239
240 /*---------------------------------------------------------------------------*/
241
242 static int grabbed = 0;
243
244 void video_set_grab(int w)
245 {
246     if (w)
247     {
248         SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
249
250         SDL_WarpMouse(config_get_d(CONFIG_WIDTH)  / 2,
251                       config_get_d(CONFIG_HEIGHT) / 2);
252
253         SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
254     }
255
256     SDL_WM_GrabInput(SDL_GRAB_ON);
257     SDL_ShowCursor(SDL_DISABLE);
258
259     grabbed = 1;
260 }
261
262 void video_clr_grab(void)
263 {
264     SDL_WM_GrabInput(SDL_GRAB_OFF);
265     SDL_ShowCursor(SDL_ENABLE);
266     grabbed = 0;
267 }
268
269 int  video_get_grab(void)
270 {
271     return grabbed;
272 }
273
274 /*---------------------------------------------------------------------------*/
275
276 void video_push_persp(float fov, float n, float f)
277 {
278     GLfloat m[4][4];
279
280     GLfloat r = fov / 2 * V_PI / 180;
281     GLfloat s = sin(r);
282     GLfloat c = cos(r) / s;
283
284     GLfloat a = ((GLfloat) config_get_d(CONFIG_WIDTH) /
285                  (GLfloat) config_get_d(CONFIG_HEIGHT));
286
287     glMatrixMode(GL_PROJECTION);
288     {
289         glPushMatrix();
290         glLoadIdentity();
291
292         m[0][0] = c / a;
293         m[0][1] =  0.0f;
294         m[0][2] =  0.0f;
295         m[0][3] =  0.0f;
296         m[1][0] =  0.0f;
297         m[1][1] =     c;
298         m[1][2] =  0.0f;
299         m[1][3] =  0.0f;
300         m[2][0] =  0.0f;
301         m[2][1] =  0.0f;
302         m[2][2] = -(f + n) / (f - n);
303         m[2][3] = -1.0f;
304         m[3][0] =  0.0f;
305         m[3][1] =  0.0f;
306         m[3][2] = -2.0f * n * f / (f - n);
307         m[3][3] =  0.0f;
308
309         glMultMatrixf(&m[0][0]);
310     }
311     glMatrixMode(GL_MODELVIEW);
312 }
313
314 void video_push_ortho(void)
315 {
316     GLfloat w = (GLfloat) config_get_d(CONFIG_WIDTH);
317     GLfloat h = (GLfloat) config_get_d(CONFIG_HEIGHT);
318
319     glMatrixMode(GL_PROJECTION);
320     {
321         glPushMatrix();
322         glLoadIdentity();
323         glOrtho(0.0, w, 0.0, h, -1.0, +1.0);
324     }
325     glMatrixMode(GL_MODELVIEW);
326 }
327
328 void video_pop_matrix(void)
329 {
330     glMatrixMode(GL_PROJECTION);
331     {
332         glPopMatrix();
333     }
334     glMatrixMode(GL_MODELVIEW);
335 }
336
337 void video_clear(void)
338 {
339     if (config_get_d(CONFIG_REFLECTION))
340         glClear(GL_DEPTH_BUFFER_BIT |
341                 GL_STENCIL_BUFFER_BIT);
342     else
343         glClear(GL_DEPTH_BUFFER_BIT);
344 }
345
346 /*---------------------------------------------------------------------------*/