erebus

annotate src/main.cc @ 36:6eab83024d28

small markdown fix in readme
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 09 Jun 2014 07:28:03 +0300
parents b1fc96c71bcc
children db8a90307386
rev   line source
nuclear@2 1 #include <stdio.h>
nuclear@2 2 #include <stdlib.h>
nuclear@34 3 #include <string.h>
nuclear@2 4 #include <assert.h>
nuclear@29 5 #include <signal.h>
nuclear@19 6 #include <vector>
nuclear@26 7 #include <chrono>
nuclear@29 8 #include <imago2.h>
nuclear@32 9 #include <drawtext.h>
nuclear@2 10 #include "opengl.h"
nuclear@4 11 #include "erebus.h"
nuclear@2 12
nuclear@26 13 using namespace std::chrono;
nuclear@26 14
nuclear@2 15 static bool init();
nuclear@2 16 static void cleanup();
nuclear@32 17 static void begin_frame(long tm);
nuclear@32 18 static void end_frame();
nuclear@2 19 static void resize_rtarget(int xsz, int ysz);
nuclear@2 20 static void update_rect(int x, int y, int xsz, int ysz, float *pixels);
nuclear@4 21 static void idle();
nuclear@2 22 static void display();
nuclear@32 23 static void display_statusbar(const erb_render_status &status);
nuclear@29 24 static void save_image(const char *fname = 0);
nuclear@2 25 static void reshape(int x, int y);
nuclear@2 26 static void keyb(unsigned char key, int x, int y);
nuclear@9 27 static void keyb_up(unsigned char key, int x, int y);
nuclear@2 28 static void mouse(int bn, int st, int x, int y);
nuclear@9 29 static void motion(int x, int y);
nuclear@9 30 static void sball_button(int bn, int st);
nuclear@9 31 static void sball_motion(int x, int y, int z);
nuclear@2 32 static int next_pow2(int x);
nuclear@29 33 static void sighandler(int s);
nuclear@32 34 static bool parse_args(int argc, char **argv);
nuclear@2 35
nuclear@32 36 static int win_width, win_height, width, height, rtex_width, rtex_height;
nuclear@2 37 static unsigned int rtex;
nuclear@2 38
nuclear@32 39 static int opt_samples = -1;
nuclear@32 40 static int opt_iter = -1;
nuclear@32 41 static int opt_threads = -1;
nuclear@32 42 static float opt_imgscale = 2.0f;
nuclear@32 43
nuclear@4 44 static erebus *erb;
nuclear@4 45 static bool render_pending;
nuclear@32 46 static bool show_status = true;
nuclear@32 47 static steady_clock::time_point start_time;
nuclear@4 48
nuclear@19 49 static std::vector<char*> sfiles;
nuclear@4 50
nuclear@32 51 static dtx_font *font;
nuclear@32 52
nuclear@2 53 int main(int argc, char **argv)
nuclear@2 54 {
nuclear@2 55 glutInitWindowSize(1024, 600);
nuclear@2 56 glutInit(&argc, argv);
nuclear@19 57
nuclear@32 58 if(!parse_args(argc, argv)) {
nuclear@32 59 return 1;
nuclear@19 60 }
nuclear@19 61
nuclear@2 62 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
nuclear@2 63 glutCreateWindow("erebus OpenGL frontend");
nuclear@2 64
nuclear@2 65 glutDisplayFunc(display);
nuclear@2 66 glutReshapeFunc(reshape);
nuclear@2 67 glutKeyboardFunc(keyb);
nuclear@9 68 glutKeyboardUpFunc(keyb_up);
nuclear@2 69 glutMouseFunc(mouse);
nuclear@9 70 glutMotionFunc(motion);
nuclear@9 71 glutSpaceballButtonFunc(sball_button);
nuclear@9 72 glutSpaceballMotionFunc(sball_motion);
nuclear@2 73
nuclear@2 74 if(!init()) {
nuclear@2 75 return 1;
nuclear@2 76 }
nuclear@2 77 atexit(cleanup);
nuclear@29 78 signal(SIGINT, sighandler);
nuclear@29 79 signal(SIGSEGV, sighandler);
nuclear@29 80 signal(SIGILL, sighandler);
nuclear@29 81 signal(SIGTERM, sighandler);
nuclear@29 82 signal(SIGFPE, sighandler);
nuclear@2 83
nuclear@2 84 glutMainLoop();
nuclear@2 85 }
nuclear@2 86
nuclear@2 87 static bool init()
nuclear@2 88 {
nuclear@32 89 width = glutGet(GLUT_WINDOW_WIDTH) / opt_imgscale;
nuclear@32 90 height = glutGet(GLUT_WINDOW_HEIGHT) / opt_imgscale;
nuclear@32 91
nuclear@34 92 //if(!(font = dtx_open_font("/usr/share/fonts/opentype/linux-libertine/LinLibertine_R.otf", 22))) {
nuclear@32 93 if(!(font = dtx_open_font_glyphmap("data/serif.glyphmap"))) {
nuclear@32 94 fprintf(stderr, "warning: failed to load font!\n");
nuclear@32 95 }
nuclear@5 96
nuclear@4 97 if(!(erb = erb_init())) {
nuclear@4 98 return false;
nuclear@4 99 }
nuclear@4 100 erb_setopti(erb, ERB_OPT_WIDTH, width);
nuclear@4 101 erb_setopti(erb, ERB_OPT_HEIGHT, height);
nuclear@4 102
nuclear@32 103 if(opt_samples != -1) {
nuclear@32 104 erb_setopti(erb, ERB_OPT_MAX_SAMPLES, opt_samples);
nuclear@32 105 }
nuclear@32 106 if(opt_iter != -1) {
nuclear@32 107 erb_setopti(erb, ERB_OPT_MAX_ITER, opt_iter);
nuclear@32 108 }
nuclear@32 109 if(opt_threads != -1) {
nuclear@32 110 erb_setopti(erb, ERB_OPT_NUM_THREADS, opt_threads);
nuclear@32 111 }
nuclear@32 112
nuclear@19 113 for(size_t i=0; i<sfiles.size(); i++) {
nuclear@19 114 printf("loading scene file: %s\n", sfiles[i]);
nuclear@19 115 if(erb_load_scene(erb, sfiles[i]) == -1) {
nuclear@19 116 return false;
nuclear@19 117 }
nuclear@4 118 }
nuclear@4 119
nuclear@21 120 if(!sfiles.empty()) {
nuclear@32 121 begin_frame(0);
nuclear@21 122 }
nuclear@4 123
nuclear@8 124 glEnable(GL_TEXTURE_2D);
nuclear@2 125 return true;
nuclear@2 126 }
nuclear@2 127
nuclear@2 128 static void cleanup()
nuclear@2 129 {
nuclear@29 130 save_image("final.png");
nuclear@4 131 erb_destroy(erb);
nuclear@2 132 }
nuclear@2 133
nuclear@32 134 static void begin_frame(long tm)
nuclear@32 135 {
nuclear@32 136 printf("rendering frame (t=%ld) ... ", tm);
nuclear@32 137 fflush(stdout);
nuclear@32 138
nuclear@32 139 render_pending = true;
nuclear@32 140 glutIdleFunc(idle);
nuclear@32 141 erb_begin_frame(erb, 0);
nuclear@32 142
nuclear@32 143 start_time = steady_clock::now();
nuclear@32 144 }
nuclear@32 145
nuclear@32 146 static void end_frame()
nuclear@32 147 {
nuclear@32 148 if(!render_pending) return;
nuclear@32 149
nuclear@32 150 auto dur = steady_clock::now() - start_time;
nuclear@32 151 long full_msec = duration_cast<milliseconds>(dur).count();
nuclear@32 152 long msec, sec, min, hr, days;
nuclear@32 153
nuclear@32 154 msec = full_msec;
nuclear@32 155 printf("done in ");
nuclear@32 156 if((sec = msec / 1000) > 0) {
nuclear@32 157 msec %= 1000;
nuclear@32 158 if((min = sec / 60) > 0) {
nuclear@32 159 sec %= 60;
nuclear@32 160 if((hr = min / 60) > 0) {
nuclear@32 161 min %= 60;
nuclear@32 162 if((days = hr / 24) > 0) {
nuclear@32 163 hr %= 24;
nuclear@32 164 printf("%ld days ", days);
nuclear@32 165 }
nuclear@32 166 printf("%ld hours ", hr);
nuclear@32 167 }
nuclear@32 168 printf("%ld min ", min);
nuclear@32 169 }
nuclear@32 170 printf("%ld sec ", sec);
nuclear@32 171 }
nuclear@32 172 printf("%ld ms (%ld total msec)\n", msec, full_msec);
nuclear@32 173
nuclear@32 174 render_pending = false;
nuclear@32 175 glutIdleFunc(0);
nuclear@32 176 }
nuclear@32 177
nuclear@2 178 static void resize_rtarget(int xsz, int ysz)
nuclear@2 179 {
nuclear@2 180 static unsigned char *defpix;
nuclear@2 181
nuclear@32 182 win_width = xsz;
nuclear@32 183 win_height = ysz;
nuclear@32 184
nuclear@32 185 width = xsz / opt_imgscale;
nuclear@32 186 height = ysz / opt_imgscale;
nuclear@2 187
nuclear@8 188 if(width <= rtex_width && height <= rtex_height) {
nuclear@2 189 return;
nuclear@2 190 }
nuclear@8 191 rtex_width = next_pow2(width);
nuclear@8 192 rtex_height = next_pow2(height);
nuclear@2 193
nuclear@2 194 printf("resizing framebuffer texture: %dx%d\n", rtex_width, rtex_height);
nuclear@2 195
nuclear@2 196 if(!rtex) {
nuclear@2 197 glGenTextures(1, &rtex);
nuclear@2 198 }
nuclear@2 199
nuclear@2 200 delete [] defpix;
nuclear@2 201 defpix = new unsigned char[rtex_width * rtex_height * 4];
nuclear@2 202 unsigned char *ptr = defpix;
nuclear@2 203 for(int i=0; i<rtex_height; i++) {
nuclear@2 204 for(int j=0; j<rtex_width; j++) {
nuclear@2 205 bool chess = ((i >> 4) & 1) == ((j >> 4) & 1);
nuclear@2 206
nuclear@2 207 int val = chess ? 64 : 48;
nuclear@2 208
nuclear@2 209 *ptr++ = val;
nuclear@2 210 *ptr++ = val;
nuclear@2 211 *ptr++ = val;
nuclear@2 212 *ptr++ = 255;
nuclear@2 213 }
nuclear@2 214 }
nuclear@2 215
nuclear@2 216 glBindTexture(GL_TEXTURE_2D, rtex);
nuclear@2 217 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
nuclear@2 218 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
nuclear@2 219 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, rtex_width, rtex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, defpix);
nuclear@2 220 }
nuclear@2 221
nuclear@2 222 static void update_rect(int x, int y, int xsz, int ysz, float *pixels)
nuclear@2 223 {
nuclear@2 224 glBindTexture(GL_TEXTURE_2D, rtex);
nuclear@2 225 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, xsz, ysz, GL_RGBA, GL_FLOAT, pixels);
nuclear@2 226 }
nuclear@2 227
nuclear@4 228 static void idle()
nuclear@4 229 {
nuclear@4 230 glutPostRedisplay();
nuclear@4 231 }
nuclear@4 232
nuclear@2 233 static void display()
nuclear@2 234 {
nuclear@32 235 static struct erb_render_status status;
nuclear@32 236
nuclear@4 237 if(render_pending) {
nuclear@8 238 if(erb_render(erb, 64) == 0) {
nuclear@32 239 end_frame();
nuclear@4 240 }
nuclear@4 241 update_rect(0, 0, width, height, erb_get_framebuffer(erb));
nuclear@32 242 erb_get_status(erb, &status);
nuclear@4 243 }
nuclear@4 244
nuclear@2 245 float maxu = (float)width / (float)rtex_width;
nuclear@2 246 float maxv = (float)height / (float)rtex_height;
nuclear@2 247
nuclear@34 248 glEnable(GL_TEXTURE_2D);
nuclear@34 249 glBindTexture(GL_TEXTURE_2D, rtex);
nuclear@34 250
nuclear@2 251 glBegin(GL_QUADS);
nuclear@32 252 glColor4f(1, 1, 1, 1);
nuclear@2 253 glTexCoord2f(0, maxv); glVertex2f(-1, -1);
nuclear@2 254 glTexCoord2f(maxu, maxv); glVertex2f(1, -1);
nuclear@2 255 glTexCoord2f(maxu, 0); glVertex2f(1, 1);
nuclear@2 256 glTexCoord2f(0, 0); glVertex2f(-1, 1);
nuclear@2 257 glEnd();
nuclear@2 258
nuclear@32 259 // draw progress information etc...
nuclear@32 260 if(show_status) {
nuclear@32 261 display_statusbar(status);
nuclear@32 262 }
nuclear@32 263
nuclear@2 264 glutSwapBuffers();
nuclear@2 265 assert(glGetError() == GL_NO_ERROR);
nuclear@2 266 }
nuclear@2 267
nuclear@32 268 static void display_statusbar(const erb_render_status &status)
nuclear@32 269 {
nuclear@32 270 if(!font) return;
nuclear@32 271
nuclear@32 272 bool show_progress = opt_samples > 0;
nuclear@32 273
nuclear@32 274 glDisable(GL_TEXTURE_2D);
nuclear@32 275
nuclear@32 276 glMatrixMode(GL_PROJECTION);
nuclear@32 277 glPushMatrix();
nuclear@32 278 glLoadIdentity();
nuclear@32 279 glOrtho(0, win_width, 0, win_height, -1, 1);
nuclear@32 280
nuclear@32 281 glMatrixMode(GL_MODELVIEW);
nuclear@32 282 glPushMatrix();
nuclear@32 283 glLoadIdentity();
nuclear@32 284
nuclear@34 285 dtx_box bbox;
nuclear@34 286 dtx_glyph_box('Q', &bbox);
nuclear@34 287
nuclear@34 288 // draw progress/status bar
nuclear@34 289 int bar_height = bbox.height + 4;
nuclear@32 290 int prog_width = show_progress ? status.progress_percent * win_width / 100 : 0;
nuclear@32 291
nuclear@32 292 glBegin(GL_QUADS);
nuclear@32 293 glColor4f(0, 0, 0, 1);
nuclear@32 294 glVertex2f(prog_width, 0);
nuclear@32 295 glVertex2f(win_width, 0);
nuclear@34 296 glVertex2f(win_width, bar_height);
nuclear@34 297 glVertex2f(prog_width, bar_height);
nuclear@32 298
nuclear@32 299 glColor4f(0.25, 0, 0, 1);
nuclear@32 300 glVertex2f(0, 0);
nuclear@32 301 glVertex2f(prog_width, 0);
nuclear@34 302 glVertex2f(prog_width, bar_height);
nuclear@34 303 glVertex2f(0, bar_height);
nuclear@32 304 glEnd();
nuclear@32 305
nuclear@34 306 // draw the text
nuclear@34 307 glTranslatef(bbox.x + 2, bbox.y + 2, 0);
nuclear@32 308
nuclear@32 309 glColor4f(1, 1, 1, 1);
nuclear@32 310
nuclear@32 311 if(opt_samples > 0) {
nuclear@34 312 dtx_printf("samples: %ld / %ld", status.samples, status.max_samples);
nuclear@32 313
nuclear@34 314 glLoadIdentity();
nuclear@34 315 glTranslatef(win_width - dtx_string_width("progress: 100%") - 2, bbox.y + 2, 0);
nuclear@34 316 dtx_printf("progress: %ld%%", status.progress_percent);
nuclear@32 317 } else {
nuclear@34 318 dtx_printf("samples: %ld", status.samples);
nuclear@32 319 }
nuclear@34 320
nuclear@34 321 // samples/sec display
nuclear@34 322 static long paths_per_sec, prev_msec, prev_paths;
nuclear@34 323
nuclear@34 324 long msec = duration_cast<milliseconds>(steady_clock::now() - start_time).count();
nuclear@34 325 long dt = msec - prev_msec;
nuclear@34 326
nuclear@34 327 if(dt >= 1500) { // average over 1.5 seconds
nuclear@34 328 long paths = status.samples * width * height;
nuclear@34 329 if(prev_msec > 0 && prev_paths <= paths) { // check valid interval (not a restart or whatever)
nuclear@34 330 paths_per_sec = 1000 * (paths - prev_paths) / dt;
nuclear@34 331 }
nuclear@34 332 prev_msec = msec;
nuclear@34 333 prev_paths = paths;
nuclear@34 334 }
nuclear@34 335
nuclear@34 336 glLoadIdentity();
nuclear@34 337 glTranslatef((win_width - dtx_string_width("paths/s: 999999")) / 2, bbox.y + 2, 0);
nuclear@34 338 if(paths_per_sec) {
nuclear@34 339 dtx_printf("paths/s: %ld", paths_per_sec);
nuclear@34 340 } else {
nuclear@34 341 dtx_printf("paths/s: ???");
nuclear@34 342 }
nuclear@34 343
nuclear@32 344 glPopMatrix();
nuclear@32 345 glMatrixMode(GL_PROJECTION);
nuclear@32 346 glPopMatrix();
nuclear@32 347 }
nuclear@32 348
nuclear@29 349 static void save_image(const char *fname)
nuclear@29 350 {
nuclear@29 351 float *fb = erb_get_framebuffer(erb);
nuclear@29 352
nuclear@29 353 if(img_save_pixels(fname ? fname : "output.png", fb, width, height, IMG_FMT_RGBAF) == -1) {
nuclear@29 354 fprintf(stderr, "failed to save image\n");
nuclear@29 355 }
nuclear@29 356 }
nuclear@29 357
nuclear@2 358 static void reshape(int x, int y)
nuclear@2 359 {
nuclear@2 360 glViewport(0, 0, x, y);
nuclear@2 361 resize_rtarget(x, y);
nuclear@4 362
nuclear@4 363 erb_setopti(erb, ERB_OPT_WIDTH, width);
nuclear@4 364 erb_setopti(erb, ERB_OPT_HEIGHT, height);
nuclear@2 365 }
nuclear@2 366
nuclear@2 367 static void keyb(unsigned char key, int x, int y)
nuclear@2 368 {
nuclear@2 369 switch(key) {
nuclear@2 370 case 27:
nuclear@32 371 end_frame();
nuclear@2 372 exit(0);
nuclear@4 373
nuclear@4 374 case ' ':
nuclear@32 375 begin_frame(0);
nuclear@4 376 break;
nuclear@29 377
nuclear@34 378 case '\b':
nuclear@29 379 printf("saving image.\n");
nuclear@29 380 save_image();
nuclear@29 381 break;
nuclear@32 382
nuclear@34 383 case '`':
nuclear@32 384 show_status = !show_status;
nuclear@32 385 glutPostRedisplay();
nuclear@32 386 break;
nuclear@2 387 }
nuclear@9 388
nuclear@10 389 if(erb_input_keyboard(erb, key, true)) {
nuclear@9 390 glutPostRedisplay();
nuclear@9 391 }
nuclear@9 392 }
nuclear@9 393
nuclear@9 394 static void keyb_up(unsigned char key, int x, int y)
nuclear@9 395 {
nuclear@10 396 if(erb_input_keyboard(erb, key, false)) {
nuclear@9 397 glutPostRedisplay();
nuclear@9 398 }
nuclear@2 399 }
nuclear@2 400
nuclear@2 401 static void mouse(int bn, int st, int x, int y)
nuclear@2 402 {
nuclear@10 403 if(erb_input_mouse_button(erb, bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y)) {
nuclear@9 404 glutPostRedisplay();
nuclear@9 405 }
nuclear@9 406 }
nuclear@9 407
nuclear@9 408 static void motion(int x, int y)
nuclear@9 409 {
nuclear@15 410 if(erb_input_mouse_motion(erb, x, y)) {
nuclear@9 411 glutPostRedisplay();
nuclear@9 412 }
nuclear@9 413 }
nuclear@9 414
nuclear@9 415 static void sball_button(int bn, int state)
nuclear@9 416 {
nuclear@10 417 if(erb_input_6dof_button(erb, bn, state == GLUT_DOWN)) {
nuclear@9 418 glutPostRedisplay();
nuclear@9 419 }
nuclear@9 420 }
nuclear@9 421
nuclear@9 422 static void sball_motion(int x, int y, int z)
nuclear@9 423 {
nuclear@10 424 if(erb_input_6dof_motion(erb, x / 65536.0, y / 65536.0, z / 65536.0)) {
nuclear@9 425 glutPostRedisplay();
nuclear@9 426 }
nuclear@2 427 }
nuclear@2 428
nuclear@2 429 static int next_pow2(int x)
nuclear@2 430 {
nuclear@2 431 int res = 2;
nuclear@2 432 while(res < x) {
nuclear@2 433 res <<= 1;
nuclear@2 434 }
nuclear@2 435 return res;
nuclear@2 436 }
nuclear@29 437
nuclear@29 438 static void sighandler(int s)
nuclear@29 439 {
nuclear@29 440 exit(0);
nuclear@29 441 }
nuclear@32 442
nuclear@32 443 static bool parse_args(int argc, char **argv)
nuclear@32 444 {
nuclear@32 445 for(int i=1; i<argc; i++) {
nuclear@32 446 if(argv[i][0] == '-') {
nuclear@32 447 if(strcmp(argv[i], "-samples") == 0) {
nuclear@32 448 opt_samples = atoi(argv[++i]);
nuclear@32 449 if(opt_samples <= 0) {
nuclear@32 450 fprintf(stderr, "invalid -samples option: %s\n", argv[i]);
nuclear@32 451 return false;
nuclear@32 452 }
nuclear@32 453
nuclear@32 454 } else if(strcmp(argv[i], "-iter") == 0) {
nuclear@32 455 opt_iter = atoi(argv[++i]);
nuclear@32 456 if(opt_iter <= 0) {
nuclear@32 457 fprintf(stderr, "invalid -iter option: %s\n", argv[i]);
nuclear@32 458 return false;
nuclear@32 459 }
nuclear@32 460
nuclear@32 461 } else if(strcmp(argv[i], "-threads") == 0) {
nuclear@32 462 opt_threads = atoi(argv[++i]);
nuclear@32 463 if(opt_threads <= 0) {
nuclear@32 464 fprintf(stderr, "invalid -threads option: %s\n", argv[i]);
nuclear@32 465 return false;
nuclear@32 466 }
nuclear@32 467
nuclear@32 468 } else if(strcmp(argv[i], "-scale") == 0) {
nuclear@32 469 opt_imgscale = atof(argv[++i]);
nuclear@32 470 if(opt_imgscale <= 0.0f) {
nuclear@32 471 fprintf(stderr, "invalid -scale option: %s\n", argv[i]);
nuclear@32 472 return false;
nuclear@32 473 }
nuclear@32 474
nuclear@32 475 } else {
nuclear@32 476 fprintf(stderr, "invalid option: %s\n", argv[i]);
nuclear@32 477 return false;
nuclear@32 478 }
nuclear@32 479 } else {
nuclear@32 480 sfiles.push_back(argv[i]);
nuclear@32 481 }
nuclear@32 482 }
nuclear@32 483
nuclear@32 484 return true;
nuclear@34 485 }