erebus

annotate src/main.cc @ 32:b1fc96c71bcc

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