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