abe25cc37ddfba069ee2054830e24f3091241e48
[dosdemo] / src / glut / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <ctype.h>
4 #include <assert.h>
5 #include "miniglut.h"
6 #include "demo.h"
7 #include "gfx.h"
8 #include "gfxutil.h"
9 #include "timer.h"
10 #include "audio.h"
11 #include "cfgopt.h"
12 #include "cgmath/cgmath.h"
13 #include "util.h"
14
15 static void display(void);
16 static void idle(void);
17 static void reshape(int x, int y);
18 static void keydown(unsigned char key, int x, int y);
19 static void keyup(unsigned char key, int x, int y);
20 static void skeydown(int key, int x, int y);
21 static void skeyup(int key, int x, int y);
22 static int translate_special(int skey);
23 static void mouse_button(int bn, int st, int x, int y);
24 static void mouse_motion(int x, int y);
25 static void sball_motion(int x, int y, int z);
26 static void sball_rotate(int x, int y, int z);
27 static void sball_button(int bn, int st);
28 static void recalc_sball_matrix(float *xform);
29 static unsigned int next_pow2(unsigned int x);
30 static void set_fullscreen(int fs);
31 static void set_vsync(int vsync);
32
33 int have_joy;
34 unsigned int joy_bnstate, joy_bndiff, joy_bnpress;
35
36 #define MODE(w, h)      \
37         {0, w, h, 16, w * 2, 5, 6, 5, 11, 5, 0, 0xf800, 0x7e0, 0x1f, 0xbadf00d, 2, 0}
38 static struct video_mode vmodes[] = {
39         MODE(320, 240), MODE(400, 300), MODE(512, 384), MODE(640, 480),
40         MODE(800, 600), MODE(1024, 768), MODE(1280, 960), MODE(1280, 1024),
41         MODE(1920, 1080), MODE(1600, 1200), MODE(1920, 1200)
42 };
43 static struct video_mode *cur_vmode;
44
45 static unsigned int num_pressed;
46 static unsigned char keystate[256];
47
48 static unsigned long start_time;
49 static unsigned int modkeys;
50
51 static int win_width, win_height;
52 static float win_aspect;
53 static unsigned int tex;
54
55 #ifdef __unix__
56 #include <GL/glx.h>
57 static Display *xdpy;
58 static Window xwin;
59
60 static void (*glx_swap_interval_ext)();
61 static void (*glx_swap_interval_sgi)();
62 #endif
63 #ifdef _WIN32
64 #include <windows.h>
65 static PROC wgl_swap_interval_ext;
66 #endif
67
68 static int use_sball;
69 static cgm_vec3 pos = {0, 0, 0};
70 static cgm_quat rot = {0, 0, 0, 1};
71
72
73 int main(int argc, char **argv)
74 {
75         glutInit(&argc, argv);
76
77         if(glutGet(GLUT_SCREEN_HEIGHT) <= 1024) {
78                 glutInitWindowSize(640, 480);
79         } else {
80                 glutInitWindowSize(1280, 960);
81         }
82         glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
83         glutCreateWindow("Mindlapse");
84
85         glutDisplayFunc(display);
86         glutIdleFunc(idle);
87         glutReshapeFunc(reshape);
88         glutKeyboardFunc(keydown);
89         glutKeyboardUpFunc(keyup);
90         glutSpecialFunc(skeydown);
91         glutSpecialUpFunc(skeyup);
92         glutMouseFunc(mouse_button);
93         glutMotionFunc(mouse_motion);
94         glutPassiveMotionFunc(mouse_motion);
95         glutSpaceballMotionFunc(sball_motion);
96         glutSpaceballRotateFunc(sball_rotate);
97         glutSpaceballButtonFunc(sball_button);
98
99         glutSetCursor(GLUT_CURSOR_NONE);
100
101         glEnable(GL_TEXTURE_2D);
102         glEnable(GL_CULL_FACE);
103
104
105         if(!set_video_mode(match_video_mode(FB_WIDTH, FB_HEIGHT, FB_BPP), 1)) {
106                 return 1;
107         }
108
109 #ifdef __unix__
110         xdpy = glXGetCurrentDisplay();
111         xwin = glXGetCurrentDrawable();
112
113         if(!(glx_swap_interval_ext = glXGetProcAddress((unsigned char*)"glXSwapIntervalEXT"))) {
114                 glx_swap_interval_sgi = glXGetProcAddress((unsigned char*)"glXSwapIntervalSGI");
115         }
116 #endif
117 #ifdef _WIN32
118         wgl_swap_interval_ext = wglGetProcAddress("wglSwapIntervalEXT");
119 #endif
120
121         reshape(glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT));
122
123         if(au_init() == -1) {
124                 return 1;
125         }
126         time_msec = 0;
127         if(demo_init(argc, argv) == -1) {
128                 return 1;
129         }
130         atexit(demo_cleanup);
131
132         if(opt.fullscreen) {
133                 set_fullscreen(opt.fullscreen);
134         }
135
136         reset_timer();
137
138         glutMainLoop();
139         return 0;
140 }
141
142 void demo_quit(void)
143 {
144         exit(0);
145 }
146
147 struct video_mode *video_modes(void)
148 {
149         return vmodes;
150 }
151
152 int num_video_modes(void)
153 {
154         return sizeof vmodes / sizeof *vmodes;
155 }
156
157 struct video_mode *get_video_mode(int idx)
158 {
159         if(idx == VMODE_CURRENT) {
160                 return cur_vmode;
161         }
162         return vmodes + idx;
163 }
164
165 int match_video_mode(int xsz, int ysz, int bpp)
166 {
167         struct video_mode *vm = vmodes;
168         int i, count = num_video_modes();
169
170         for(i=0; i<count; i++) {
171                 if(vm->xsz == xsz && vm->ysz == ysz && vm->bpp == bpp) {
172                         return i;
173                 }
174                 vm++;
175         }
176         return -1;
177 }
178
179 static int tex_xsz, tex_ysz;
180 static uint32_t *convbuf;
181 static int convbuf_size;
182
183 void *set_video_mode(int idx, int nbuf)
184 {
185         struct video_mode *vm = vmodes + idx;
186
187         if(cur_vmode == vm) {
188                 return vmem;
189         }
190
191         glGenTextures(1, &tex);
192         glBindTexture(GL_TEXTURE_2D, tex);
193
194         tex_xsz = next_pow2(vm->xsz);
195         tex_ysz = next_pow2(vm->ysz);
196         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_xsz, tex_ysz, 0, GL_RGBA,
197                         GL_UNSIGNED_BYTE, 0);
198         if(opt.scaler == SCALER_LINEAR) {
199                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
200                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
201         } else {
202                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
203                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
204         }
205
206         glMatrixMode(GL_TEXTURE);
207         glLoadIdentity();
208         glScalef((float)vm->xsz / tex_xsz, (float)vm->ysz / tex_ysz, 1);
209
210         if(vm->xsz * vm->ysz > convbuf_size) {
211                 convbuf_size = vm->xsz * vm->ysz;
212                 free(convbuf);
213                 convbuf = malloc(convbuf_size * sizeof *convbuf);
214         }
215
216         if(demo_resizefb(vm->xsz, vm->ysz, vm->bpp) == -1) {
217                 fprintf(stderr, "failed to allocate virtual framebuffer\n");
218                 return 0;
219         }
220         vmem = fb_pixels;
221
222         cur_vmode = vm;
223         return vmem;
224 }
225
226 void wait_vsync(void)
227 {
228 }
229
230 void blit_frame(void *pixels, int vsync)
231 {
232         int i;
233         uint32_t *dptr = convbuf;
234         uint16_t *sptr = pixels;
235         static int prev_vsync = -1;
236
237         if(vsync != prev_vsync) {
238                 set_vsync(vsync);
239                 prev_vsync = vsync;
240         }
241
242         demo_post_draw(pixels);
243
244         for(i=0; i<FB_WIDTH * FB_HEIGHT; i++) {
245                 int r = UNPACK_R16(*sptr);
246                 int g = UNPACK_G16(*sptr);
247                 int b = UNPACK_B16(*sptr);
248                 *dptr++ = PACK_RGB32(b, g, r);
249                 sptr++;
250         }
251
252         glBindTexture(GL_TEXTURE_2D, tex);
253         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, FB_WIDTH, FB_HEIGHT, GL_RGBA,
254                         GL_UNSIGNED_BYTE, convbuf);
255
256         glMatrixMode(GL_MODELVIEW);
257         glLoadIdentity();
258         if(win_aspect >= FB_ASPECT) {
259                 glScalef(FB_ASPECT / win_aspect, 1, 1);
260         } else {
261                 glScalef(1, win_aspect / FB_ASPECT, 1);
262         }
263
264         glClear(GL_COLOR_BUFFER_BIT);
265
266         glBegin(GL_QUADS);
267         glTexCoord2f(0, 1);
268         glVertex2f(-1, -1);
269         glTexCoord2f(1, 1);
270         glVertex2f(1, -1);
271         glTexCoord2f(1, 0);
272         glVertex2f(1, 1);
273         glTexCoord2f(0, 0);
274         glVertex2f(-1, 1);
275         glEnd();
276
277         glutSwapBuffers();
278         assert(glGetError() == GL_NO_ERROR);
279 }
280
281 int kb_isdown(int key)
282 {
283         switch(key) {
284         case KB_ANY:
285                 return num_pressed;
286
287         case KB_ALT:
288                 return keystate[KB_LALT] + keystate[KB_RALT];
289
290         case KB_CTRL:
291                 return keystate[KB_LCTRL] + keystate[KB_RCTRL];
292         }
293
294         if(isalpha(key)) {
295                 key = tolower(key);
296         }
297         return keystate[key];
298 }
299
300 /* timer */
301 void init_timer(int res_hz)
302 {
303 }
304
305 void reset_timer(void)
306 {
307         start_time = glutGet(GLUT_ELAPSED_TIME);
308 }
309
310 unsigned long get_msec(void)
311 {
312         return glutGet(GLUT_ELAPSED_TIME) - start_time;
313 }
314
315 #ifdef _WIN32
316 #include <windows.h>
317
318 void sleep_msec(unsigned long msec)
319 {
320         Sleep(msec);
321 }
322
323 #else
324 #include <unistd.h>
325
326 void sleep_msec(unsigned long msec)
327 {
328         usleep(msec * 1000);
329 }
330 #endif
331
332 static void display(void)
333 {
334         recalc_sball_matrix(sball_matrix);
335
336         time_msec = get_msec();
337         demo_draw();
338 }
339
340 static void idle(void)
341 {
342         glutPostRedisplay();
343 }
344
345 static void reshape(int x, int y)
346 {
347         win_width = x;
348         win_height = y;
349         win_aspect = (float)x / (float)y;
350         glViewport(0, 0, x, y);
351 }
352
353 static void keydown(unsigned char key, int x, int y)
354 {
355         modkeys = glutGetModifiers();
356
357         if((key == '\n' || key == '\r') && (modkeys & GLUT_ACTIVE_ALT)) {
358                 opt.fullscreen ^= 1;
359                 set_fullscreen(opt.fullscreen);
360                 return;
361         }
362         keystate[key] = 1;
363         demo_keyboard(key, 1);
364 }
365
366 static void keyup(unsigned char key, int x, int y)
367 {
368         keystate[key] = 0;
369         demo_keyboard(key, 0);
370 }
371
372 static void skeydown(int key, int x, int y)
373 {
374         if(key == GLUT_KEY_F5) {
375                 opt.scaler = (opt.scaler + 1) % NUM_SCALERS;
376
377                 if(opt.scaler == SCALER_LINEAR) {
378                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
379                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
380                 } else {
381                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
382                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
383                 }
384         }
385         key = translate_special(key);
386         keystate[key] = 1;
387         demo_keyboard(key, 1);
388 }
389
390 static void skeyup(int key, int x, int y)
391 {
392         key = translate_special(key);
393         keystate[key] = 0;
394         demo_keyboard(key, 0);
395 }
396
397 static int translate_special(int skey)
398 {
399         switch(skey) {
400         case 127:
401                 return 127;
402         case GLUT_KEY_LEFT:
403                 return KB_LEFT;
404         case GLUT_KEY_RIGHT:
405                 return KB_RIGHT;
406         case GLUT_KEY_UP:
407                 return KB_UP;
408         case GLUT_KEY_DOWN:
409                 return KB_DOWN;
410         case GLUT_KEY_PAGE_UP:
411                 return KB_PGUP;
412         case GLUT_KEY_PAGE_DOWN:
413                 return KB_PGDN;
414         case GLUT_KEY_HOME:
415                 return KB_HOME;
416         case GLUT_KEY_END:
417                 return KB_END;
418         default:
419                 if(skey >= GLUT_KEY_F1 && skey <= GLUT_KEY_F12) {
420                         return KB_F1 + skey - GLUT_KEY_F1;
421                 }
422         }
423         return 0;
424 }
425
426 static void map_mouse_pos(int *xp, int *yp)
427 {
428         int x = *xp;
429         int y = *yp;
430
431         /* TODO */
432         *xp = x * FB_WIDTH / win_width;
433         *yp = y * FB_HEIGHT / win_height;
434 }
435
436 static void mouse_button(int bn, int st, int x, int y)
437 {
438         int bit;
439
440         map_mouse_pos(&x, &y);
441         mouse_x = x;
442         mouse_y = y;
443
444         switch(bn) {
445         case GLUT_LEFT_BUTTON:
446                 bit = 0;
447                 break;
448         case GLUT_RIGHT_BUTTON:
449                 bit = 1;
450                 break;
451         case GLUT_MIDDLE_BUTTON:
452                 bit = 2;
453                 break;
454         }
455
456         if(st == GLUT_DOWN) {
457                 mouse_bmask |= 1 << bit;
458         } else {
459                 mouse_bmask &= ~(1 << bit);
460         }
461 }
462
463 static void mouse_motion(int x, int y)
464 {
465         map_mouse_pos(&x, &y);
466         mouse_x = x;
467         mouse_y = y;
468 }
469
470 static void sball_motion(int x, int y, int z)
471 {
472         pos.x += x * 0.001f;
473         pos.y += y * 0.001f;
474         pos.z -= z * 0.001f;
475
476 }
477
478 static void sball_rotate(int rx, int ry, int rz)
479 {
480         if(rx | ry | rz) {
481                 float s = (float)rsqrt(rx * rx + ry * ry + rz * rz);
482                 cgm_qrotate(&rot, 0.001f / s, rx * s, ry * s, -rz * s);
483         }
484 }
485
486 static void sball_button(int bn, int st)
487 {
488         pos.x = pos.y = pos.z = 0;
489         rot.x = rot.y = rot.z = 0;
490         rot.w = 1;
491 }
492
493 static void recalc_sball_matrix(float *xform)
494 {
495         cgm_mrotation_quat(xform, &rot);
496         xform[12] = pos.x;
497         xform[13] = pos.y;
498         xform[14] = pos.z;
499 }
500
501
502 static unsigned int next_pow2(unsigned int x)
503 {
504         x--;
505         x |= x >> 1;
506         x |= x >> 2;
507         x |= x >> 4;
508         x |= x >> 8;
509         x |= x >> 16;
510         return x + 1;
511 }
512
513 static void set_fullscreen(int fs)
514 {
515         static int win_x, win_y;
516
517         if(fs) {
518                 win_x = glutGet(GLUT_WINDOW_WIDTH);
519                 win_y = glutGet(GLUT_WINDOW_HEIGHT);
520                 glutFullScreen();
521         } else {
522                 glutReshapeWindow(win_x, win_y);
523         }
524 }
525
526 #ifdef __unix__
527 static void set_vsync(int vsync)
528 {
529         vsync = vsync ? 1 : 0;
530         if(glx_swap_interval_ext) {
531                 glx_swap_interval_ext(xdpy, xwin, vsync);
532         } else if(glx_swap_interval_sgi) {
533                 glx_swap_interval_sgi(vsync);
534         }
535 }
536 #endif
537 #ifdef WIN32
538 static void set_vsync(int vsync)
539 {
540         if(wgl_swap_interval_ext) {
541                 wgl_swap_interval_ext(vsync ? 1 : 0);
542         }
543 }
544 #endif