eqemu

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