mouse speed
[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
17 #define NEAR_CLIP       5.0
18 #define FAR_CLIP        10000.0
19
20 static void draw_scene();
21
22 long time_msec;
23 int win_width, win_height;
24 float win_aspect;
25 bool fb_srgb;
26 bool opt_gear_wireframe;
27
28 static float cam_dist = 0.0;
29 static float cam_theta, cam_phi = 20;
30 static Vec3 cam_pos;
31 static float floor_y;   // last floor height
32 static float user_eye_height = 165;
33
34 static float walk_speed = 300.0f;
35 static float mouse_speed = 1.0f;
36 static bool show_walk_mesh, noclip = false;
37
38 static bool have_headtracking, should_swap;
39
40 static int prev_mx, prev_my;
41 static bool bnstate[8];
42 static bool keystate[256];
43
44 static Mat4 view_matrix, mouse_view_matrix, proj_matrix;
45 static TextureSet texman;
46 static Scene *scn;
47 static unsigned int sdr, sdr_post_gamma;
48
49 static long prev_msec;
50
51
52 bool app_init(int argc, char **argv)
53 {
54         if(!init_options(argc, argv, "demo.conf")) {
55                 return false;
56         }
57         app_resize(opt.width, opt.height);
58         app_fullscreen(opt.fullscreen);
59
60         if(opt.vr) {
61                 if(goatvr_init() == -1) {
62                         return false;
63                 }
64                 goatvr_set_origin_mode(GOATVR_HEAD);
65                 goatvr_set_units_scale(100.0f);
66
67                 goatvr_startvr();
68                 should_swap = goatvr_should_swap() != 0;
69                 user_eye_height = goatvr_get_eye_height();
70                 have_headtracking = goatvr_have_headtracking();
71
72                 goatvr_recenter();
73         }
74
75         int srgb_capable;
76         glGetIntegerv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable);
77         printf("Framebuffer %s sRGB-capable\n", srgb_capable ? "is" : "is not");
78         fb_srgb = srgb_capable != 0;
79         glEnable(GL_FRAMEBUFFER_SRGB);
80
81         glEnable(GL_MULTISAMPLE);
82         glEnable(GL_DEPTH_TEST);
83         glEnable(GL_CULL_FACE);
84         glEnable(GL_LIGHTING);
85         glEnable(GL_NORMALIZE);
86
87         Mesh::use_custom_sdr_attr = false;
88
89         float ambient[] = {0.0, 0.0, 0.0, 0.0};
90         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
91
92         glClearColor(0.2, 0.2, 0.2, 1.0);
93
94         scn = new Scene(&texman);
95         if(!load_scene(scn, "data/museum.scene")) {
96                 return false;
97         }
98
99         // set initial cam_pos above the center of the walk mesh (if any)
100         if(scn->walk_mesh) {
101                 Vec3 bcent;
102                 float brad;
103                 scn->walk_mesh->get_bsphere(&bcent, &brad);
104
105                 floor_y = bcent.y;
106                 cam_pos = bcent + Vec3(0, user_eye_height, 0);
107         }
108
109         if(!(sdr = create_program_load("sdr/test.v.glsl", "sdr/test.p.glsl"))) {
110                 fprintf(stderr, "failed to load test shaders\n");
111                 return false;
112         }
113         set_uniform_int(sdr, "texmap", 0);
114         set_uniform_int(sdr, "lightmap", 1);
115
116         if(!fb_srgb) {
117                 sdr_post_gamma = create_program_load("sdr/post_gamma.v.glsl", "sdr/post_gamma.p.glsl");
118         }
119
120         glUseProgram(0);
121
122         if(opt.vr || opt.fullscreen) {
123                 app_grab_mouse(true);
124         }
125         return true;
126 }
127
128 void app_cleanup()
129 {
130         app_grab_mouse(false);
131         if(opt.vr) {
132                 goatvr_shutdown();
133         }
134         texman.clear();
135 }
136
137 static bool constrain_walk_mesh(const Vec3 &v, Vec3 *newv)
138 {
139         Mesh *wm = scn->walk_mesh;
140         if(!wm) {
141                 *newv = v;
142                 return true;
143         }
144
145         Ray downray = Ray(v, Vec3(0, -1, 0));
146         HitPoint hit;
147         if(scn->walk_mesh->intersect(downray, &hit)) {
148                 *newv = hit.pos;
149                 newv->y += user_eye_height;
150                 return true;
151         }
152         return false;
153 }
154
155 static void update(float dt)
156 {
157         texman.update();
158
159         scn->update(dt);
160
161         float speed = walk_speed * dt;
162         Vec3 dir;
163
164         if(keystate[(int)'w']) {
165                 dir.z -= speed;
166         }
167         if(keystate[(int)'s']) {
168                 dir.z += speed;
169         }
170         if(keystate[(int)'d']) {
171                 dir.x += speed;
172         }
173         if(keystate[(int)'a']) {
174                 dir.x -= speed;
175         }
176         if(keystate[(int)'q']) {
177                 cam_pos.y += speed;
178         }
179         if(keystate[(int)'z']) {
180                 cam_pos.y -= speed;
181         }
182
183         float theta = M_PI * cam_theta / 180.0f;
184         Vec3 newpos;
185         newpos.x = cam_pos.x + cos(theta) * dir.x - sin(theta) * dir.z;
186         newpos.y = cam_pos.y;
187         newpos.z = cam_pos.z + sin(theta) * dir.x + cos(theta) * dir.z;
188
189         if(noclip) {
190                 cam_pos = newpos;
191         } else {
192                 if(!constrain_walk_mesh(newpos, &cam_pos)) {
193                         float dtheta = M_PI / 32.0;
194                         float theta = dtheta;
195                         Vec2 dir2d = newpos.xz() - cam_pos.xz();
196
197                         for(int i=0; i<16; i++) {
198                                 Vec2 dvec = rotate(dir2d, theta);
199                                 Vec3 pos = cam_pos + Vec3(dvec.x, 0, dvec.y);
200                                 if(constrain_walk_mesh(pos, &cam_pos)) {
201                                         break;
202                                 }
203                                 dvec = rotate(dir2d, -theta);
204                                 pos = cam_pos + Vec3(dvec.x, 0, dvec.y);
205                                 if(constrain_walk_mesh(pos, &cam_pos)) {
206                                         break;
207                                 }
208                                 theta += dtheta;
209                         }
210                 }
211                 floor_y = cam_pos.y - user_eye_height;
212         }
213
214         // calculate mouselook view matrix
215         mouse_view_matrix = Mat4::identity;
216         mouse_view_matrix.pre_translate(0, 0, -cam_dist);
217         if(!have_headtracking) {
218                 mouse_view_matrix.pre_rotate_x(deg_to_rad(cam_phi));
219         }
220         mouse_view_matrix.pre_rotate_y(deg_to_rad(cam_theta));
221         mouse_view_matrix.pre_translate(-cam_pos.x, -cam_pos.y, -cam_pos.z);
222 }
223
224 static void set_light(int idx, const Vec3 &pos, const Vec3 &color)
225 {
226         unsigned int lt = GL_LIGHT0 + idx;
227         float posv[] = { pos.x, pos.y, pos.z, 1 };
228         float colv[] = { color.x, color.y, color.z, 1 };
229
230         glEnable(lt);
231         glLightfv(lt, GL_POSITION, posv);
232         glLightfv(lt, GL_DIFFUSE, colv);
233         glLightfv(lt, GL_SPECULAR, colv);
234 }
235
236 void app_display()
237 {
238         float dt = (float)(time_msec - prev_msec) / 1000.0f;
239         prev_msec = time_msec;
240
241         update(dt);
242
243         if(opt.vr) {
244                 // VR mode
245                 goatvr_draw_start();
246                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
247
248                 for(int i=0; i<2; i++) {
249                         // for each eye
250                         goatvr_draw_eye(i);
251
252                         proj_matrix = goatvr_projection_matrix(i, NEAR_CLIP, FAR_CLIP);
253                         glMatrixMode(GL_PROJECTION);
254                         glLoadMatrixf(proj_matrix[0]);
255
256                         view_matrix = mouse_view_matrix * Mat4(goatvr_view_matrix(i));
257                         glMatrixMode(GL_MODELVIEW);
258                         glLoadMatrixf(view_matrix[0]);
259
260                         draw_scene();
261                 }
262                 goatvr_draw_done();
263
264                 if(should_swap) {
265                         app_swap_buffers();
266                 }
267
268         } else {
269                 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
270
271                 proj_matrix.perspective(deg_to_rad(50.0), win_aspect, NEAR_CLIP, FAR_CLIP);
272                 glMatrixMode(GL_PROJECTION);
273                 glLoadMatrixf(proj_matrix[0]);
274
275                 view_matrix = mouse_view_matrix;
276                 glMatrixMode(GL_MODELVIEW);
277                 glLoadMatrixf(view_matrix[0]);
278
279                 draw_scene();
280
281                 if(!fb_srgb && sdr_post_gamma) {
282                         slow_post(sdr_post_gamma);
283                         glUseProgram(0);
284                 }
285                 app_swap_buffers();
286         }
287         assert(glGetError() == GL_NO_ERROR);
288 }
289
290
291 static void draw_scene()
292 {
293         static const Vec3 lpos[] = { Vec3(-50, 75, 100), Vec3(100, 0, 30), Vec3(-10, -10, 60) };
294         set_light(0, lpos[0], Vec3(1.0, 0.8, 0.7) * 0.8);
295         set_light(1, lpos[1], Vec3(0.6, 0.7, 1.0) * 0.6);
296         set_light(2, lpos[2], Vec3(0.8, 1.0, 0.8) * 0.3);
297
298         glUseProgram(sdr);
299         scn->draw();
300         glUseProgram(0);
301
302         if(show_walk_mesh && scn->walk_mesh) {
303                 glPushAttrib(GL_ENABLE_BIT);
304                 glEnable(GL_BLEND);
305                 glBlendFunc(GL_ONE, GL_ONE);
306                 glDisable(GL_LIGHTING);
307                 glEnable(GL_POLYGON_OFFSET_FILL);
308
309                 glPolygonOffset(-1, 1);
310                 glDepthMask(0);
311
312                 glColor3f(0.3, 0.08, 0.01);
313                 scn->walk_mesh->draw();
314
315                 glDepthMask(1);
316
317                 glPopAttrib();
318         }
319
320         draw_ui();
321 }
322
323
324 void app_reshape(int x, int y)
325 {
326         glViewport(0, 0, x, y);
327         goatvr_set_fb_size(x, y, 1.0f);
328 }
329
330 void app_keyboard(int key, bool pressed)
331 {
332         unsigned int mod = app_get_modifiers();
333
334         if(pressed) {
335                 switch(key) {
336                 case 27:
337                         app_quit();
338                         break;
339
340                 case 'f':
341                         app_toggle_fullscreen();
342                         break;
343
344                 case '`':
345                         app_toggle_grab_mouse();
346                         show_message("mouse %s", app_is_mouse_grabbed() ? "grabbed" : "released");
347                         break;
348
349                 case 'w':
350                         if(mod & MOD_CTRL) {
351                                 show_walk_mesh = !show_walk_mesh;
352                                 show_message("walk mesh: %s", show_walk_mesh ? "on" : "off");
353                         }
354                         break;
355
356                 case 'c':
357                         if(mod & MOD_CTRL) {
358                                 noclip = !noclip;
359                                 show_message(noclip ? "no clip" : "clip");
360                         }
361                         break;
362
363                 case 'p':
364                         if(mod & MOD_CTRL) {
365                                 fb_srgb = !fb_srgb;
366                                 show_message("gamma correction for non-sRGB framebuffers: %s\n", fb_srgb ? "off" : "on");
367                         }
368                         break;
369
370                 case '=':
371                         walk_speed *= 1.25;
372                         show_message("walk speed: %g", walk_speed);
373                         break;
374
375                 case '-':
376                         walk_speed *= 0.75;
377                         show_message("walk speed: %g", walk_speed);
378                         break;
379
380                 case ']':
381                         mouse_speed *= 1.2;
382                         show_message("mouse speed: %g", mouse_speed);
383                         break;
384
385                 case '[':
386                         mouse_speed *= 0.8;
387                         show_message("mouse speed: %g", mouse_speed);
388                         break;
389                 }
390         }
391
392         if(key < 256 && !(mod & (MOD_CTRL | MOD_ALT))) {
393                 keystate[key] = pressed;
394         }
395 }
396
397 void app_mouse_button(int bn, bool pressed, int x, int y)
398 {
399         prev_mx = x;
400         prev_my = y;
401         bnstate[bn] = pressed;
402 }
403
404 static inline void mouse_look(int dx, int dy)
405 {
406         float scrsz = (float)win_height;
407         cam_theta += dx * 512.0 / scrsz;
408         cam_phi += dy * 512.0 / scrsz;
409
410         if(cam_phi < -90) cam_phi = -90;
411         if(cam_phi > 90) cam_phi = 90;
412 }
413
414 static void mouse_zoom(int dx, int dy)
415 {
416         cam_dist += dy * 0.1;
417         if(cam_dist < 0.0) cam_dist = 0.0;
418 }
419
420 void app_mouse_motion(int x, int y)
421 {
422         int dx = x - prev_mx;
423         int dy = y - prev_my;
424         prev_mx = x;
425         prev_my = y;
426
427         if(!dx && !dy) return;
428
429         if(bnstate[0]) {
430                 mouse_look(dx, dy);
431         }
432         if(bnstate[2]) {
433                 mouse_zoom(dx, dy);
434         }
435 }
436
437 void app_mouse_delta(int dx, int dy)
438 {
439         if(bnstate[2]) {
440                 mouse_zoom(dx * mouse_speed, dy * mouse_speed);
441         } else {
442                 mouse_look(dx * mouse_speed, dy * mouse_speed);
443         }
444 }