ui notifications
[laserbrain_demo] / src / app.cc
1 #include <stdio.h>
2 #include <assert.h>
3 #include "app.h"
4 #include "opengl.h"
5 #include "sdr.h"
6 #include "texture.h"
7 #include "mesh.h"
8 #include "meshgen.h"
9 #include "scene.h"
10 #include "metascene.h"
11 #include "datamap.h"
12 #include "ui.h"
13
14 static void draw_scene();
15
16 long time_msec;
17 int win_width, win_height;
18 float win_aspect;
19 bool opt_gear_wireframe;
20
21 static float cam_dist = 0.0;
22 static float cam_theta, cam_phi = 20;
23 static Vec3 cam_pos;
24 static float floor_y;   // last floor height
25 static float user_eye_height = 165;
26
27 static float walk_speed = 400.0f;
28 static bool show_walk_mesh, noclip = false;
29
30 static int prev_mx, prev_my;
31 static bool bnstate[8];
32 static bool keystate[256];
33
34 static Mat4 view_matrix;
35 static TextureSet texman;
36 static Scene *scn;
37 static unsigned int sdr;
38
39 static long prev_msec;
40
41
42 bool app_init()
43 {
44         glEnable(GL_FRAMEBUFFER_SRGB);
45         glEnable(GL_MULTISAMPLE);
46         glEnable(GL_DEPTH_TEST);
47         glEnable(GL_CULL_FACE);
48         glEnable(GL_LIGHTING);
49         glEnable(GL_NORMALIZE);
50
51         Mesh::use_custom_sdr_attr = false;
52
53         float ambient[] = {0.0, 0.0, 0.0, 0.0};
54         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
55
56         scn = new Scene(&texman);
57         if(!load_scene(scn, "data/museum.scene")) {
58                 return false;
59         }
60
61         // set initial cam_pos above the center of the walk mesh (if any)
62         if(scn->walk_mesh) {
63                 Vec3 bcent;
64                 float brad;
65                 scn->walk_mesh->get_bsphere(&bcent, &brad);
66
67                 floor_y = bcent.y;
68                 cam_pos = bcent + Vec3(0, user_eye_height, 0);
69         }
70
71         if(!(sdr = create_program_load("sdr/test.v.glsl", "sdr/test.p.glsl"))) {
72                 fprintf(stderr, "failed to load test shaders\n");
73                 return false;
74         }
75         set_uniform_int(sdr, "texmap", 0);
76         set_uniform_int(sdr, "lightmap", 1);
77
78         glUseProgram(0);
79         return true;
80 }
81
82 void app_cleanup()
83 {
84         texman.clear();
85 }
86
87 static bool constrain_walk_mesh(const Vec3 &v, Vec3 *newv)
88 {
89         Mesh *wm = scn->walk_mesh;
90         if(!wm) {
91                 *newv = v;
92                 return true;
93         }
94
95         Ray downray = Ray(v, Vec3(0, -1, 0));
96         HitPoint hit;
97         if(scn->walk_mesh->intersect(downray, &hit)) {
98                 *newv = hit.pos;
99                 newv->y += user_eye_height;
100                 return true;
101         }
102         return false;
103 }
104
105 static void update(float dt)
106 {
107         texman.update();
108
109         scn->update(dt);
110
111         float speed = walk_speed * dt;
112         Vec3 dir;
113
114         if(keystate[(int)'w']) {
115                 dir.z -= speed;
116         }
117         if(keystate[(int)'s']) {
118                 dir.z += speed;
119         }
120         if(keystate[(int)'d']) {
121                 dir.x += speed;
122         }
123         if(keystate[(int)'a']) {
124                 dir.x -= speed;
125         }
126         if(keystate[(int)'q']) {
127                 cam_pos.y += speed;
128         }
129         if(keystate[(int)'z']) {
130                 cam_pos.y -= speed;
131         }
132
133         float theta = M_PI * cam_theta / 180.0f;
134         Vec3 newpos;
135         newpos.x = cam_pos.x + cos(theta) * dir.x - sin(theta) * dir.z;
136         newpos.y = cam_pos.y;
137         newpos.z = cam_pos.z + sin(theta) * dir.x + cos(theta) * dir.z;
138
139         if(noclip) {
140                 cam_pos = newpos;
141         } else {
142                 if(!constrain_walk_mesh(newpos, &cam_pos)) {
143                         float dtheta = M_PI / 32.0;
144                         float theta = dtheta;
145                         Vec2 dir2d = newpos.xz() - cam_pos.xz();
146
147                         for(int i=0; i<16; i++) {
148                                 Vec2 dvec = rotate(dir2d, theta);
149                                 Vec3 pos = cam_pos + Vec3(dvec.x, 0, dvec.y);
150                                 if(constrain_walk_mesh(pos, &cam_pos)) {
151                                         break;
152                                 }
153                                 dvec = rotate(dir2d, -theta);
154                                 pos = cam_pos + Vec3(dvec.x, 0, dvec.y);
155                                 if(constrain_walk_mesh(pos, &cam_pos)) {
156                                         break;
157                                 }
158                                 theta += dtheta;
159                         }
160                 }
161                 floor_y = cam_pos.y - user_eye_height;
162         }
163 }
164
165 static void set_light(int idx, const Vec3 &pos, const Vec3 &color)
166 {
167         unsigned int lt = GL_LIGHT0 + idx;
168         float posv[] = { pos.x, pos.y, pos.z, 1 };
169         float colv[] = { color.x, color.y, color.z, 1 };
170
171         glEnable(lt);
172         glLightfv(lt, GL_POSITION, posv);
173         glLightfv(lt, GL_DIFFUSE, colv);
174         glLightfv(lt, GL_SPECULAR, colv);
175 }
176
177 void app_display()
178 {
179         float dt = (float)(time_msec - prev_msec) / 1000.0f;
180         prev_msec = time_msec;
181
182         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
183
184         view_matrix = Mat4::identity;
185         view_matrix.pre_translate(0, 0, -cam_dist);
186         view_matrix.pre_rotate(deg_to_rad(cam_phi), 1, 0, 0);
187         view_matrix.pre_rotate(deg_to_rad(cam_theta), 0, 1, 0);
188         view_matrix.pre_translate(-cam_pos.x, -cam_pos.y, -cam_pos.z);
189
190         glMatrixMode(GL_MODELVIEW);
191         glLoadMatrixf(view_matrix[0]);
192
193         static const Vec3 lpos[] = { Vec3(-50, 75, 100), Vec3(100, 0, 30), Vec3(-10, -10, 60) };
194         set_light(0, lpos[0], Vec3(1.0, 0.8, 0.7) * 0.8);
195         set_light(1, lpos[1], Vec3(0.6, 0.7, 1.0) * 0.6);
196         set_light(2, lpos[2], Vec3(0.8, 1.0, 0.8) * 0.3);
197
198         update(dt);
199
200         draw_scene();
201         draw_ui();
202
203         app_swap_buffers();
204         assert(glGetError() == GL_NO_ERROR);
205 }
206
207
208 static void draw_scene()
209 {
210         glUseProgram(sdr);
211         scn->draw();
212         glUseProgram(0);
213
214         if(show_walk_mesh && scn->walk_mesh) {
215                 glPushAttrib(GL_ENABLE_BIT);
216                 glEnable(GL_BLEND);
217                 glBlendFunc(GL_ONE, GL_ONE);
218                 glDisable(GL_LIGHTING);
219                 glEnable(GL_POLYGON_OFFSET_FILL);
220
221                 glPolygonOffset(-1, 1);
222                 glDepthMask(0);
223
224                 glColor3f(0.3, 0.08, 0.01);
225                 scn->walk_mesh->draw();
226
227                 glDepthMask(1);
228
229                 glPopAttrib();
230         }
231 }
232
233
234 void app_reshape(int x, int y)
235 {
236         glViewport(0, 0, x, y);
237
238         glMatrixMode(GL_PROJECTION);
239         glLoadIdentity();
240         gluPerspective(50.0, (float)x / (float)y, 1.0, 10000.0);
241 }
242
243 void app_keyboard(int key, bool pressed)
244 {
245         unsigned int mod = app_get_modifiers();
246
247         if(pressed) {
248                 switch(key) {
249                 case 27:
250                         app_quit();
251                         break;
252
253                 case 'w':
254                         if(mod & MOD_CTRL) {
255                                 show_walk_mesh = !show_walk_mesh;
256                                 show_message("walk mesh: %s", show_walk_mesh ? "on" : "off");
257                         }
258                         break;
259
260                 case 'c':
261                         if(mod & MOD_CTRL) {
262                                 noclip = !noclip;
263                                 show_message(noclip ? "no clip" : "clip");
264                         }
265                         break;
266
267                 case '=':
268                         walk_speed *= 1.25;
269                         show_message("walk speed: %g", walk_speed);
270                         break;
271
272                 case '-':
273                         walk_speed *= 0.75;
274                         show_message("walk speed: %g", walk_speed);
275                         break;
276                 }
277         }
278
279         if(key < 256 && !(mod & (MOD_CTRL | MOD_ALT))) {
280                 keystate[key] = pressed;
281         }
282 }
283
284 void app_mouse_button(int bn, bool pressed, int x, int y)
285 {
286         prev_mx = x;
287         prev_my = y;
288         bnstate[bn] = pressed;
289 }
290
291 void app_mouse_motion(int x, int y)
292 {
293         int dx = x - prev_mx;
294         int dy = y - prev_my;
295         prev_mx = x;
296         prev_my = y;
297
298         if(!dx && !dy) return;
299
300         app_mouse_delta(dx, dy);
301 }
302
303 void app_mouse_delta(int dx, int dy)
304 {
305         if(bnstate[0]) {
306                 cam_theta += dx * 0.5;
307                 cam_phi += dy * 0.5;
308
309                 if(cam_phi < -90) cam_phi = -90;
310                 if(cam_phi > 90) cam_phi = 90;
311         }
312         if(bnstate[2]) {
313                 cam_dist += dy * 0.1;
314                 if(cam_dist < 0.0) cam_dist = 0.0;
315         }
316 }