2aec0a01e46050306267199bc4c1c3b2b09c3bbb
[antikythera] / src / main.cc
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <assert.h>
4 #include <GL/glew.h>
5 #ifdef __APPLE__
6 #include <GLUT/glut.h>
7 #else
8 #include <GL/glut.h>
9 #endif
10 #include "app.h"
11 #include "sdr.h"
12 #include "shadow.h"
13 #include "texture.h"
14 #include "machine.h"
15 #include "meshgen.h"
16 #include "mparser.h"
17
18 static bool init();
19 static void cleanup();
20 static void display();
21 static void idle();
22 static void draw_scene();
23 static void draw_gears();
24 static void reshape(int x, int y);
25 static void keyb(unsigned char key, int x, int y);
26 static void mouse(int bn, int st, int x, int y);
27 static void motion(int x, int y);
28 static void passive_motion(int x, int y);
29 static Gear *pick_gear(int x, int y);
30
31 static int win_width, win_height;
32
33 static float cam_dist = 0.25;
34 static float cam_theta, cam_phi = 20;
35 static int prev_mx, prev_my;
36 static bool bnstate[8];
37
38 static Mat4 view_matrix;
39
40 static unsigned int start_time, prev_msec;
41 static Machine *machine;
42 static Gear *hover_gear, *sel_gear;
43 static HitPoint pick_hit;
44 static Vec3 sel_hit_pos;
45
46 static unsigned int sdr_shadow_notex;
47 static int dbg_show_shadowmap;
48
49 static TextureSet texman;
50 static Texture *envmap;
51 static Mesh *skydome;
52 static unsigned int sdr_skydome;
53
54
55 int main(int argc, char **argv)
56 {
57         glutInitWindowSize(1024, 768);
58         glutInit(&argc, argv);
59         glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE | GLUT_MULTISAMPLE);
60         glutCreateWindow("Antikythera");
61
62         glutDisplayFunc(display);
63         glutIdleFunc(idle);
64         glutReshapeFunc(reshape);
65         glutKeyboardFunc(keyb);
66         glutMouseFunc(mouse);
67         glutMotionFunc(motion);
68         glutPassiveMotionFunc(passive_motion);
69
70         if(!init()) {
71                 return 1;
72         }
73         atexit(cleanup);
74
75         glutMainLoop();
76         return 0;
77 }
78
79 static bool init()
80 {
81         glewInit();
82
83         glEnable(GL_MULTISAMPLE);
84         glEnable(GL_DEPTH_TEST);
85         glEnable(GL_CULL_FACE);
86         glEnable(GL_LIGHTING);
87         glEnable(GL_NORMALIZE);
88
89         Mesh::use_custom_sdr_attr = false;
90
91         machine = new Machine;
92         if(!parse_machine(machine, "data/test.machine")) {
93                 fprintf(stderr, "failed to parse machine\n");
94                 return false;
95         }
96
97         /*
98         const float pitch = 10.0f;
99
100         Gear *gear1 = new Gear;
101         gear1->pos = Vec3(-50, 0, 0);
102         gear1->set_teeth(16, pitch);
103         gear1->gen_mesh();
104         gear1->color = Vec3(0.35, 0.6, 0.8);
105         machine->add_gear(gear1);
106
107         Gear *gear2 = new Gear;
108         gear2->set_teeth(32, pitch);
109         gear2->pos = gear1->pos + Vec3(gear1->radius + gear2->radius - gear1->teeth_length * 0.75, 0, 0);
110         gear2->thickness = 5;
111         gear2->gen_mesh();
112         machine->add_gear(gear2);
113
114         Gear *gear3 = new Gear;
115         gear3->set_teeth(8, pitch);
116         gear3->pos = gear2->pos + Vec3(0, gear2->radius + gear3->radius - gear2->teeth_length * 0.75, 0);
117         gear3->gen_mesh();
118         machine->add_gear(gear3);
119         gear3->color = Vec3(0.5, 0.8, 0.6);
120
121         Gear *subgear = new Gear;
122         subgear->set_teeth(10, pitch);
123         subgear->pos = Vec3(0, 0, (gear2->thickness + subgear->thickness) / 2 + 1);
124         subgear->gen_mesh();
125         gear2->attach(subgear);
126         machine->add_gear(subgear);
127         subgear->color = Vec3(0.8, 0.7, 0.5);
128         */
129
130         machine->add_motor(0, 1.0);
131
132         // shadows
133         init_shadow(2048);
134
135         if(!(sdr_shadow_notex = create_program_load("sdr/shadow.v.glsl", "sdr/shadow-notex.p.glsl"))) {
136                 return false;
137         }
138         set_uniform_int(sdr_shadow_notex, "shadowmap", 1);
139         set_uniform_int(sdr_shadow_notex, "envmap", 2);
140
141         float ambient[] = {0.1, 0.1, 0.1, 0.0};
142         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
143
144         // env
145         envmap = texman.get_texture("data/stpeters_cross.jpg", TEX_CUBE);
146
147         skydome = new Mesh;
148         gen_sphere(skydome, 1.0, 16, 8);
149         skydome->flip_faces();
150
151         if(!(sdr_skydome = create_program_load("sdr/skydome.v.glsl", "sdr/skydome.p.glsl"))) {
152                 return false;
153         }
154         glUseProgram(0);
155
156         start_time = glutGet(GLUT_ELAPSED_TIME);
157         return true;
158 }
159
160 static void cleanup()
161 {
162         texman.clear();
163         delete machine;
164 }
165
166 static void update(float dt)
167 {
168         texman.update();
169
170         machine->update(dt);
171
172         if(sel_gear) {
173         }
174
175         hover_gear = pick_gear(prev_mx, prev_my);
176 }
177
178 static void set_light(int idx, const Vec3 &pos, const Vec3 &color)
179 {
180         unsigned int lt = GL_LIGHT0 + idx;
181         float posv[] = { pos.x, pos.y, pos.z, 1 };
182         float colv[] = { color.x, color.y, color.z, 1 };
183
184         glEnable(lt);
185         glLightfv(lt, GL_POSITION, posv);
186         glLightfv(lt, GL_DIFFUSE, colv);
187         glLightfv(lt, GL_SPECULAR, colv);
188 }
189
190 static void display()
191 {
192         unsigned int msec = glutGet(GLUT_ELAPSED_TIME) - start_time;
193         float dt = (float)(msec - prev_msec) / 1000.0f;
194         prev_msec = msec;
195
196         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
197
198         view_matrix = Mat4::identity;
199         view_matrix.pre_translate(0, 0, -cam_dist);
200         view_matrix.pre_rotate(deg_to_rad(cam_phi), 1, 0, 0);
201         view_matrix.pre_rotate(deg_to_rad(cam_theta), 0, 1, 0);
202
203         glMatrixMode(GL_MODELVIEW);
204         glLoadMatrixf(view_matrix[0]);
205
206         static const Vec3 lpos[] = { Vec3(-50, 75, 100), Vec3(100, 0, 30), Vec3(-10, -10, 60) };
207         set_light(0, lpos[0], Vec3(1.0, 0.8, 0.7) * 0.8);
208         set_light(1, lpos[1], Vec3(0.6, 0.7, 1.0) * 0.6);
209         set_light(2, lpos[2], Vec3(0.8, 1.0, 0.8) * 0.3);
210
211         update(dt);
212
213         // shadowmap pass
214         begin_shadow_pass(lpos[0], Vec3(0, 0, 0), 0.2, 100, 150);
215         draw_scene();
216         end_shadow_pass();
217
218         // regular pass
219         const Mat4 &shadow_matrix = get_shadow_matrix();
220         Mat4 env_matrix = transpose(view_matrix.upper3x3());
221         set_uniform_matrix4(sdr_shadow_notex, "envmap_matrix", env_matrix[0]);
222
223         glActiveTexture(GL_TEXTURE1);
224         glBindTexture(GL_TEXTURE_2D, get_shadow_tex());
225         glMatrixMode(GL_TEXTURE);
226         glLoadMatrixf(shadow_matrix[0]);
227
228         glActiveTexture(GL_TEXTURE2);
229         glBindTexture(GL_TEXTURE_CUBE_MAP, envmap->get_id());
230
231         glActiveTexture(GL_TEXTURE0);
232         glMatrixMode(GL_MODELVIEW);
233
234         draw_scene();
235
236         glMatrixMode(GL_TEXTURE);
237         glLoadIdentity();
238         glMatrixMode(GL_MODELVIEW);
239
240
241         if(dbg_show_shadowmap) {
242                 glMatrixMode(GL_PROJECTION);
243                 glPushMatrix();
244                 glLoadIdentity();
245                 glMatrixMode(GL_MODELVIEW);
246                 glPushMatrix();
247                 glLoadIdentity();
248                 glMatrixMode(GL_TEXTURE);
249                 glLoadIdentity();
250
251                 glPushAttrib(GL_ENABLE_BIT);
252                 glUseProgram(0);
253                 glBindTexture(GL_TEXTURE_2D, get_shadow_tex());
254                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
255                 glEnable(GL_TEXTURE_2D);
256                 glDisable(GL_DEPTH_TEST);
257                 glDisable(GL_LIGHTING);
258                 glDisable(GL_BLEND);
259
260                 glBegin(GL_QUADS);
261                 glColor4f(1, 1, 1, 1);
262                 glTexCoord2f(0, 0); glVertex2f(-0.95, -0.95);
263                 glTexCoord2f(1, 0); glVertex2f(-0.5, -0.95);
264                 glTexCoord2f(1, 1); glVertex2f(-0.5, -0.5);
265                 glTexCoord2f(0, 1); glVertex2f(-0.95, -0.5);
266                 glEnd();
267
268                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
269                 glBindTexture(GL_TEXTURE_2D, 0);
270
271                 glPopAttrib();
272
273                 glMatrixMode(GL_PROJECTION);
274                 glPopMatrix();
275                 glMatrixMode(GL_MODELVIEW);
276                 glPopMatrix();
277         }
278
279
280         glutSwapBuffers();
281         assert(glGetError() == GL_NO_ERROR);
282 }
283
284 static void idle()
285 {
286         glutPostRedisplay();
287 }
288
289 static void draw_scene()
290 {
291         // draw skydome
292         glDepthMask(0);
293
294         Mat4 rot_view = view_matrix.upper3x3();
295         glMatrixMode(GL_MODELVIEW);
296         glPushMatrix();
297         glLoadMatrixf(rot_view[0]);
298
299         bind_texture(envmap, 0);
300
301         glUseProgram(sdr_skydome);
302         skydome->draw();
303         glUseProgram(0);
304
305         bind_texture(0, 0);
306
307         glPopMatrix();
308         glDepthMask(1);
309
310         // draw mechanism
311         draw_gears();
312 }
313
314 static void draw_gears()
315 {
316         /* world scale is in meters, gears are in millimeters, scale by 1/1000 */
317         glPushMatrix();
318         glScalef(0.001, 0.001, 0.001);
319
320         if(!shadow_pass && (sel_gear || hover_gear)) {
321                 glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT);
322
323                 glDisable(GL_LIGHTING);
324                 glFrontFace(GL_CW);
325                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
326                 glLineWidth(3.0);
327
328                 if(sel_gear) {
329                         glColor3f(0.2, 1.0, 0.3);
330                         sel_gear->draw();
331                 } else {
332                         glColor3f(1.0, 0.75, 0.2);
333                         hover_gear->draw();
334                 }
335
336                 glPopAttrib();
337         }
338
339         glUseProgram(shadow_pass ? 0 : sdr_shadow_notex);
340         machine->draw();
341         glUseProgram(0);
342
343         glPopMatrix();
344 }
345
346 static void reshape(int x, int y)
347 {
348         win_width = x;
349         win_height = y;
350
351         glViewport(0, 0, x, y);
352
353         glMatrixMode(GL_PROJECTION);
354         glLoadIdentity();
355         gluPerspective(50.0, (float)x / (float)y, 0.01, 50.0);
356 }
357
358 static void keyb(unsigned char key, int x, int y)
359 {
360         switch(key) {
361         case 27:
362                 exit(0);
363
364         case 'w':
365                 opt_gear_wireframe = !opt_gear_wireframe;
366                 glutPostRedisplay();
367                 break;
368
369         case 's':
370                 dbg_show_shadowmap = !dbg_show_shadowmap;
371                 glutPostRedisplay();
372                 break;
373         }
374 }
375
376 static void mouse(int bn, int st, int x, int y)
377 {
378         int bidx = bn - GLUT_LEFT_BUTTON;
379         bool down = st == GLUT_DOWN;
380
381         prev_mx = x;
382         prev_my = y;
383         bnstate[bidx] = down;
384
385         if(bidx == 0) {
386                 if(down) {
387                         sel_gear = pick_gear(x, y);
388                         sel_hit_pos = pick_hit.pos;
389                 } else {
390                         sel_gear = 0;
391                 }
392         }
393
394         if(bidx == 3 || bidx == 4) {    /* wheel */
395                 if(hover_gear) {
396                         float dz = bidx == 4 ? 1 : -1;
397                         hover_gear->set_position(hover_gear->get_position() + hover_gear->get_axis() * dz);
398                         machine->invalidate_meshing();
399                 }
400         }
401 }
402
403 static void motion(int x, int y)
404 {
405         int dx = x - prev_mx;
406         int dy = y - prev_my;
407         prev_mx = x;
408         prev_my = y;
409
410         if(!dx && !dy) return;
411
412         if(sel_gear) {
413                 float speed = 0.5;
414                 Vec3 offs = Vec3(dx * speed, -dy * speed, 0.0);
415                 offs = sel_gear->get_dir_matrix() * offs;
416
417                 sel_gear->set_position(sel_gear->get_position() + offs);
418                 machine->invalidate_meshing();
419
420         } else {
421                 if(bnstate[0]) {
422                         cam_theta += dx * 0.5;
423                         cam_phi += dy * 0.5;
424
425                         if(cam_phi < -90) cam_phi = -90;
426                         if(cam_phi > 90) cam_phi = 90;
427                         glutPostRedisplay();
428                 }
429                 if(bnstate[2]) {
430                         cam_dist += dy * 0.01;
431                         if(cam_dist < 0.0) cam_dist = 0.0;
432                         glutPostRedisplay();
433                 }
434         }
435 }
436
437 static void passive_motion(int x, int y)
438 {
439         prev_mx = x;
440         prev_my = y;
441 }
442
443 static Gear *pick_gear(int x, int y)
444 {
445         double pt[3];
446         double viewmat[16], projmat[16];
447         int vp[4];
448         Ray ray;
449
450         y = win_height - y;
451
452         glGetDoublev(GL_MODELVIEW_MATRIX, viewmat);
453         glGetDoublev(GL_PROJECTION_MATRIX, projmat);
454         glGetIntegerv(GL_VIEWPORT, vp);
455
456         gluUnProject(x, y, 0, viewmat, projmat, vp, pt, pt + 1, pt + 2);
457         ray.origin = Vec3(pt[0], pt[1], pt[2]) * 1000.0f;
458
459         gluUnProject(x, y, 1, viewmat, projmat, vp, pt, pt + 1, pt + 2);
460         ray.dir = Vec3(pt[0], pt[1], pt[2]) * 1000.0f - ray.origin;
461
462         return machine->intersect_gear(ray, &pick_hit);
463 }