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