6eb019aad7e3f47a3a1a09e8e08eaa18708fa566
[laserbrain_demo] / src / app.cc
1 #include <stdio.h>
2 #include <assert.h>
3 #include <goatvr.h>
4 #include "app.h"
5 #include "opengl.h"
6 #include "sdr.h"
7 #include "texture.h"
8 #include "mesh.h"
9 #include "meshgen.h"
10 #include "scene.h"
11 #include "metascene.h"
12 #include "datamap.h"
13 #include "ui.h"
14 #include "opt.h"
15 #include "post.h"
16 #include "renderer.h"
17 #include "exman.h"
18 #include "blob_exhibit.h"
19
20 #define NEAR_CLIP       5.0
21 #define FAR_CLIP        10000.0
22
23 static void draw_scene();
24 static void toggle_flight();
25 static void calc_framerate();
26
27 long time_msec;
28 int win_width, win_height;
29 float win_aspect;
30 bool fb_srgb;
31 bool opt_gear_wireframe;
32
33 TextureSet texman;
34 SceneSet sceneman;
35
36 unsigned int sdr_ltmap, sdr_ltmap_notex;
37
38 static float cam_dist = 0.0;
39 static float cam_theta, cam_phi;
40 static Vec3 cam_pos;
41 static float floor_y;   // last floor height
42 static float user_eye_height = 165;
43
44 static float walk_speed = 300.0f;
45 static float mouse_speed = 0.5f;
46 static bool show_walk_mesh, noclip = false;
47
48 static bool have_headtracking, should_swap;
49
50 static int prev_mx, prev_my;
51 static bool bnstate[8];
52 static bool keystate[256];
53 static bool gpad_bnstate[64];
54 static Vec2 joy_move, joy_look;
55 static float joy_deadzone = 0.01;
56
57 static float framerate;
58
59 static Mat4 view_matrix, mouse_view_matrix, proj_matrix;
60 static MetaScene *mscn;
61 static unsigned int sdr_post_gamma;
62
63 static long prev_msec;
64
65 static ExhibitManager *exman;
66 static BlobExhibit *blobs;
67 static bool show_blobs;
68
69 static Renderer *rend;
70
71
72 bool app_init(int argc, char **argv)
73 {
74         if(!init_options(argc, argv, "demo.conf")) {
75                 return false;
76         }
77         app_resize(opt.width, opt.height);
78         app_fullscreen(opt.fullscreen);
79
80         if(opt.vr) {
81                 if(goatvr_init() == -1) {
82                         return false;
83                 }
84                 goatvr_set_origin_mode(GOATVR_HEAD);
85                 goatvr_set_units_scale(100.0f);
86
87                 goatvr_startvr();
88                 should_swap = goatvr_should_swap() != 0;
89                 user_eye_height = goatvr_get_eye_height();
90                 have_headtracking = goatvr_have_headtracking();
91
92                 goatvr_recenter();
93         }
94
95         int srgb_capable;
96         glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable);
97         printf("Framebuffer %s sRGB-capable\n", srgb_capable ? "is" : "is not");
98         fb_srgb = srgb_capable != 0;
99         glEnable(GL_FRAMEBUFFER_SRGB);
100
101         glEnable(GL_MULTISAMPLE);
102         glEnable(GL_DEPTH_TEST);
103         glEnable(GL_CULL_FACE);
104         glEnable(GL_LIGHTING);
105         glEnable(GL_NORMALIZE);
106
107         Mesh::use_custom_sdr_attr = false;
108
109         float ambient[] = {0.0, 0.0, 0.0, 0.0};
110         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
111
112         glClearColor(1, 1, 1, 1);
113
114         mscn = new MetaScene;
115         if(!mscn->load(opt.scenefile ? opt.scenefile : "data/museum.scene")) {
116                 return false;
117         }
118
119         cam_pos = mscn->start_pos;
120         Vec3 dir = rotate(Vec3(0, 0, 1), mscn->start_rot);
121         dir.y = 0;
122         cam_theta = rad_to_deg(acos(dot(dir, Vec3(0, 0, 1))));
123
124         exman = new ExhibitManager;
125         if(!exman->load(mscn, "data/exhibits")) {
126                 //return false;
127         }
128
129         blobs = new BlobExhibit;
130         blobs->node = new SceneNode;
131         blobs->init();
132         blobs->node->set_position(Vec3(-680, 160, -100));
133         blobs->node->set_scaling(Vec3(28, 28, 28));
134         blobs->node->update(0);
135
136         exman->add(blobs);
137
138         if(!(sdr_ltmap_notex = create_program_load("sdr/lightmap.v.glsl", "sdr/lightmap-notex.p.glsl"))) {
139                 return false;
140         }
141         set_uniform_int(sdr_ltmap_notex, "texmap", MTL_TEX_DIFFUSE);
142         set_uniform_int(sdr_ltmap_notex, "lightmap", MTL_TEX_LIGHTMAP);
143
144         if(!(sdr_ltmap = create_program_load("sdr/lightmap.v.glsl", "sdr/lightmap-tex.p.glsl"))) {
145                 return false;
146         }
147         set_uniform_int(sdr_ltmap, "texmap", MTL_TEX_DIFFUSE);
148         set_uniform_int(sdr_ltmap, "lightmap", MTL_TEX_LIGHTMAP);
149
150         if(!fb_srgb) {
151                 sdr_post_gamma = create_program_load("sdr/post_gamma.v.glsl", "sdr/post_gamma.p.glsl");
152         }
153
154         rend = new Renderer;
155         rend->set_scene(mscn);
156
157         glUseProgram(0);
158
159         if(opt.vr || opt.fullscreen) {
160                 app_grab_mouse(true);
161         }
162         return true;
163 }
164
165 void app_cleanup()
166 {
167         app_grab_mouse(false);
168         if(opt.vr) {
169                 goatvr_shutdown();
170         }
171
172         delete rend;
173
174         delete exman;
175
176         texman.clear();
177         sceneman.clear();
178 }
179
180 static bool constrain_walk_mesh(const Vec3 &v, Vec3 *newv)
181 {
182         Mesh *wm = mscn->walk_mesh;
183         if(!wm) {
184                 *newv = v;
185                 return true;
186         }
187
188         Ray downray = Ray(v, Vec3(0, -1, 0));
189         HitPoint hit;
190         if(mscn->walk_mesh->intersect(downray, &hit)) {
191                 *newv = hit.pos;
192                 newv->y += user_eye_height;
193                 return true;
194         }
195         return false;
196 }
197
198 static void update(float dt)
199 {
200         texman.update();
201         sceneman.update();
202
203         mscn->update(dt);
204         exman->update(dt);
205
206         float speed = walk_speed * dt;
207         Vec3 dir;
208
209         // joystick
210         float jdeadsq = joy_deadzone * joy_deadzone;
211         float jmove_lensq = length_sq(joy_move);
212         float jlook_lensq = length_sq(joy_look);
213
214         if(jmove_lensq > jdeadsq) {
215                 float len = sqrt(jmove_lensq);
216                 jmove_lensq -= jdeadsq;
217
218                 float mag = len * len;
219                 dir.x += mag * joy_move.x / len * 2.0 * speed;
220                 dir.z += mag * joy_move.y / len * 2.0 * speed;
221         }
222         if(jlook_lensq > jdeadsq) {
223                 float len = sqrt(jlook_lensq);
224                 jlook_lensq -= jdeadsq;
225
226                 float mag = len * len;
227                 cam_theta += mag * joy_look.x / len * 200.0 * dt;
228                 cam_phi += mag * joy_look.y / len * 100.0 * dt;
229                 if(cam_phi < -90.0f) cam_phi = -90.0f;
230                 if(cam_phi > 90.0f) cam_phi = 90.0f;
231         }
232
233         // keyboard move
234         if(keystate[(int)'w']) {
235                 dir.z -= speed;
236         }
237         if(keystate[(int)'s']) {
238                 dir.z += speed;
239         }
240         if(keystate[(int)'d']) {
241                 dir.x += speed;
242         }
243         if(keystate[(int)'a']) {
244                 dir.x -= speed;
245         }
246         if(keystate[(int)'q'] || gpad_bnstate[GPAD_UP]) {
247                 cam_pos.y += speed;
248         }
249         if(keystate[(int)'z'] || gpad_bnstate[GPAD_DOWN]) {
250                 cam_pos.y -= speed;
251         }
252
253         float theta = M_PI * cam_theta / 180.0f;
254         Vec3 newpos;
255         newpos.x = cam_pos.x + cos(theta) * dir.x - sin(theta) * dir.z;
256         newpos.y = cam_pos.y;
257         newpos.z = cam_pos.z + sin(theta) * dir.x + cos(theta) * dir.z;
258
259         if(noclip) {
260                 cam_pos = newpos;
261         } else {
262                 if(!constrain_walk_mesh(newpos, &cam_pos)) {
263                         float dtheta = M_PI / 32.0;
264                         float theta = dtheta;
265                         Vec2 dir2d = newpos.xz() - cam_pos.xz();
266
267                         for(int i=0; i<16; i++) {
268                                 Vec2 dvec = rotate(dir2d, theta);
269                                 Vec3 pos = cam_pos + Vec3(dvec.x, 0, dvec.y);
270                                 if(constrain_walk_mesh(pos, &cam_pos)) {
271                                         break;
272                                 }
273                                 dvec = rotate(dir2d, -theta);
274                                 pos = cam_pos + Vec3(dvec.x, 0, dvec.y);
275                                 if(constrain_walk_mesh(pos, &cam_pos)) {
276                                         break;
277                                 }
278                                 theta += dtheta;
279                         }
280                 }
281                 floor_y = cam_pos.y - user_eye_height;
282         }
283
284         // calculate mouselook view matrix
285         mouse_view_matrix = Mat4::identity;
286         mouse_view_matrix.pre_translate(0, 0, -cam_dist);
287         if(!have_headtracking) {
288                 mouse_view_matrix.pre_rotate_x(deg_to_rad(cam_phi));
289         }
290         mouse_view_matrix.pre_rotate_y(deg_to_rad(cam_theta));
291         mouse_view_matrix.pre_translate(-cam_pos.x, -cam_pos.y, -cam_pos.z);
292 }
293
294 static void set_light(int idx, const Vec3 &pos, const Vec3 &color)
295 {
296         unsigned int lt = GL_LIGHT0 + idx;
297         float posv[] = { pos.x, pos.y, pos.z, 1 };
298         float colv[] = { color.x, color.y, color.z, 1 };
299
300         glEnable(lt);
301         glLightfv(lt, GL_POSITION, posv);
302         glLightfv(lt, GL_DIFFUSE, colv);
303         glLightfv(lt, GL_SPECULAR, colv);
304 }
305
306 void app_display()
307 {
308         float dt = (float)(time_msec - prev_msec) / 1000.0f;
309         prev_msec = time_msec;
310
311         update(dt);
312
313         if(opt.vr) {
314                 // VR mode
315                 goatvr_draw_start();
316                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
317
318                 for(int i=0; i<2; i++) {
319                         // for each eye
320                         goatvr_draw_eye(i);
321
322                         proj_matrix = goatvr_projection_matrix(i, NEAR_CLIP, FAR_CLIP);
323                         glMatrixMode(GL_PROJECTION);
324                         glLoadMatrixf(proj_matrix[0]);
325
326                         view_matrix = mouse_view_matrix * Mat4(goatvr_view_matrix(i));
327                         glMatrixMode(GL_MODELVIEW);
328                         glLoadMatrixf(view_matrix[0]);
329
330                         draw_scene();
331                 }
332                 goatvr_draw_done();
333
334                 if(should_swap) {
335                         app_swap_buffers();
336                 }
337
338         } else {
339                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
340
341                 proj_matrix.perspective(deg_to_rad(50.0), win_aspect, NEAR_CLIP, FAR_CLIP);
342                 glMatrixMode(GL_PROJECTION);
343                 glLoadMatrixf(proj_matrix[0]);
344
345                 view_matrix = mouse_view_matrix;
346                 glMatrixMode(GL_MODELVIEW);
347                 glLoadMatrixf(view_matrix[0]);
348
349                 draw_scene();
350
351                 if(!fb_srgb && sdr_post_gamma) {
352                         slow_post(sdr_post_gamma);
353                         glUseProgram(0);
354                 }
355                 app_swap_buffers();
356         }
357         assert(glGetError() == GL_NO_ERROR);
358
359         calc_framerate();
360 }
361
362
363 static void draw_scene()
364 {
365         static const Vec3 lpos[] = { Vec3(-50, 75, 100), Vec3(100, 0, 30), Vec3(-10, -10, 60) };
366         set_light(0, lpos[0], Vec3(1.0, 0.8, 0.7) * 0.8);
367         set_light(1, lpos[1], Vec3(0.6, 0.7, 1.0) * 0.6);
368         set_light(2, lpos[2], Vec3(0.8, 1.0, 0.8) * 0.3);
369
370         rend->draw();
371
372         if(show_blobs) {
373                 blobs->draw();
374         }
375
376         if(show_walk_mesh && mscn->walk_mesh) {
377                 glPushAttrib(GL_ENABLE_BIT);
378                 glEnable(GL_BLEND);
379                 glBlendFunc(GL_ONE, GL_ONE);
380                 glDisable(GL_LIGHTING);
381                 glEnable(GL_POLYGON_OFFSET_FILL);
382
383                 glUseProgram(0);
384
385                 glPolygonOffset(-1, 1);
386                 glDepthMask(0);
387
388                 glColor3f(0.3, 0.08, 0.01);
389                 mscn->walk_mesh->draw();
390
391                 glDepthMask(1);
392
393                 glPopAttrib();
394         }
395
396         print_text(Vec2(9 * win_width / 10, 20), Vec3(1, 1, 0), "fps: %.1f", framerate);
397         draw_ui();
398 }
399
400
401 void app_reshape(int x, int y)
402 {
403         glViewport(0, 0, x, y);
404         goatvr_set_fb_size(x, y, 1.0f);
405 }
406
407 void app_keyboard(int key, bool pressed)
408 {
409         unsigned int mod = app_get_modifiers();
410
411         if(pressed) {
412                 switch(key) {
413                 case 27:
414                         app_quit();
415                         break;
416
417                 case '\n':
418                 case '\r':
419                         if(mod & MOD_ALT) {
420                                 app_toggle_fullscreen();
421                         }
422                         break;
423
424                 case '`':
425                         app_toggle_grab_mouse();
426                         show_message("mouse %s", app_is_mouse_grabbed() ? "grabbed" : "released");
427                         break;
428
429                 case 'w':
430                         if(mod & MOD_CTRL) {
431                                 show_walk_mesh = !show_walk_mesh;
432                                 show_message("walk mesh: %s", show_walk_mesh ? "on" : "off");
433                         }
434                         break;
435
436                 case 'c':
437                         if(mod & MOD_CTRL) {
438                                 noclip = !noclip;
439                                 show_message(noclip ? "no clip" : "clip");
440                         }
441                         break;
442
443                 case 'f':
444                         toggle_flight();
445                         break;
446
447                 case 'p':
448                         if(mod & MOD_CTRL) {
449                                 fb_srgb = !fb_srgb;
450                                 show_message("gamma correction for non-sRGB framebuffers: %s\n", fb_srgb ? "off" : "on");
451                         }
452                         break;
453
454                 case '=':
455                         walk_speed *= 1.25;
456                         show_message("walk speed: %g", walk_speed);
457                         break;
458
459                 case '-':
460                         walk_speed *= 0.75;
461                         show_message("walk speed: %g", walk_speed);
462                         break;
463
464                 case ']':
465                         mouse_speed *= 1.2;
466                         show_message("mouse speed: %g", mouse_speed);
467                         break;
468
469                 case '[':
470                         mouse_speed *= 0.8;
471                         show_message("mouse speed: %g", mouse_speed);
472                         break;
473
474                 case 'b':
475                         show_blobs = !show_blobs;
476                         show_message("blobs: %s\n", show_blobs ? "on" : "off");
477                         break;
478
479                 case ' ':
480                         goatvr_recenter();
481                         show_message("VR recenter\n");
482                         break;
483                 }
484         }
485
486         if(key < 256 && !(mod & (MOD_CTRL | MOD_ALT))) {
487                 keystate[key] = pressed;
488         }
489 }
490
491 void app_mouse_button(int bn, bool pressed, int x, int y)
492 {
493         prev_mx = x;
494         prev_my = y;
495         bnstate[bn] = pressed;
496 }
497
498 static inline void mouse_look(float dx, float dy)
499 {
500         float scrsz = (float)win_height;
501         cam_theta += dx * 512.0 / scrsz;
502         cam_phi += dy * 512.0 / scrsz;
503
504         if(cam_phi < -90) cam_phi = -90;
505         if(cam_phi > 90) cam_phi = 90;
506 }
507
508 static void mouse_zoom(float dx, float dy)
509 {
510         cam_dist += dy * 0.1;
511         if(cam_dist < 0.0) cam_dist = 0.0;
512 }
513
514 void app_mouse_motion(int x, int y)
515 {
516         int dx = x - prev_mx;
517         int dy = y - prev_my;
518         prev_mx = x;
519         prev_my = y;
520
521         if(!dx && !dy) return;
522
523         if(bnstate[0]) {
524                 mouse_look(dx, dy);
525         }
526         if(bnstate[2]) {
527                 mouse_zoom(dx, dy);
528         }
529 }
530
531 void app_mouse_delta(int dx, int dy)
532 {
533         if(bnstate[2]) {
534                 mouse_zoom(dx * mouse_speed, dy * mouse_speed);
535         } else {
536                 mouse_look(dx * mouse_speed, dy * mouse_speed);
537         }
538 }
539
540 void app_gamepad_axis(int axis, float val)
541 {
542         switch(axis) {
543         case 0:
544                 joy_move.x = val;
545                 break;
546         case 1:
547                 joy_move.y = val;
548                 break;
549
550         case 2:
551                 joy_look.x = val;
552                 break;
553         case 3:
554                 joy_look.y = val;
555                 break;
556         }
557 }
558
559 void app_gamepad_button(int bn, bool pressed)
560 {
561         gpad_bnstate[bn] = pressed;
562
563         if(pressed) {
564                 switch(bn) {
565                 case GPAD_LSTICK:
566                         toggle_flight();
567                         break;
568
569                 case GPAD_X:
570                         show_blobs = !show_blobs;
571                         show_message("blobs: %s\n", show_blobs ? "on" : "off");
572                         break;
573
574                 case GPAD_START:
575                         goatvr_recenter();
576                         show_message("VR recenter\n");
577                         break;
578
579                 default:
580                         break;
581                 }
582         }
583 }
584
585 static void toggle_flight()
586 {
587         static float prev_walk_speed = -1.0;
588         if(prev_walk_speed < 0.0) {
589                 noclip = true;
590                 prev_walk_speed = walk_speed;
591                 walk_speed = 1000.0;
592                 show_message("fly mode\n");
593         } else {
594                 noclip = false;
595                 walk_speed = prev_walk_speed;
596                 prev_walk_speed = -1.0;
597                 show_message("walk mode\n");
598         }
599 }
600
601 static void calc_framerate()
602 {
603         static int ncalc;
604         static int nframes;
605         static long prev_upd;
606
607         long elapsed = time_msec - prev_upd;
608         if(elapsed >= 1000) {
609                 framerate = (float)nframes / (float)(elapsed * 0.001);
610                 nframes = 1;
611                 prev_upd = time_msec;
612
613                 /*if(++ncalc >= 5) {
614                         printf("fps: %f\n", framerate);
615                         ncalc = 0;
616                 }*/
617         } else {
618                 ++nframes;
619         }
620 }