a9bd0e68612a9b181d414f0283f5e38c4806c991
[lighthouse] / src / main.cc
1 #include <GL/glew.h>
2 #include <GL/freeglut.h>
3
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <math.h>
7 #include <assert.h>
8
9 #include "sdr.h"
10 #include "geom.h"
11 #include "seq.h"
12 #include "ui.h"
13
14 #define BEAM_SHELLS 40
15 #define BEAM_RMIN 0.01
16 #define BEAM_RMAX 0.125
17 #define BEAM_ENERGY 0.01
18 #define BEAM_LEN 16.0
19 #define BEAM_DEF_SPEED  0.1
20 #define BEAM_HEIGHT             4.65
21
22 #define XLOGO_RAD       0.5
23
24 #define LERP(a, b, t)   ((a) + ((b) - (a)) * (t))
25
26 struct Camera {
27         float x, y, z;
28         float theta, phi;
29         float dist;
30 };
31
32 static bool init();
33 static void cleanup();
34
35 static void display();
36 static void light();
37 static void paper_light();
38 static void backdrop(float alpha);
39 static void help();
40
41 static void idle();
42 static void reshape(int x, int y);
43 static void keyboard(unsigned char c, int x, int y);
44 static void keyb_special(int key, int x, int y);
45 static void mbutton(int bn, int state, int x, int y);
46 static void mmotion(int x, int y);
47
48 int win_width, win_height;
49 static bool freecam = true;
50
51 static Camera cam = {0, 0, 0, 0, 0, 10};
52 static unsigned int sdr_beam, sdr_sky;
53 static long start_time;
54 static long anim_stop_time;
55 long tmsec, prev_tmsec, anim_time;
56 bool anim_stopped;
57
58 static const float sil_color[] = {0.05, 0.02, 0.1, 1.0};
59 static const float ink_color[] = {0.05, 0.15, 0.3, 1.0};
60 static const float beam_color[] = {0.5, 0.4, 0.2, 1.0};
61 static const float paper_color[] = {0.9, 0.8, 0.8, 1.0};
62
63 static float beam_angle, beam_speed;
64 static float beam_len;
65 static float xlogo_alpha, xcircle;
66 static float paper;
67
68 static bool show_help, show_ui = true;
69
70 int main(int argc, char **argv)
71 {
72         glutInit(&argc, argv);
73         glutInitWindowSize(800, 600);
74         glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE);
75
76         glutCreateWindow("Faros (press F1 for controls help)");
77
78         glutDisplayFunc(display);
79         glutIdleFunc(idle);
80         glutReshapeFunc(reshape);
81         glutKeyboardFunc(keyboard);
82         glutSpecialFunc(keyb_special);
83         glutMouseFunc(mbutton);
84         glutMotionFunc(mmotion);
85
86         if(!init()) {
87                 return 1;
88         }
89
90         atexit(cleanup);
91         glutMainLoop();
92
93         return 0;
94 }
95
96 static bool init()
97 {
98         glewInit();
99
100         glEnable(GL_CULL_FACE);
101         glEnable(GL_DEPTH_TEST);
102         glEnable(GL_MULTISAMPLE);
103
104 //      glEnable(GL_LIGHTING);
105         glEnable(GL_LIGHT0);
106
107         glEnable(GL_NORMALIZE);
108
109         if(!init_geom()) {
110                 return false;
111         }
112         if(!(sdr_beam = create_program_load("sdr/beam.v.glsl", "sdr/beam.f.glsl")))
113                 return false;
114
115         if(!(sdr_sky = create_program_load("sdr/sky.v.glsl", "sdr/sky.f.glsl"))) {
116                 return false;
117         }
118
119         if(!init_seq()) {
120                 return false;
121         }
122         add_seq_track("beam-speed", INTERP_SIGMOID, EXTRAP_CLAMP, BEAM_DEF_SPEED);
123         add_seq_track("beam-len", INTERP_SIGMOID, EXTRAP_CLAMP, BEAM_LEN);
124         add_seq_track("cam-dist", INTERP_SIGMOID, EXTRAP_CLAMP, 10);
125         add_seq_track("cam-phi", INTERP_SIGMOID, EXTRAP_CLAMP, 0);
126         add_seq_track("cam-theta", INTERP_SIGMOID, EXTRAP_CLAMP, 0);
127         add_seq_track("cam-x", INTERP_SIGMOID, EXTRAP_CLAMP, 0);
128         add_seq_track("cam-y", INTERP_SIGMOID, EXTRAP_CLAMP, 0);
129         add_seq_track("cam-z", INTERP_SIGMOID, EXTRAP_CLAMP, 0);
130         add_seq_track("xlogo", INTERP_SIGMOID, EXTRAP_CLAMP, 0);
131         add_seq_track("xcircle", INTERP_SIGMOID, EXTRAP_CLAMP, 1);
132         add_seq_track("paper", INTERP_SIGMOID, EXTRAP_CLAMP, 0);
133         load_seq("seq");
134
135         freecam = seq_track_empty("cam-theta");
136
137         glClearColor(paper_color[0], paper_color[1], paper_color[2], 1);
138
139         start_time = glutGet(GLUT_ELAPSED_TIME);
140         prev_tmsec = start_time;
141         return true;
142 }
143
144 static void cleanup()
145 {
146         destroy_seq();
147         destroy_geom();
148         free_program(sdr_beam);
149         free_program(sdr_sky);
150 }
151
152 static void display()
153 {
154         tmsec = (long)glutGet(GLUT_ELAPSED_TIME) - start_time;
155         float dt = (tmsec - prev_tmsec) / 1000.0f;
156         prev_tmsec = tmsec;
157
158         anim_time = tmsec;
159         if(anim_stopped) {
160                 dt = 0.0f;
161                 anim_time = anim_stop_time - start_time;
162         }
163
164         paper = get_seq_value("paper", anim_time);
165
166         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
167         backdrop(1.0f - paper);
168
169         glMatrixMode(GL_MODELVIEW);
170         glLoadIdentity();
171
172         if(!freecam) {
173                 cam.dist = get_seq_value("cam-dist", anim_time);
174                 cam.phi = get_seq_value("cam-phi", anim_time);
175                 cam.theta = get_seq_value("cam-theta", anim_time);
176                 cam.x = get_seq_value("cam-x", anim_time);
177                 cam.y = get_seq_value("cam-y", anim_time);
178                 cam.z = get_seq_value("cam-z", anim_time);
179         }
180
181         glTranslatef(0, -2, -cam.dist);
182         glRotatef(cam.phi, 1, 0, 0);
183         glRotatef(cam.theta, 0, 1, 0);
184         glTranslatef(-cam.x, -cam.y, -cam.z);
185
186         float color[3];
187         color[0] = LERP(sil_color[0], ink_color[0], paper);
188         color[1] = LERP(sil_color[1], ink_color[1], paper);
189         color[2] = LERP(sil_color[2], ink_color[2], paper);
190
191         glColor3fv(color);
192         ground();
193         faros();
194
195         beam_len = get_seq_value("beam-len", anim_time);
196         beam_speed = get_seq_value("beam-speed", anim_time);
197         beam_angle += beam_speed * 360.0f * dt;
198
199         xlogo_alpha = get_seq_value("xlogo", anim_time);
200         xcircle = get_seq_value("xcircle", anim_time);
201
202         if(xlogo_alpha > 0.0) {
203                 glPushMatrix();
204                 float beam_angle_rad = beam_angle / 180.0 * M_PI;
205                 float xlogo_dist = beam_len;
206                 float xlogo_pos[3] = {sin(beam_angle_rad), 0, cos(beam_angle_rad)};
207                 glTranslatef(xlogo_pos[0] * xlogo_dist, xlogo_pos[1] * xlogo_dist + BEAM_HEIGHT, xlogo_pos[2] * xlogo_dist);
208                 xlogo(XLOGO_RAD, color, paper_color, xlogo_alpha, xcircle);
209                 glPopMatrix();
210         }
211
212         glPushMatrix();
213         glRotatef(beam_angle, 0, 1, 0);
214         light();
215         glPopMatrix();
216
217         paper_light();
218
219         if(show_ui) {
220                 ui();
221         }
222
223         if(show_help) {
224                 help();
225         }
226
227         glutSwapBuffers();
228 }
229
230 static void light()
231 {
232         float beam_alpha = 1.0 - paper;
233         if(beam_alpha <= 0.0f) return;
234
235         glPushAttrib(GL_ENABLE_BIT);
236         glDisable(GL_CULL_FACE);
237
238         glPushMatrix();
239
240         glTranslatef(0, BEAM_HEIGHT, 0.2);
241         bind_program(sdr_beam);
242         set_uniform_float(sdr_beam, "beam_len", beam_len);
243
244         glEnable(GL_BLEND);
245         glBlendFunc(GL_SRC_ALPHA, GL_ONE);
246         glDepthMask(0);
247
248         for(int i=0; i<BEAM_SHELLS; i++) {
249                 float t = (float)i / (float)(BEAM_SHELLS - 1);
250                 float rad = BEAM_RMIN + (BEAM_RMAX - BEAM_RMIN) * t;
251                 t += 0.00001;
252                 float alpha = BEAM_ENERGY / (t * t);
253
254                 if(i == 0) continue;
255                 glColor4f(beam_color[0], beam_color[1], beam_color[2], alpha * beam_alpha);
256
257                 glutSolidCylinder(rad, beam_len, 12, 1);
258         }
259
260         glDepthMask(1);
261
262         bind_program(0);
263
264         glPopMatrix();
265
266         glPopAttrib();
267 }
268
269 static void paper_light()
270 {
271         glPushAttrib(GL_ENABLE_BIT);
272         glDisable(GL_CULL_FACE);
273
274         glEnable(GL_BLEND);
275         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
276
277         glPushMatrix();
278         glRotatef(beam_angle, 0, 1, 0);
279         glTranslatef(0, BEAM_HEIGHT, 0.3);
280
281         float rad = BEAM_RMIN + (BEAM_RMAX - BEAM_RMIN) * 0.6f;
282         float maxdist = beam_len - 0.6f;
283         float end_rad = XLOGO_RAD * 0.4;
284
285         glLineWidth(3);
286         glBegin(GL_LINES);
287         glColor4f(ink_color[0], ink_color[1], ink_color[2], paper);
288
289         glVertex3f(0, rad, 0);
290         glVertex3f(0, end_rad, maxdist);
291         glVertex3f(0, -rad, 0);
292         glVertex3f(0, -end_rad, maxdist);
293         glEnd();
294
295         glPopMatrix();
296         glPopAttrib();
297 }
298
299 static void backdrop(float alpha)
300 {
301         glPushAttrib(GL_ENABLE_BIT);
302         glEnable(GL_BLEND);
303         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
304
305         glFrontFace(GL_CW);
306         bind_program(sdr_sky);
307         set_uniform_float(sdr_sky, "alpha", alpha);
308         glutSolidSphere(200, 16, 32);
309         bind_program(0);
310         glFrontFace(GL_CCW);
311
312         glPopAttrib();
313 }
314
315 static void help()
316 {
317         static const char *help_lines[] = {
318                 "Camera control",
319                 "   LMB drag ....... rotate",
320                 "   MMB drag ....... pan",
321                 "   RMB drag/wheel . zoom",
322                 "   c .............. toggle free/animated camera",
323                 "   v .............. print current view parameters",
324                 "Animation control",
325                 "   <space> ........ pause time",
326                 "   <backspace> .... restart time",
327                 "   +/- ............ change beam rotation speed and set keyframe",
328                 "   0 .............. clear beam rotation keyframes",
329                 "   [/] ............ change beam length and set keyframe",
330                 "   \\ .............. clear beam length keyframes",
331                 "   <enter> ........ record automatic beam stop transition",
332                 "   K .............. set camera keyframe",
333                 "   <shift>-L ...... clear all camera keyframes",
334                 "   X/Z ............ toggle X logo/circle and set keyframe",
335                 "   <shift>-X/Z .... clear logo/circle keyframes",
336                 "   P .............. toggle paper",
337                 "   <shift>-P ...... clear paper keyframes",
338                 "   ~ .............. dump all animation keyframes to seq_dump",
339                 "Other",
340                 "   F5 ............. toggle UI",
341                 0
342         };
343
344         glPushAttrib(GL_ENABLE_BIT);
345         glDisable(GL_DEPTH_TEST);
346
347         glMatrixMode(GL_MODELVIEW);
348         glPushMatrix();
349         glLoadIdentity();
350         glMatrixMode(GL_PROJECTION);
351         glPushMatrix();
352         glLoadIdentity();
353         glOrtho(0, win_width, win_height, 0, -1, 1);
354
355         glEnable(GL_BLEND);
356         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
357         glBegin(GL_QUADS);
358         glColor4f(0, 0, 0, 0.5);
359         glVertex2f(0, 0);
360         glVertex2f(0, win_height);
361         glVertex2f(win_width, win_height);
362         glVertex2f(win_width, 0);
363         glEnd();
364         glDisable(GL_BLEND);
365
366         int xpos = 20;
367         int ypos = 30;
368         for(int i=0; help_lines[i]; i++) {
369                 glColor3f(0.05, 0.05, 0.05);
370                 glRasterPos2i(xpos + 1, ypos + 2);
371                 const char *s = help_lines[i];
372                 while(*s) {
373                         glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *s++);
374                 }
375                 glColor3f(0.7, 1, 0.6);
376                 glRasterPos2i(xpos, ypos);
377                 s = help_lines[i];
378                 while(*s) {
379                         glutBitmapCharacter(GLUT_BITMAP_9_BY_15, *s++);
380                 }
381
382                 ypos += 22;
383         }
384
385         glPopMatrix();
386         glMatrixMode(GL_MODELVIEW);
387         glPopMatrix();
388
389         glPopAttrib();
390 }
391
392 static void idle()
393 {
394         glutPostRedisplay();
395 }
396
397 static void reshape(int x, int y)
398 {
399         win_width = x;
400         win_height = y;
401         glViewport(0, 0, x, y);
402
403         glMatrixMode(GL_PROJECTION);
404         glLoadIdentity();
405
406         gluPerspective(50, (float)x / (float)y, 0.5, 500);
407 }
408
409 static void keyboard(unsigned char c, int x, int y)
410 {
411         int idx;
412         static Camera orig_cam;
413         long anim_time = anim_stopped ? anim_stop_time - start_time : tmsec;
414
415         switch(c) {
416         case 27:
417                 exit(0);
418
419         case '\b':
420                 start_time = glutGet(GLUT_ELAPSED_TIME);
421                 prev_tmsec = 0;
422                 anim_stop_time = anim_stopped ? start_time : 0;
423                 beam_angle = 0;
424                 break;
425
426         case ' ':
427                 if(anim_stopped) {
428                         long msec = glutGet(GLUT_ELAPSED_TIME);
429                         start_time += msec - anim_stop_time;
430                         prev_tmsec = msec - start_time;
431                         anim_stop_time = 0;
432                         anim_stopped = false;
433                 } else {
434                         anim_stop_time = glutGet(GLUT_ELAPSED_TIME);
435                         anim_stopped = true;
436                 }
437                 break;
438
439         case '=':
440                 beam_speed = get_seq_value("beam-speed", anim_time);
441                 set_seq_value("beam-speed", anim_time, beam_speed + 0.1);
442                 break;
443
444         case '-':
445                 beam_speed = get_seq_value("beam-speed", anim_time) - 0.1;
446                 if(beam_speed < 0) beam_speed = 0;
447                 set_seq_value("beam-speed", anim_time, beam_speed);
448                 break;
449
450         case '0':
451                 clear_seq_track("beam-speed");
452                 break;
453
454         case '[':
455                 beam_len = get_seq_value("beam-len", anim_time) - 0.5;
456                 if(beam_len < 0) beam_len = 0;
457                 set_seq_value("beam-len", anim_time, beam_len);
458                 break;
459
460         case ']':
461                 beam_len = get_seq_value("beam-len", anim_time);
462                 set_seq_value("beam-len", anim_time, beam_len + 0.5);
463                 break;
464
465         case '\\':
466                 clear_seq_track("beam-len");
467                 break;
468
469         case '\r':
470         case '\n':
471                 idx = find_seq_track("beam-speed");
472                 assert(idx >= 0);
473                 set_seq_value(idx, anim_time, beam_speed);
474                 set_seq_value(idx, anim_time + 2000, 0);
475                 break;
476
477         case 'c':
478                 freecam = !freecam;
479                 printf("camera mode: %s\n", freecam ? "free" : "animated");
480                 if(!freecam) {
481                         orig_cam = cam;
482                 } else {
483                         cam = orig_cam;
484                 }
485                 break;
486
487         case 'v':
488                 printf("current view\n");
489                 printf(" pos: %f %f %f\n", cam.x, cam.y, cam.z);
490                 printf(" theta: %f, phi: %f\n", cam.theta, cam.phi);
491                 printf(" dist: %f\n", cam.dist);
492                 break;
493
494         case 'L':
495                 printf("clearing camera keyframes\n");
496                 clear_seq_track("cam-x");
497                 clear_seq_track("cam-y");
498                 clear_seq_track("cam-z");
499                 clear_seq_track("cam-theta");
500                 clear_seq_track("cam-phi");
501                 clear_seq_track("cam-dist");
502                 break;
503
504         case 'k':
505                 printf("setting camera keyframe for time: %ld\n", anim_time);
506                 set_seq_value("cam-x", anim_time, cam.x);
507                 set_seq_value("cam-y", anim_time, cam.y);
508                 set_seq_value("cam-z", anim_time, cam.z);
509                 set_seq_value("cam-theta", anim_time, cam.theta);
510                 set_seq_value("cam-phi", anim_time, cam.phi);
511                 set_seq_value("cam-dist", anim_time, cam.dist);
512                 break;
513
514         case 'x':
515                 set_seq_value("xlogo", anim_time, xlogo_alpha < 0.5 ? 1.0 : 0.0);
516                 break;
517
518         case 'X':
519                 printf("clearing logo keyframes\n");
520                 clear_seq_track("xlogo");
521                 break;
522
523         case 'z':
524                 set_seq_value("xcircle", anim_time, xcircle < 0.5 ? 1.0 : 0.0);
525                 break;
526
527         case 'Z':
528                 printf("clearing circle keyframes\n");
529                 clear_seq_track("xcircle");
530                 break;
531
532
533         case 'p':
534                 set_seq_value("paper", anim_time, paper < 0.5 ? 1.0 : 0.0);
535                 break;
536
537         case 'P':
538                 printf("clearing paper keyframes\n");
539                 clear_seq_track("paper");
540                 break;
541
542         case '`':
543                 printf("dumping animation data to: seq_dump\n");
544                 if(!dump_seq("seq_dump")) {
545                         fprintf(stderr, "dump failed\n");
546                 }
547                 break;
548
549         default:
550                 break;
551         }
552 }
553
554 static void keyb_special(int key, int x, int y)
555 {
556         switch(key) {
557         case GLUT_KEY_F1:
558                 show_help = !show_help;
559                 break;
560
561         case GLUT_KEY_F5:
562                 show_ui = !show_ui;
563                 break;
564
565         default:
566                 break;
567         }
568 }
569
570 static int prev_x, prev_y;
571 static bool bst[8];
572 static void mbutton(int bn, int state, int x, int y)
573 {
574         int button = bn - GLUT_LEFT_BUTTON;
575         bool pressed = state == GLUT_DOWN;
576         bst[button] = pressed;
577
578         prev_x = x;
579         prev_y = y;
580
581         if(pressed) {
582                 switch(bn) {
583                 case 3:
584                         cam.dist -= 0.5;
585                         if(cam.dist < 0) cam.dist = 0;
586                         break;
587
588                 case 4:
589                         cam.dist += 0.5;
590                         break;
591
592                 default:
593                         break;
594                 }
595         }
596 }
597
598 static void mmotion(int x, int y)
599 {
600         int dx = x - prev_x;
601         int dy = y - prev_y;
602
603         prev_x = x;
604         prev_y = y;
605
606         if (dx == 0 && dy == 0)
607                 return;
608
609         if (bst[0]) {
610                 cam.theta += dx * 0.5;
611                 cam.phi += dy * 0.5;
612
613                 if (cam.phi < -90)
614                         cam.phi = -90;
615
616                 if (cam.phi > 90)
617                         cam.phi = 90;
618         }
619
620         if (bst[2]) {
621                 cam.dist += dy * 0.1;
622
623                 if (cam.dist < 0)
624                         cam.dist = 0;
625         }
626
627         if(bst[1]) {
628                 float theta = cam.theta / 180.0f * M_PI;
629                 float phi = cam.phi / 180.0f * M_PI;
630
631                 float pan_u[3], pan_v[3];
632
633                 pan_u[0] = cos(theta);
634                 pan_u[1] = 0;
635                 pan_u[2] = sin(theta);
636
637                 pan_v[0] = sin(phi) * sin(theta);
638                 pan_v[1] = cos(phi);
639                 pan_v[2] = -sin(phi) * cos(theta);
640
641                 float pan_x = -dx * 0.002 * cam.dist;
642                 float pan_y = dy * 0.002 * cam.dist;
643
644                 cam.x += pan_u[0] * pan_x + pan_v[0] * pan_y;
645                 cam.y += pan_u[1] * pan_x + pan_v[1] * pan_y;
646                 cam.z += pan_u[2] * pan_x + pan_v[2] * pan_y;
647         }
648 }