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