camera rotates less angrily with the waves
[maximus:incidents.git] / code / main.c
1 // for popen, pclose
2 #define _POSIX_C_SOURCE 2
3
4 #include <math.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include <GL/glew.h>
10 #include <GL/glut.h>
11
12 #include <jack/jack.h>
13
14 #include "boat.h"
15 #include "colour.h"
16 #include "vector.h"
17 #include "wave.h"
18
19 const double pi = 3.141592653589793;
20
21 const double waveSpeed = 556.0618996853933; // 2 * pi * 4425 / 50 / 3;
22 const double waveZoom = 1.272019649514069;//1.189207115002721; // 1.4142135623730951; // sqrt(2);sqrt $ (sqrt 5 + 1) / 2
23
24
25 struct vec4 darkRed, darkBlue;
26
27 struct {
28   struct boat boat;
29   struct wave wave;
30   int width, height, winwidth, winheight, frame, frames, record, ready, bufsize;
31   char *buffer;
32   double time;
33   jack_client_t *jack;
34   FILE *eca;
35 } data;
36
37 void keyboard(unsigned char c, int x, int y) {
38   if (! data.record) {
39     exit(0);
40   }
41 }
42
43 void timer(int t) {
44   glutTimerFunc(data.record ? 1 : 10, timer, t + 1);
45   glutPostRedisplay();
46 }
47
48 void reshape(int w, int h) {
49   data.winwidth = w;
50   data.winheight = h;
51   data.ready = 1;
52 }
53
54 void update(void) {
55   jack_position_t pos;
56   /*jack_transport_state_t s = */ jack_transport_query(data.jack, &pos);
57   double phase = pos.frame * 25.0 / pos.frame_rate / data.frames;
58   data.time = phase - floor(phase);
59 }
60
61 void display(void) {
62   if (data.ready) {
63     update();
64   }
65   {
66     // view
67     float x = data.width * 0.5 / data.height;
68     glMatrixMode(GL_PROJECTION);
69     glLoadIdentity();
70     gluOrtho2D(-x, x, -0.5, 0.5);
71     glViewport(0, 0, data.width, data.height);
72     // background
73     struct vec4 sky;
74     if (data.time < 0.5) {
75       mix(&orange, &darkRed, data.time * 2, &sky);
76     } else {
77       mix(&darkRed, &darkBlue, data.time * 2 - 1, &sky);
78     }
79     glClearColor(sky.v[0], sky.v[1], sky.v[2], sky.v[3]);
80     glClear(GL_COLOR_BUFFER_BIT);
81     // perspective
82     int boatBias = -8;
83     int viewBias =  4;
84     double frequency = data.height * pi / data.width * pow(waveZoom, boatBias);
85     double amplitude = (1 - cos(2 * pi * data.time)) / 2;
86     double z = 1;//pow(8, amplitude);
87     double boatFrequency = frequency * pow(waveZoom, -boatBias);
88     double viewFrequency = frequency * pow(waveZoom, -viewBias);
89     double boatAmplitude = amplitude / boatFrequency;
90     double viewAmplitude = amplitude / viewFrequency;
91     double boatX = data.width * -0.5 * cos(pi * data.time) / data.height;
92     double viewX = 0;
93     double boatPhase = data.time * waveSpeed;
94     double viewPhase = data.time * waveSpeed;
95     double boatY = boatAmplitude * (sin(boatX * boatFrequency + boatPhase) - 0.5);
96     double viewY = boatY + viewAmplitude;
97     double viewT = -atan2(viewFrequency * viewAmplitude * cos(viewX * viewFrequency + viewPhase), 16);
98     double k = - (viewY + boatY) / (boatY - pow(waveZoom, viewBias - boatBias) * boatY);
99     double tc = cos(viewT);
100     double ts = sin(viewT);
101     double dx = boatX;
102     double dy = boatY;
103     double y0 = -0.5 - dy;
104     double y1 =  0.5 - dy;
105     double tx00 = z * ( tc * (-x - dx) + ts * y0) + dx;
106     double ty00 = z * (-ts * (-x - dx) + tc * y0) + dy + 1.0/6;
107     double tx10 = z * ( tc * (-x - dx) + ts * y1) + dx;
108     double ty10 = z * (-ts * (-x - dx) + tc * y1) + dy + 1.0/6;
109     double tx01 = z * ( tc * ( x - dx) + ts * y0) + dx;
110     double ty01 = z * (-ts * ( x - dx) + tc * y0) + dy + 1.0/6;
111     double tx11 = z * ( tc * ( x - dx) + ts * y1) + dx;
112     double ty11 = z * (-ts * ( x - dx) + tc * y1) + dy + 1.0/6;
113     // distant waves
114     for (int bias = -19; bias < boatBias; bias += 2) {
115       data.wave.value.frequency = frequency * pow(waveZoom, -bias) / z;
116       data.wave.value.amplitude = amplitude / data.wave.value.frequency;
117       data.wave.value.level = (-pow(waveZoom, bias - boatBias) * boatY + boatY) * k - boatY;
118       data.wave.value.phase = data.time * waveSpeed;
119       data.wave.value.width = z * fmax(1, pow(waveZoom, bias)) / data.height;
120       data.wave.value.blend = data.height / 2.0 / z;
121       mix(&sky, &blue,  1 - (boatBias - bias) / 12.0, &data.wave.value.fill);
122       data.wave.value.fill.v[3] = 0.25;
123       mix(&sky, &white, 0.5 - (boatBias - bias) / 24.0, &data.wave.value.stroke);
124       wave_use(&data.wave);
125       glBegin(GL_QUADS); {
126         glTexCoord2f(tx00, ty00); glVertex2f(-x, -0.5);
127         glTexCoord2f(tx01, ty01); glVertex2f( x, -0.5);
128         glTexCoord2f(tx11, ty11); glVertex2f( x,  0.5);
129         glTexCoord2f(tx10, ty10); glVertex2f(-x,  0.5);
130       } glEnd();
131       glUseProgram(0);
132     }
133     // boat
134     data.boat.value.boatX = boatX;
135     data.boat.value.frequency = boatFrequency;
136     data.boat.value.amplitude = boatAmplitude;
137     data.boat.value.level = -boatY;
138     data.boat.value.phase = boatPhase;
139     data.boat.value.size = (0.25 / (1 - fabs(boatX / x)) - 0.0625) / z;
140     mix(&sky, &red,   1, &data.boat.value.fill);
141     mix(&sky, &white, 0.5, &data.boat.value.stroke);
142     boat_use(&data.boat);
143     glBegin(GL_QUADS); {
144       glTexCoord2f(tx00, ty00); glVertex2f(-x, -0.5);
145       glTexCoord2f(tx01, ty01); glVertex2f( x, -0.5);
146       glTexCoord2f(tx11, ty11); glVertex2f( x,  0.5);
147       glTexCoord2f(tx10, ty10); glVertex2f(-x,  0.5);
148     } glEnd();
149     boat_use(0);
150     // near waves
151     for (int bias = boatBias + 1; bias < 4; bias += 2) {
152       data.wave.value.frequency = frequency * pow(waveZoom, -bias) / z;
153       data.wave.value.amplitude = amplitude / data.wave.value.frequency;
154       data.wave.value.level = (-pow(waveZoom, bias - boatBias) * boatY + boatY) * k - boatY;
155       data.wave.value.phase = data.time * waveSpeed;
156       data.wave.value.width = z * fmax(1, pow(waveZoom, bias)) / data.height;
157       data.wave.value.blend = data.height / 2.0 / z;
158       struct vec4 tmp;
159       float b = (bias - boatBias) / 12.0;
160       mix(&blue, &green, b, &tmp);
161       mix(&tmp, &black, b, &data.wave.value.fill);
162       data.wave.value.fill.v[3] = 0.25;
163       mix(&sky, &white, 0.5 - (boatBias - bias) / 24.0, &data.wave.value.stroke);
164       wave_use(&data.wave);
165       glBegin(GL_QUADS); {
166         glTexCoord2f(tx00, ty00); glVertex2f(-x, -0.5);
167         glTexCoord2f(tx01, ty01); glVertex2f( x, -0.5);
168         glTexCoord2f(tx11, ty11); glVertex2f( x,  0.5);
169         glTexCoord2f(tx10, ty10); glVertex2f(-x,  0.5);
170       } glEnd();
171       glUseProgram(0);
172     }
173   }
174   glutSwapBuffers();
175   { // recording
176     if (data.ready && data.record) {
177       if (data.frame == data.frames) {
178         exit(0);
179       }
180       fprintf(stdout, "P6\n%d %d 255\n", data.width, data.height);
181       glPixelStorei(GL_PACK_ALIGNMENT, 1);
182       glReadPixels(0, 0, data.width, data.height, GL_RGB, GL_UNSIGNED_BYTE, data.buffer);
183       fwrite(data.buffer, data.bufsize, 1, stdout);
184     }
185   }
186   glutReportErrors();
187 }
188
189 void cleanup(void) {
190   if (data.buffer) { free(data.buffer); }
191   if (data.eca) { pclose(data.eca); }
192 }
193
194 int main(int argc, char **argv) {
195   data.width = 1050;
196   data.height = 576;
197   data.frames = 4425;
198   data.record = 0;
199   mix(&red, &black, 1.0/3.0, &darkRed);
200   mix(&blue, &black, 2.0/3.0, &darkBlue);
201   glutInitWindowSize(data.width, data.height);
202   glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
203   glutInit(&argc, argv);
204   glutCreateWindow("Incidents At Sea");
205   glewInit();
206   glEnable(GL_BLEND);
207   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
208   glMatrixMode(GL_MODELVIEW);
209   glLoadIdentity();
210   glMatrixMode(GL_PROJECTION);
211   glLoadIdentity();
212   boat_init(&data.boat);
213   wave_init(&data.wave);
214   if (data.record) {
215     data.bufsize = data.width * data.height * 3;
216     data.buffer = malloc(data.bufsize);
217     memset(data.buffer, 128, data.bufsize);
218   } else {
219     data.buffer = 0;
220   }
221   data.frame = 0;
222   data.ready = 0;
223   glClearColor(0.5, 1.0, 0.5, 1);
224   glutReshapeFunc(reshape);
225   glutDisplayFunc(display);
226   glutTimerFunc(1, timer, 0);
227   glutKeyboardFunc(keyboard);
228   glutReportErrors();
229   data.jack = jack_client_open("incidents", 0, 0);
230   data.eca = popen("ecasound -G:jack,incidents,send -i:../audio/nightboat.wav -o:jack,system", "w");
231   atexit(cleanup);
232   glutMainLoop();
233   return 0;
234 }