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