eqemu

view src/main.cc @ 4:3d3656360a82

rendering properly, added picking, almost done...
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 17 Jul 2014 08:51:17 +0300
parents 48dce4ee4850
children 977bc1cb055b
line source
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <float.h>
4 #include <assert.h>
5 #include <errno.h>
6 #include <unistd.h>
7 #include <sys/select.h>
8 #include <GL/glew.h>
9 #include <X11/Xlib.h>
10 #include <GL/glx.h>
11 #include "dev.h"
12 #include "scene.h"
14 static bool init();
15 static void cleanup();
16 static void display();
17 static void keyb(int key, bool pressed);
18 static void mouse(int bn, bool pressed, int x, int y);
19 static void motion(int x, int y);
20 static Ray calc_pick_ray(int x, int y);
22 static Window create_window(const char *title, int xsz, int ysz);
23 static void process_events();
24 static int translate_keysym(KeySym sym);
26 static int proc_args(int argc, char **argv);
28 static Display *dpy;
29 static Window win;
30 static GLXContext ctx;
31 static Atom xa_wm_prot, xa_wm_del_win;
33 static int win_width, win_height;
35 static bool draw_pending;
36 static bool win_mapped;
38 static int fakefd = -1;
39 static char *fake_devpath;
41 static float cam_theta, cam_phi, cam_dist = 140;
42 static Scene *scn;
44 enum { BN_TICKET, BN_NEXT, NUM_BUTTONS };
45 const char *button_names[] = { "button1", "button2" };
46 static Object *button_obj[NUM_BUTTONS];
48 int main(int argc, char **argv)
49 {
50 if(proc_args(argc, argv) == -1) {
51 return 1;
52 }
53 if(!init()) {
54 return 1;
55 }
56 atexit(cleanup);
58 int xfd = ConnectionNumber(dpy);
60 // run once through pending events before going into the select loop
61 process_events();
63 for(;;) {
64 fd_set rd;
65 FD_ZERO(&rd);
67 FD_SET(xfd, &rd);
68 FD_SET(fakefd, &rd);
70 struct timeval noblock = {0, 0};
71 int maxfd = xfd > fakefd ? xfd : fakefd;
72 while(select(maxfd + 1, &rd, 0, 0, draw_pending ? &noblock : 0) == -1 && errno == EINTR);
74 if(FD_ISSET(xfd, &rd)) {
75 process_events();
76 }
77 if(FD_ISSET(fakefd, &rd)) {
78 proc_dev_input();
79 }
81 if(draw_pending) {
82 display();
83 draw_pending = false;
84 }
85 }
86 return 0;
87 }
89 static bool init()
90 {
91 if(fake_devpath) {
92 if((fakefd = start_dev(fake_devpath)) == -1) {
93 return false;
94 }
95 }
97 if(!(dpy = XOpenDisplay(0))) {
98 fprintf(stderr, "failed to connect to the X server!\n");
99 return false;
100 }
102 if(!(win = create_window("equeue device emulator", 800, 600))) {
103 return false;
104 }
106 glewInit();
108 scn = new Scene;
109 if(!scn->load("data/device.obj")) {
110 fprintf(stderr, "failed to load device 3D model\n");
111 return false;
112 }
114 for(int i=0; i<NUM_BUTTONS; i++) {
115 button_obj[i] = scn->get_object(button_names[i]);
116 if(!button_obj[i]) {
117 fprintf(stderr, "invalid 3D model\n");
118 return false;
119 }
120 BSphere &bs = button_obj[i]->get_mesh()->get_bounds();
121 bs.set_radius(bs.get_radius() * 1.5);
122 }
124 glEnable(GL_DEPTH_TEST);
125 glEnable(GL_CULL_FACE);
126 glEnable(GL_LIGHTING);
127 glEnable(GL_LIGHT0);
129 glClearColor(0.1, 0.1, 0.1, 1);
131 return true;
132 }
134 static void cleanup()
135 {
136 delete scn;
138 stop_dev();
140 if(!dpy) return;
142 if(win) {
143 XDestroyWindow(dpy, win);
144 }
145 XCloseDisplay(dpy);
146 }
148 static void display()
149 {
150 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
152 glMatrixMode(GL_MODELVIEW);
153 glLoadIdentity();
154 glTranslatef(0, 0, -cam_dist);
155 glRotatef(cam_phi, 1, 0, 0);
156 glRotatef(cam_theta, 0, 1, 0);
158 float lpos[] = {-7, 5, 10, 0};
159 glLightfv(GL_LIGHT0, GL_POSITION, lpos);
161 scn->render();
163 glXSwapBuffers(dpy, win);
164 assert(glGetError() == GL_NO_ERROR);
165 }
167 static void reshape(int x, int y)
168 {
169 glViewport(0, 0, x, y);
171 glMatrixMode(GL_PROJECTION);
172 glLoadIdentity();
173 gluPerspective(50.0, (float)x / (float)y, 1.0, 1000.0);
175 win_width = x;
176 win_height = y;
177 }
179 static void keyb(int key, bool pressed)
180 {
181 if(pressed) {
182 switch(key) {
183 case 27:
184 exit(0);
185 }
186 }
187 }
189 static bool bnstate[32];
190 static int prev_x, prev_y;
192 static void mouse(int bn, bool pressed, int x, int y)
193 {
194 bnstate[bn] = pressed;
195 prev_x = x;
196 prev_y = y;
198 if(bn == 0 && pressed) {
199 // do picking
200 Ray ray = calc_pick_ray(x, win_height - y);
202 HitPoint minhit;
203 minhit.t = FLT_MAX;
204 int hit_found = -1;
206 for(int i=0; i<NUM_BUTTONS; i++) {
207 HitPoint hit;
208 if(button_obj[i]->get_mesh()->get_bounds().intersect(ray, &hit) && hit.t < minhit.t) {
209 minhit = hit;
210 hit_found = i;
211 }
212 }
214 if(hit_found != -1) {
215 switch(hit_found) {
216 case BN_TICKET:
217 issue_ticket();
218 printf("issue ticket\n");
219 break;
221 case BN_NEXT:
222 next_customer();
223 printf("next customer\n");
224 break;
225 }
226 draw_pending = true;
227 }
228 }
229 }
231 static void motion(int x, int y)
232 {
233 int dx = x - prev_x;
234 int dy = y - prev_y;
235 prev_x = x;
236 prev_y = y;
238 if(bnstate[0]) {
239 cam_theta += dx * 0.5;
240 cam_phi += dy * 0.5;
241 if(cam_phi < -90) cam_phi = -90;
242 if(cam_phi > 90) cam_phi = 90;
243 draw_pending = true;
244 }
245 if(bnstate[2]) {
246 cam_dist += dy * 0.5;
247 if(cam_dist < 0.0) cam_dist = 0.0;
248 draw_pending = true;
249 }
250 }
252 static Ray calc_pick_ray(int x, int y)
253 {
254 double mv[16], proj[16];
255 int vp[4];
256 double resx, resy, resz;
257 Ray ray;
259 glGetDoublev(GL_MODELVIEW_MATRIX, mv);
260 glGetDoublev(GL_PROJECTION_MATRIX, proj);
261 glGetIntegerv(GL_VIEWPORT, vp);
263 gluUnProject(x, y, 0, mv, proj, vp, &resx, &resy, &resz);
264 ray.origin = Vector3(resx, resy, resz);
266 gluUnProject(x, y, 1, mv, proj, vp, &resx, &resy, &resz);
267 ray.dir = normalize(Vector3(resx, resy, resz) - ray.origin);
269 return ray;
270 }
272 static Window create_window(const char *title, int xsz, int ysz)
273 {
274 int scr = DefaultScreen(dpy);
275 Window root = RootWindow(dpy, scr);
277 int glxattr[] = {
278 GLX_RGBA,
279 GLX_RED_SIZE, 8,
280 GLX_GREEN_SIZE, 8,
281 GLX_BLUE_SIZE, 8,
282 GLX_DEPTH_SIZE, 24,
283 GLX_DOUBLEBUFFER,
284 #if defined(GLX_VERSION_1_4) || defined(GLX_ARB_multisample)
285 GLX_SAMPLE_BUFFERS_ARB, 1,
286 GLX_SAMPLES_ARB, 1,
287 #endif
288 None
289 };
291 XVisualInfo *vis = glXChooseVisual(dpy, scr, glxattr);
292 if(!vis) {
293 fprintf(stderr, "failed to find a suitable visual\n");
294 return 0;
295 }
297 if(!(ctx = glXCreateContext(dpy, vis, 0, True))) {
298 fprintf(stderr, "failed to create OpenGL context\n");
299 XFree(vis);
300 return -1;
301 }
303 XSetWindowAttributes xattr;
304 xattr.background_pixel = xattr.border_pixel = BlackPixel(dpy, scr);
305 xattr.colormap = XCreateColormap(dpy, root, vis->visual, AllocNone);
306 unsigned int xattr_mask = CWColormap | CWBackPixel | CWBorderPixel;
308 Window win = XCreateWindow(dpy, root, 0, 0, xsz, ysz, 0, vis->depth, InputOutput,
309 vis->visual, xattr_mask, &xattr);
310 if(!win) {
311 fprintf(stderr, "failed to create window\n");
312 glXDestroyContext(dpy, ctx);
313 XFree(vis);
314 return -1;
315 }
316 XFree(vis);
318 unsigned int evmask = StructureNotifyMask | VisibilityChangeMask | ExposureMask |
319 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask |
320 ButtonMotionMask | PointerMotionMask;
321 XSelectInput(dpy, win, evmask);
323 xa_wm_prot = XInternAtom(dpy, "WM_PROTOCOLS", False);
324 xa_wm_del_win = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
325 XSetWMProtocols(dpy, win, &xa_wm_del_win, 1);
327 XClassHint hint;
328 hint.res_name = hint.res_class = (char*)"equeue_win";
329 XSetClassHint(dpy, win, &hint);
331 XTextProperty wm_name;
332 XStringListToTextProperty((char**)&title, 1, &wm_name);
333 XSetWMName(dpy, win, &wm_name);
334 XSetWMIconName(dpy, win, &wm_name);
335 XFree(wm_name.value);
337 XMapWindow(dpy, win);
338 glXMakeCurrent(dpy, win, ctx);
340 return win;
341 }
343 static void process_events()
344 {
345 XEvent ev;
347 while(XPending(dpy)) {
348 XNextEvent(dpy, &ev);
349 switch(ev.type) {
350 case MapNotify:
351 win_mapped = true;
352 break;
354 case UnmapNotify:
355 win_mapped = false;
356 break;
358 case Expose:
359 if(win_mapped && ev.xexpose.count == 0) {
360 draw_pending = 1;
361 }
362 break;
364 case MotionNotify:
365 motion(ev.xmotion.x, ev.xmotion.y);
366 break;
368 case ButtonPress:
369 mouse(ev.xbutton.button - 1, true, ev.xbutton.x, ev.xbutton.y);
370 break;
372 case ButtonRelease:
373 mouse(ev.xbutton.button - 1, false, ev.xbutton.x, ev.xbutton.y);
374 break;
376 case KeyPress:
377 {
378 KeySym sym = XLookupKeysym(&ev.xkey, 0);
379 keyb(translate_keysym(sym), true);
380 }
381 break;
383 case KeyRelease:
384 {
385 KeySym sym = XLookupKeysym(&ev.xkey, 0);
386 keyb(translate_keysym(sym), false);
387 }
388 break;
390 case ConfigureNotify:
391 {
392 int xsz = ev.xconfigure.width;
393 int ysz = ev.xconfigure.height;
395 if(xsz != win_width || ysz != win_height) {
396 win_width = xsz;
397 win_height = ysz;
398 reshape(xsz, ysz);
399 }
400 }
401 break;
403 case ClientMessage:
404 if(ev.xclient.message_type == xa_wm_prot) {
405 if((Atom)ev.xclient.data.l[0] == xa_wm_del_win) {
406 exit(0);
407 }
408 }
409 break;
411 default:
412 break;
413 }
415 }
416 }
418 static int translate_keysym(KeySym sym)
419 {
420 switch(sym) {
421 case XK_BackSpace:
422 return '\b';
423 case XK_Tab:
424 return '\t';
425 case XK_Linefeed:
426 return '\r';
427 case XK_Return:
428 return '\n';
429 case XK_Escape:
430 return 27;
431 default:
432 break;
433 }
434 return (int)sym;
435 }
437 static int proc_args(int argc, char **argv)
438 {
439 for(int i=1; i<argc; i++) {
440 if(argv[i][0] == '-') {
441 fprintf(stderr, "unexpected option: %s\n", argv[i]);
442 return -1;
444 } else {
445 if(fake_devpath) {
446 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
447 return -1;
448 }
449 fake_devpath = argv[i];
450 }
451 }
452 if(!fake_devpath) {
453 fprintf(stderr, "no device path specified, running standalone\n");
454 }
455 return 0;
456 }