refactoring
[laserbrain_demo] / 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("demo");
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         // shadows
98         init_shadow(2048);
99
100         if(!(sdr_shadow_notex = create_program_load("sdr/shadow.v.glsl", "sdr/shadow-notex.p.glsl"))) {
101                 return false;
102         }
103         set_uniform_int(sdr_shadow_notex, "shadowmap", 1);
104         set_uniform_int(sdr_shadow_notex, "envmap", 2);
105
106         float ambient[] = {0.1, 0.1, 0.1, 0.0};
107         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
108
109         // env
110         envmap = texman.get_texture("data/stpeters_cross.jpg", TEX_CUBE);
111
112         skydome = new Mesh;
113         gen_sphere(skydome, 1.0, 16, 8);
114         skydome->flip_faces();
115
116         if(!(sdr_skydome = create_program_load("sdr/skydome.v.glsl", "sdr/skydome.p.glsl"))) {
117                 return false;
118         }
119         glUseProgram(0);
120
121         start_time = glutGet(GLUT_ELAPSED_TIME);
122         return true;
123 }
124
125 static void cleanup()
126 {
127         texman.clear();
128         delete machine;
129 }
130
131 static void update(float dt)
132 {
133         texman.update();
134
135         machine->update(dt);
136
137         if(sel_gear) {
138         }
139
140         hover_gear = pick_gear(prev_mx, prev_my);
141 }
142
143 static void set_light(int idx, const Vec3 &pos, const Vec3 &color)
144 {
145         unsigned int lt = GL_LIGHT0 + idx;
146         float posv[] = { pos.x, pos.y, pos.z, 1 };
147         float colv[] = { color.x, color.y, color.z, 1 };
148
149         glEnable(lt);
150         glLightfv(lt, GL_POSITION, posv);
151         glLightfv(lt, GL_DIFFUSE, colv);
152         glLightfv(lt, GL_SPECULAR, colv);
153 }
154
155 static void display()
156 {
157         unsigned int msec = glutGet(GLUT_ELAPSED_TIME) - start_time;
158         float dt = (float)(msec - prev_msec) / 1000.0f;
159         prev_msec = msec;
160
161         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
162
163         view_matrix = Mat4::identity;
164         view_matrix.pre_translate(0, 0, -cam_dist);
165         view_matrix.pre_rotate(deg_to_rad(cam_phi), 1, 0, 0);
166         view_matrix.pre_rotate(deg_to_rad(cam_theta), 0, 1, 0);
167
168         glMatrixMode(GL_MODELVIEW);
169         glLoadMatrixf(view_matrix[0]);
170
171         static const Vec3 lpos[] = { Vec3(-50, 75, 100), Vec3(100, 0, 30), Vec3(-10, -10, 60) };
172         set_light(0, lpos[0], Vec3(1.0, 0.8, 0.7) * 0.8);
173         set_light(1, lpos[1], Vec3(0.6, 0.7, 1.0) * 0.6);
174         set_light(2, lpos[2], Vec3(0.8, 1.0, 0.8) * 0.3);
175
176         update(dt);
177
178         // shadowmap pass
179         begin_shadow_pass(lpos[0], Vec3(0, 0, 0), 0.2, 100, 150);
180         draw_scene();
181         end_shadow_pass();
182
183         // regular pass
184         const Mat4 &shadow_matrix = get_shadow_matrix();
185         Mat4 env_matrix = transpose(view_matrix.upper3x3());
186         set_uniform_matrix4(sdr_shadow_notex, "envmap_matrix", env_matrix[0]);
187
188         glActiveTexture(GL_TEXTURE1);
189         glBindTexture(GL_TEXTURE_2D, get_shadow_tex());
190         glMatrixMode(GL_TEXTURE);
191         glLoadMatrixf(shadow_matrix[0]);
192
193         glActiveTexture(GL_TEXTURE2);
194         glBindTexture(GL_TEXTURE_CUBE_MAP, envmap->get_id());
195
196         glActiveTexture(GL_TEXTURE0);
197         glMatrixMode(GL_MODELVIEW);
198
199         draw_scene();
200
201         glMatrixMode(GL_TEXTURE);
202         glLoadIdentity();
203         glMatrixMode(GL_MODELVIEW);
204
205
206         if(dbg_show_shadowmap) {
207                 glMatrixMode(GL_PROJECTION);
208                 glPushMatrix();
209                 glLoadIdentity();
210                 glMatrixMode(GL_MODELVIEW);
211                 glPushMatrix();
212                 glLoadIdentity();
213                 glMatrixMode(GL_TEXTURE);
214                 glLoadIdentity();
215
216                 glPushAttrib(GL_ENABLE_BIT);
217                 glUseProgram(0);
218                 glBindTexture(GL_TEXTURE_2D, get_shadow_tex());
219                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
220                 glEnable(GL_TEXTURE_2D);
221                 glDisable(GL_DEPTH_TEST);
222                 glDisable(GL_LIGHTING);
223                 glDisable(GL_BLEND);
224
225                 glBegin(GL_QUADS);
226                 glColor4f(1, 1, 1, 1);
227                 glTexCoord2f(0, 0); glVertex2f(-0.95, -0.95);
228                 glTexCoord2f(1, 0); glVertex2f(-0.5, -0.95);
229                 glTexCoord2f(1, 1); glVertex2f(-0.5, -0.5);
230                 glTexCoord2f(0, 1); glVertex2f(-0.95, -0.5);
231                 glEnd();
232
233                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
234                 glBindTexture(GL_TEXTURE_2D, 0);
235
236                 glPopAttrib();
237
238                 glMatrixMode(GL_PROJECTION);
239                 glPopMatrix();
240                 glMatrixMode(GL_MODELVIEW);
241                 glPopMatrix();
242         }
243
244
245         glutSwapBuffers();
246         assert(glGetError() == GL_NO_ERROR);
247 }
248
249 static void idle()
250 {
251         glutPostRedisplay();
252 }
253
254 static void draw_scene()
255 {
256         // draw skydome
257         glDepthMask(0);
258
259         Mat4 rot_view = view_matrix.upper3x3();
260         glMatrixMode(GL_MODELVIEW);
261         glPushMatrix();
262         glLoadMatrixf(rot_view[0]);
263
264         bind_texture(envmap, 0);
265
266         glUseProgram(sdr_skydome);
267         skydome->draw();
268         glUseProgram(0);
269
270         bind_texture(0, 0);
271
272         glPopMatrix();
273         glDepthMask(1);
274
275         // draw mechanism
276         draw_gears();
277 }
278
279 static void draw_gears()
280 {
281         /* world scale is in meters, gears are in millimeters, scale by 1/1000 */
282         glPushMatrix();
283         glScalef(0.001, 0.001, 0.001);
284
285         if(!shadow_pass && (sel_gear || hover_gear)) {
286                 glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_POLYGON_BIT);
287
288                 glDisable(GL_LIGHTING);
289                 glFrontFace(GL_CW);
290                 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
291                 glLineWidth(3.0);
292
293                 if(sel_gear) {
294                         glColor3f(0.2, 1.0, 0.3);
295                         sel_gear->draw();
296                 } else {
297                         glColor3f(1.0, 0.75, 0.2);
298                         hover_gear->draw();
299                 }
300
301                 glPopAttrib();
302         }
303
304         glUseProgram(shadow_pass ? 0 : sdr_shadow_notex);
305         machine->draw();
306         glUseProgram(0);
307
308         glPopMatrix();
309 }
310
311 static void reshape(int x, int y)
312 {
313         win_width = x;
314         win_height = y;
315
316         glViewport(0, 0, x, y);
317
318         glMatrixMode(GL_PROJECTION);
319         glLoadIdentity();
320         gluPerspective(50.0, (float)x / (float)y, 0.01, 50.0);
321 }
322
323 static void keyb(unsigned char key, int x, int y)
324 {
325         switch(key) {
326         case 27:
327                 exit(0);
328
329         case 'w':
330                 opt_gear_wireframe = !opt_gear_wireframe;
331                 glutPostRedisplay();
332                 break;
333
334         case 's':
335                 dbg_show_shadowmap = !dbg_show_shadowmap;
336                 glutPostRedisplay();
337                 break;
338         }
339 }
340
341 static void mouse(int bn, int st, int x, int y)
342 {
343         int bidx = bn - GLUT_LEFT_BUTTON;
344         bool down = st == GLUT_DOWN;
345
346         prev_mx = x;
347         prev_my = y;
348         bnstate[bidx] = down;
349
350         if(bidx == 0) {
351                 if(down) {
352                         sel_gear = pick_gear(x, y);
353                         sel_hit_pos = pick_hit.pos;
354                 } else {
355                         sel_gear = 0;
356                 }
357         }
358
359         if(bidx == 3 || bidx == 4) {    /* wheel */
360                 if(hover_gear) {
361                         float dz = bidx == 4 ? 1 : -1;
362                         hover_gear->set_position(hover_gear->get_position() + hover_gear->get_axis() * dz);
363                         machine->invalidate_meshing();
364                 }
365         }
366 }
367
368 static void motion(int x, int y)
369 {
370         int dx = x - prev_mx;
371         int dy = y - prev_my;
372         prev_mx = x;
373         prev_my = y;
374
375         if(!dx && !dy) return;
376
377         if(sel_gear) {
378                 float speed = 0.5;
379                 Vec3 offs = Vec3(dx * speed, -dy * speed, 0.0);
380                 offs = sel_gear->get_dir_matrix() * offs;
381
382                 sel_gear->set_position(sel_gear->get_position() + offs);
383                 machine->invalidate_meshing();
384
385         } else {
386                 if(bnstate[0]) {
387                         cam_theta += dx * 0.5;
388                         cam_phi += dy * 0.5;
389
390                         if(cam_phi < -90) cam_phi = -90;
391                         if(cam_phi > 90) cam_phi = 90;
392                         glutPostRedisplay();
393                 }
394                 if(bnstate[2]) {
395                         cam_dist += dy * 0.01;
396                         if(cam_dist < 0.0) cam_dist = 0.0;
397                         glutPostRedisplay();
398                 }
399         }
400 }
401
402 static void passive_motion(int x, int y)
403 {
404         prev_mx = x;
405         prev_my = y;
406 }
407
408 static Gear *pick_gear(int x, int y)
409 {
410         double pt[3];
411         double viewmat[16], projmat[16];
412         int vp[4];
413         Ray ray;
414
415         y = win_height - y;
416
417         glGetDoublev(GL_MODELVIEW_MATRIX, viewmat);
418         glGetDoublev(GL_PROJECTION_MATRIX, projmat);
419         glGetIntegerv(GL_VIEWPORT, vp);
420
421         gluUnProject(x, y, 0, viewmat, projmat, vp, pt, pt + 1, pt + 2);
422         ray.origin = Vec3(pt[0], pt[1], pt[2]) * 1000.0f;
423
424         gluUnProject(x, y, 1, viewmat, projmat, vp, pt, pt + 1, pt + 2);
425         ray.dir = Vec3(pt[0], pt[1], pt[2]) * 1000.0f - ray.origin;
426
427         return machine->intersect_gear(ray, &pick_hit);
428 }