moving the first exhibit, no picking yet
[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 "avatar.h"
18 #include "vrinput.h"
19 #include "exman.h"
20 #include "blob_exhibit.h"
21 #include "dbg_gui.h"
22
23 #define NEAR_CLIP       5.0
24 #define FAR_CLIP        10000.0
25
26 static void draw_scene();
27 static void toggle_flight();
28 static void calc_framerate();
29 static Ray calc_pick_ray(int x, int y, const Mat4 &view_mat, const Mat4 &proj_mat);
30
31 long time_msec;
32 int win_width, win_height;
33 float win_aspect;
34 bool fb_srgb;
35 bool opt_gear_wireframe;
36
37 TextureSet texman;
38 SceneSet sceneman;
39
40 unsigned int sdr_ltmap, sdr_ltmap_notex;
41
42 static Avatar avatar;
43
44 static float cam_dist = 0.0;
45 static float floor_y;   // last floor height
46 static float user_eye_height = 165;
47
48 static float walk_speed = 300.0f;
49 static float mouse_speed = 0.5f;
50 static bool show_walk_mesh, noclip = false;
51
52 static bool have_headtracking, have_handtracking, should_swap;
53
54 static int prev_mx, prev_my;
55 static bool bnstate[8];
56 static bool keystate[256];
57 static bool gpad_bnstate[64];
58 static Vec2 joy_move, joy_look;
59 static float joy_deadzone = 0.01;
60
61 static float framerate;
62
63 static Mat4 view_matrix, mouse_view_matrix, proj_matrix;
64 static MetaScene *mscn;
65 static unsigned int sdr_post_gamma;
66
67 static long prev_msec;
68
69 static ExhibitManager *exman;
70 static BlobExhibit *blobs;
71 static bool show_blobs;
72
73 static ExSelection ex_sel;
74
75 static Renderer *rend;
76
77
78 bool app_init(int argc, char **argv)
79 {
80         if(!init_options(argc, argv, "demo.conf")) {
81                 return false;
82         }
83         app_resize(opt.width, opt.height);
84         app_fullscreen(opt.fullscreen);
85
86         if(opt.vr) {
87                 if(goatvr_init() == -1) {
88                         return false;
89                 }
90                 goatvr_set_origin_mode(GOATVR_HEAD);
91                 goatvr_set_units_scale(100.0f);
92
93                 goatvr_startvr();
94                 should_swap = goatvr_should_swap() != 0;
95                 user_eye_height = goatvr_get_eye_height();
96                 have_headtracking = goatvr_have_headtracking();
97                 have_handtracking = goatvr_have_handtracking();
98
99                 goatvr_recenter();
100         }
101
102         if(fb_srgb) {
103                 int srgb_capable;
104                 glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable);
105                 printf("Framebuffer %s sRGB-capable\n", srgb_capable ? "is" : "is not");
106                 if(srgb_capable) {
107                         glEnable(GL_FRAMEBUFFER_SRGB);
108                 } else {
109                         fb_srgb = 0;
110                 }
111         }
112
113         glEnable(GL_MULTISAMPLE);
114         glEnable(GL_DEPTH_TEST);
115         glEnable(GL_CULL_FACE);
116         glEnable(GL_LIGHTING);
117         glEnable(GL_NORMALIZE);
118
119         if(!init_debug_gui()) {
120                 return false;
121         }
122
123         Mesh::use_custom_sdr_attr = false;
124
125         float ambient[] = {0.0, 0.0, 0.0, 0.0};
126         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
127
128         glClearColor(1, 1, 1, 1);
129
130         init_audio();
131
132         if(!init_vrhands()) {
133                 return false;
134         }
135
136         mscn = new MetaScene;
137         if(!mscn->load(opt.scenefile ? opt.scenefile : "data/museum.scene")) {
138                 return false;
139         }
140
141         avatar.pos = mscn->start_pos;
142         Vec3 dir = rotate(Vec3(0, 0, 1), mscn->start_rot);
143         dir.y = 0;
144         avatar.body_rot = rad_to_deg(acos(dot(dir, Vec3(0, 0, 1))));
145
146         exman = new ExhibitManager;
147         /*
148         if(!exman->load(mscn, "data/exhibits")) {
149                 //return false;
150         }
151         */
152
153         blobs = new BlobExhibit;
154         blobs->node = new SceneNode;
155         blobs->init();
156         blobs->node->set_position(Vec3(-680, 160, -100));
157         blobs->node->set_scaling(Vec3(28, 28, 28));
158         blobs->node->update(0);
159
160         exman->add(blobs);
161
162         if(!(sdr_ltmap_notex = create_program_load("sdr/lightmap.v.glsl", "sdr/lightmap-notex.p.glsl"))) {
163                 return false;
164         }
165         set_uniform_int(sdr_ltmap_notex, "texmap", MTL_TEX_DIFFUSE);
166         set_uniform_int(sdr_ltmap_notex, "lightmap", MTL_TEX_LIGHTMAP);
167
168         if(!(sdr_ltmap = create_program_load("sdr/lightmap.v.glsl", "sdr/lightmap-tex.p.glsl"))) {
169                 return false;
170         }
171         set_uniform_int(sdr_ltmap, "texmap", MTL_TEX_DIFFUSE);
172         set_uniform_int(sdr_ltmap, "lightmap", MTL_TEX_LIGHTMAP);
173
174         if(!fb_srgb) {
175                 sdr_post_gamma = create_program_load("sdr/post_gamma.v.glsl", "sdr/post_gamma.p.glsl");
176         }
177
178         rend = new Renderer;
179         rend->set_scene(mscn);
180
181         glUseProgram(0);
182
183         if(opt.vr || opt.fullscreen) {
184                 app_grab_mouse(true);
185         }
186
187         if(mscn->music && opt.music) {
188                 mscn->music->play(AUDIO_PLAYMODE_LOOP);
189         }
190         return true;
191 }
192
193 void app_cleanup()
194 {
195         if(mscn->music) {
196                 mscn->music->stop();
197         }
198         destroy_audio();
199
200         app_grab_mouse(false);
201         if(opt.vr) {
202                 goatvr_shutdown();
203         }
204         destroy_vrhands();
205
206         delete rend;
207
208         delete exman;
209
210         texman.clear();
211         sceneman.clear();
212
213         cleanup_debug_gui();
214 }
215
216 static bool constrain_walk_mesh(const Vec3 &v, Vec3 *newv)
217 {
218         Mesh *wm = mscn->walk_mesh;
219         if(!wm) {
220                 *newv = v;
221                 return true;
222         }
223
224         Ray downray = Ray(v, Vec3(0, -1, 0));
225         HitPoint hit;
226         if(mscn->walk_mesh->intersect(downray, &hit)) {
227                 *newv = hit.pos;
228                 newv->y += user_eye_height;
229                 return true;
230         }
231         return false;
232 }
233
234 static void update(float dt)
235 {
236         texman.update();
237         sceneman.update();
238
239         mscn->update(dt);
240         exman->update(dt);
241
242         float speed = walk_speed * dt;
243         Vec3 dir;
244
245         // joystick
246         float jdeadsq = joy_deadzone * joy_deadzone;
247         float jmove_lensq = length_sq(joy_move);
248         float jlook_lensq = length_sq(joy_look);
249
250         if(jmove_lensq > jdeadsq) {
251                 float len = sqrt(jmove_lensq);
252                 jmove_lensq -= jdeadsq;
253
254                 float mag = len * len;
255                 dir.x += mag * joy_move.x / len * 2.0 * speed;
256                 dir.z += mag * joy_move.y / len * 2.0 * speed;
257         }
258         if(jlook_lensq > jdeadsq) {
259                 float len = sqrt(jlook_lensq);
260                 jlook_lensq -= jdeadsq;
261
262                 float mag = len * len;
263                 avatar.body_rot += mag * joy_look.x / len * 200.0 * dt;
264                 avatar.head_alt += mag * joy_look.y / len * 100.0 * dt;
265                 if(avatar.head_alt < -90.0f) avatar.head_alt = -90.0f;
266                 if(avatar.head_alt > 90.0f) avatar.head_alt = 90.0f;
267         }
268
269         // keyboard move
270         if(keystate[(int)'w']) {
271                 dir.z -= speed;
272         }
273         if(keystate[(int)'s']) {
274                 dir.z += speed;
275         }
276         if(keystate[(int)'d']) {
277                 dir.x += speed;
278         }
279         if(keystate[(int)'a']) {
280                 dir.x -= speed;
281         }
282         if(keystate[(int)'q'] || gpad_bnstate[GPAD_UP]) {
283                 avatar.pos.y += speed;
284         }
285         if(keystate[(int)'z'] || gpad_bnstate[GPAD_DOWN]) {
286                 avatar.pos.y -= speed;
287         }
288
289         float theta = M_PI * avatar.body_rot / 180.0f;
290         Vec3 newpos;
291         newpos.x = avatar.pos.x + cos(theta) * dir.x - sin(theta) * dir.z;
292         newpos.y = avatar.pos.y;
293         newpos.z = avatar.pos.z + sin(theta) * dir.x + cos(theta) * dir.z;
294
295         if(noclip) {
296                 avatar.pos = newpos;
297         } else {
298                 if(!constrain_walk_mesh(newpos, &avatar.pos)) {
299                         float dtheta = M_PI / 32.0;
300                         float theta = dtheta;
301                         Vec2 dir2d = newpos.xz() - avatar.pos.xz();
302
303                         for(int i=0; i<16; i++) {
304                                 Vec2 dvec = rotate(dir2d, theta);
305                                 Vec3 pos = avatar.pos + Vec3(dvec.x, 0, dvec.y);
306                                 if(constrain_walk_mesh(pos, &avatar.pos)) {
307                                         break;
308                                 }
309                                 dvec = rotate(dir2d, -theta);
310                                 pos = avatar.pos + Vec3(dvec.x, 0, dvec.y);
311                                 if(constrain_walk_mesh(pos, &avatar.pos)) {
312                                         break;
313                                 }
314                                 theta += dtheta;
315                         }
316                 }
317                 floor_y = avatar.pos.y - user_eye_height;
318         }
319
320         // TODO move to avatar
321         // calculate mouselook view matrix
322         mouse_view_matrix = Mat4::identity;
323         mouse_view_matrix.pre_translate(0, 0, -cam_dist);
324         if(!have_headtracking) {
325                 mouse_view_matrix.pre_rotate_x(deg_to_rad(avatar.head_alt));
326         }
327         mouse_view_matrix.pre_rotate_y(deg_to_rad(avatar.body_rot));
328         mouse_view_matrix.pre_translate(-avatar.pos.x, -avatar.pos.y, -avatar.pos.z);
329
330         // update hand-tracking
331         if(have_handtracking) {
332                 update_vrhands(&avatar);
333         }
334 }
335
336 static void set_light(int idx, const Vec3 &pos, const Vec3 &color)
337 {
338         unsigned int lt = GL_LIGHT0 + idx;
339         float posv[] = { pos.x, pos.y, pos.z, 1 };
340         float colv[] = { color.x, color.y, color.z, 1 };
341
342         glEnable(lt);
343         glLightfv(lt, GL_POSITION, posv);
344         glLightfv(lt, GL_DIFFUSE, colv);
345         glLightfv(lt, GL_SPECULAR, colv);
346 }
347
348 void app_display()
349 {
350         float dt = (float)(time_msec - prev_msec) / 1000.0f;
351         prev_msec = time_msec;
352
353         if(debug_gui) {
354                 ImGui::GetIOPtr()->DeltaTime = dt;
355                 ImGui::NewFrame();
356
357                 ImGui::ShowTestWindow();
358         }
359
360         if(opt.vr) {
361                 // VR mode
362                 goatvr_draw_start();
363                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
364
365                 update(dt);
366
367                 for(int i=0; i<2; i++) {
368                         // for each eye
369                         goatvr_draw_eye(i);
370
371                         proj_matrix = goatvr_projection_matrix(i, NEAR_CLIP, FAR_CLIP);
372                         glMatrixMode(GL_PROJECTION);
373                         glLoadMatrixf(proj_matrix[0]);
374
375                         view_matrix = mouse_view_matrix * Mat4(goatvr_view_matrix(i));
376                         glMatrixMode(GL_MODELVIEW);
377                         glLoadMatrixf(view_matrix[0]);
378
379                         draw_scene();
380                         if(have_handtracking) {
381                                 draw_vrhands();
382                         }
383
384                         if(debug_gui) {
385                                 ImGui::Render();
386                         }
387                 }
388                 goatvr_draw_done();
389
390                 if(should_swap) {
391                         app_swap_buffers();
392                 }
393
394         } else {
395                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
396
397                 update(dt);
398
399                 proj_matrix.perspective(deg_to_rad(50.0), win_aspect, NEAR_CLIP, FAR_CLIP);
400                 glMatrixMode(GL_PROJECTION);
401                 glLoadMatrixf(proj_matrix[0]);
402
403                 view_matrix = mouse_view_matrix;
404                 glMatrixMode(GL_MODELVIEW);
405                 glLoadMatrixf(view_matrix[0]);
406
407                 draw_scene();
408
409                 if(!fb_srgb && sdr_post_gamma) {
410                         slow_post(sdr_post_gamma);
411                         glUseProgram(0);
412                 }
413
414                 if(debug_gui) {
415                         ImGui::Render();
416                 }
417                 app_swap_buffers();
418         }
419         assert(glGetError() == GL_NO_ERROR);
420
421         calc_framerate();
422 }
423
424
425 static void draw_scene()
426 {
427         static const Vec3 lpos[] = { Vec3(-50, 75, 100), Vec3(100, 0, 30), Vec3(-10, -10, 60) };
428         set_light(0, lpos[0], Vec3(1.0, 0.8, 0.7) * 0.8);
429         set_light(1, lpos[1], Vec3(0.6, 0.7, 1.0) * 0.6);
430         set_light(2, lpos[2], Vec3(0.8, 1.0, 0.8) * 0.3);
431
432         rend->draw();
433         exman->draw();
434
435         /*
436         if(have_handtracking) {
437                 Mat4 head_xform = inverse(mouse_view_matrix);//goatvr_head_matrix();
438                 Mat4 head_dir_xform = head_xform.upper3x3();
439
440                 glUseProgram(0);
441                 glPushAttrib(GL_ENABLE_BIT);
442                 glDisable(GL_LIGHTING);
443                 glBegin(GL_LINES);
444                 for(int i=0; i<2; i++) {
445                         if(hand[i].valid) {
446                                 glColor3f(i, 1 - i, i);
447                         } else {
448                                 glColor3f(0.5, 0.5, 0.5);
449                         }
450                         Vec3 v = head_xform * hand[i].pos;
451                         Vec3 dir = head_dir_xform * rotate(Vec3(0, 0, -1), hand[i].rot) * 20.0f;
452                         Vec3 up = head_dir_xform * rotate(Vec3(0, 1, 0), hand[i].rot) * 10.0f;
453                         Vec3 right = head_dir_xform * rotate(Vec3(1, 0, 0), hand[i].rot) * 10.0f;
454
455                         glVertex3f(v.x, v.y, v.z);
456                         glVertex3f(v.x + dir.x, v.y + dir.y, v.z + dir.z);
457                         glVertex3f(v.x - right.x, v.y - right.y, v.z - right.z);
458                         glVertex3f(v.x + right.x, v.y + right.y, v.z + right.z);
459                         glVertex3f(v.x - up.x, v.y - up.y, v.z - up.z);
460                         glVertex3f(v.x + up.x, v.y + up.y, v.z + up.z);
461                 }
462                 glEnd();
463                 glPopAttrib();
464         }
465         */
466
467         if(show_walk_mesh && mscn->walk_mesh) {
468                 glPushAttrib(GL_ENABLE_BIT);
469                 glEnable(GL_BLEND);
470                 glBlendFunc(GL_ONE, GL_ONE);
471                 glDisable(GL_LIGHTING);
472                 glEnable(GL_POLYGON_OFFSET_FILL);
473
474                 glUseProgram(0);
475
476                 glPolygonOffset(-1, 1);
477                 glDepthMask(0);
478
479                 glColor3f(0.3, 0.08, 0.01);
480                 mscn->walk_mesh->draw();
481
482                 glDepthMask(1);
483
484                 glPopAttrib();
485         }
486
487         print_text(Vec2(9 * win_width / 10, 20), Vec3(1, 1, 0), "fps: %.1f", framerate);
488         draw_ui();
489 }
490
491
492 void app_reshape(int x, int y)
493 {
494         glViewport(0, 0, x, y);
495         goatvr_set_fb_size(x, y, 1.0f);
496         debug_gui_reshape(x, y);
497 }
498
499 void app_keyboard(int key, bool pressed)
500 {
501         unsigned int mod = app_get_modifiers();
502
503         if(debug_gui && !(pressed && (key == '`' || key == 27))) {
504                 debug_gui_key(key, pressed, mod);
505                 return; // ignore all keystrokes when GUI is visible
506         }
507
508         if(pressed) {
509                 switch(key) {
510                 case 27:
511                         app_quit();
512                         break;
513
514                 case '\n':
515                 case '\r':
516                         if(mod & MOD_ALT) {
517                                 app_toggle_fullscreen();
518                         }
519                         break;
520
521                 case '`':
522                         debug_gui = !debug_gui;
523                         show_message("debug gui %s", debug_gui ? "enabled" : "disabled");
524                         break;
525
526                 case 'm':
527                         app_toggle_grab_mouse();
528                         show_message("mouse %s", app_is_mouse_grabbed() ? "grabbed" : "released");
529                         break;
530
531                 case 'w':
532                         if(mod & MOD_CTRL) {
533                                 show_walk_mesh = !show_walk_mesh;
534                                 show_message("walk mesh: %s", show_walk_mesh ? "on" : "off");
535                         }
536                         break;
537
538                 case 'c':
539                         if(mod & MOD_CTRL) {
540                                 noclip = !noclip;
541                                 show_message(noclip ? "no clip" : "clip");
542                         }
543                         break;
544
545                 case 'f':
546                         toggle_flight();
547                         break;
548
549                 case 'p':
550                         if(mod & MOD_CTRL) {
551                                 fb_srgb = !fb_srgb;
552                                 show_message("gamma correction for non-sRGB framebuffers: %s\n", fb_srgb ? "off" : "on");
553                         }
554                         break;
555
556                 case '=':
557                         walk_speed *= 1.25;
558                         show_message("walk speed: %g", walk_speed);
559                         break;
560
561                 case '-':
562                         walk_speed *= 0.75;
563                         show_message("walk speed: %g", walk_speed);
564                         break;
565
566                 case ']':
567                         mouse_speed *= 1.2;
568                         show_message("mouse speed: %g", mouse_speed);
569                         break;
570
571                 case '[':
572                         mouse_speed *= 0.8;
573                         show_message("mouse speed: %g", mouse_speed);
574                         break;
575
576                 case 'b':
577                         show_blobs = !show_blobs;
578                         show_message("blobs: %s\n", show_blobs ? "on" : "off");
579                         break;
580
581                 case ' ':
582                         goatvr_recenter();
583                         show_message("VR recenter\n");
584                         break;
585
586                 case 'x':
587                         exman->load(mscn, "data/exhibits");
588                         break;
589                 }
590         }
591
592         if(key < 256 && !(mod & (MOD_CTRL | MOD_ALT))) {
593                 keystate[key] = pressed;
594         }
595 }
596
597 void app_mouse_button(int bn, bool pressed, int x, int y)
598 {
599         if(debug_gui) {
600                 debug_gui_mbutton(bn, pressed, x, y);
601                 return; // ignore mouse events while GUI is visible
602         }
603
604         prev_mx = x;
605         prev_my = y;
606         bnstate[bn] = pressed;
607
608         if(bn == 0) {
609                 if(!pressed) {
610                         if(ex_sel.ex) {
611                                 ex_sel.ex = 0;
612                         } else {
613                                 Ray ray = calc_pick_ray(x, y, view_matrix, proj_matrix);
614                                 ex_sel = exman->select(ray);
615                         }
616                 }
617         }
618 }
619
620 static inline void mouse_look(float dx, float dy)
621 {
622         float scrsz = (float)win_height;
623         avatar.body_rot += dx * 512.0 / scrsz;
624         avatar.head_alt += dy * 512.0 / scrsz;
625
626         if(avatar.head_alt < -90) avatar.head_alt = -90;
627         if(avatar.head_alt > 90) avatar.head_alt = 90;
628 }
629
630 static void mouse_zoom(float dx, float dy)
631 {
632         cam_dist += dy * 0.1;
633         if(cam_dist < 0.0) cam_dist = 0.0;
634 }
635
636 void app_mouse_motion(int x, int y)
637 {
638         if(debug_gui) {
639                 debug_gui_mmotion(x, y);
640                 return; // ignore mouse events while GUI is visible
641         }
642
643         int dx = x - prev_mx;
644         int dy = y - prev_my;
645         prev_mx = x;
646         prev_my = y;
647
648         if(!dx && !dy) return;
649
650         if(ex_sel.ex) {
651                 Vec3 pos = ex_sel.ex->node->get_node_position();
652                 Vec3 dir = transpose(view_matrix.upper3x3()) * Vec3(dx * 1.0, dy * -1.0, 0);
653
654                 ex_sel.ex->node->set_position(pos + dir);
655         }
656
657         if(bnstate[2]) {
658                 mouse_look(dx, dy);
659         }
660 }
661
662 void app_mouse_delta(int dx, int dy)
663 {
664         if(bnstate[2]) {
665                 mouse_zoom(dx * mouse_speed, dy * mouse_speed);
666         } else {
667                 mouse_look(dx * mouse_speed, dy * mouse_speed);
668         }
669 }
670
671 void app_mouse_wheel(int dir)
672 {
673         if(debug_gui) {
674                 debug_gui_wheel(dir);
675         }
676 }
677
678 void app_gamepad_axis(int axis, float val)
679 {
680         switch(axis) {
681         case 0:
682                 joy_move.x = val;
683                 break;
684         case 1:
685                 joy_move.y = val;
686                 break;
687
688         case 2:
689                 joy_look.x = val;
690                 break;
691         case 3:
692                 joy_look.y = val;
693                 break;
694         }
695 }
696
697 void app_gamepad_button(int bn, bool pressed)
698 {
699         gpad_bnstate[bn] = pressed;
700
701         if(pressed) {
702                 switch(bn) {
703                 case GPAD_LSTICK:
704                         toggle_flight();
705                         break;
706
707                 case GPAD_X:
708                         show_blobs = !show_blobs;
709                         show_message("blobs: %s\n", show_blobs ? "on" : "off");
710                         break;
711
712                 case GPAD_START:
713                         goatvr_recenter();
714                         show_message("VR recenter\n");
715                         break;
716
717                 default:
718                         break;
719                 }
720         }
721 }
722
723 static void toggle_flight()
724 {
725         static float prev_walk_speed = -1.0;
726         if(prev_walk_speed < 0.0) {
727                 noclip = true;
728                 prev_walk_speed = walk_speed;
729                 walk_speed = 1000.0;
730                 show_message("fly mode\n");
731         } else {
732                 noclip = false;
733                 walk_speed = prev_walk_speed;
734                 prev_walk_speed = -1.0;
735                 show_message("walk mode\n");
736         }
737 }
738
739 static void calc_framerate()
740 {
741         static int ncalc;
742         static int nframes;
743         static long prev_upd;
744
745         long elapsed = time_msec - prev_upd;
746         if(elapsed >= 1000) {
747                 framerate = (float)nframes / (float)(elapsed * 0.001);
748                 nframes = 1;
749                 prev_upd = time_msec;
750
751                 /*if(++ncalc >= 5) {
752                         printf("fps: %f\n", framerate);
753                         ncalc = 0;
754                 }*/
755         } else {
756                 ++nframes;
757         }
758 }
759
760 static Ray calc_pick_ray(int x, int y, const Mat4 &view_mat, const Mat4 &proj_mat)
761 {
762         return Ray();   // TODO
763 }