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