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