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