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