erebus

annotate src/main.cc @ 48:9971a08f4104

merged
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 24 Feb 2016 00:29:31 +0200
parents 9d6368850fe1
children
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@37 12 #include "console.h"
nuclear@2 13
nuclear@26 14 using namespace std::chrono;
nuclear@26 15
nuclear@2 16 static bool init();
nuclear@2 17 static void cleanup();
nuclear@32 18 static void begin_frame(long tm);
nuclear@32 19 static void end_frame();
nuclear@2 20 static void resize_rtarget(int xsz, int ysz);
nuclear@2 21 static void update_rect(int x, int y, int xsz, int ysz, float *pixels);
nuclear@4 22 static void idle();
nuclear@2 23 static void display();
nuclear@32 24 static void display_statusbar(const erb_render_status &status);
nuclear@29 25 static void save_image(const char *fname = 0);
nuclear@2 26 static void reshape(int x, int y);
nuclear@2 27 static void keyb(unsigned char key, int x, int y);
nuclear@9 28 static void keyb_up(unsigned char key, int x, int y);
nuclear@37 29 static void skeyb(int key, int x, int y);
nuclear@2 30 static void mouse(int bn, int st, int x, int y);
nuclear@9 31 static void motion(int x, int y);
nuclear@9 32 static void sball_button(int bn, int st);
nuclear@9 33 static void sball_motion(int x, int y, int z);
nuclear@2 34 static int next_pow2(int x);
nuclear@29 35 static void sighandler(int s);
nuclear@32 36 static bool parse_args(int argc, char **argv);
nuclear@37 37 static void con_parse(const char *line);
nuclear@2 38
nuclear@32 39 static int win_width, win_height, width, height, rtex_width, rtex_height;
nuclear@2 40 static unsigned int rtex;
nuclear@2 41
nuclear@32 42 static int opt_samples = -1;
nuclear@32 43 static int opt_iter = -1;
nuclear@32 44 static int opt_threads = -1;
nuclear@32 45 static float opt_imgscale = 2.0f;
nuclear@32 46
nuclear@4 47 static erebus *erb;
nuclear@4 48 static bool render_pending;
nuclear@32 49 static bool show_status = true;
nuclear@32 50 static steady_clock::time_point start_time;
nuclear@4 51
nuclear@19 52 static std::vector<char*> sfiles;
nuclear@4 53
nuclear@37 54 #define FONTSZ 22
nuclear@32 55 static dtx_font *font;
nuclear@37 56 static Console con;
nuclear@32 57
nuclear@2 58 int main(int argc, char **argv)
nuclear@2 59 {
nuclear@2 60 glutInitWindowSize(1024, 600);
nuclear@2 61 glutInit(&argc, argv);
nuclear@19 62
nuclear@32 63 if(!parse_args(argc, argv)) {
nuclear@32 64 return 1;
nuclear@19 65 }
nuclear@19 66
nuclear@2 67 glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
nuclear@2 68 glutCreateWindow("erebus OpenGL frontend");
nuclear@2 69
nuclear@2 70 glutDisplayFunc(display);
nuclear@2 71 glutReshapeFunc(reshape);
nuclear@2 72 glutKeyboardFunc(keyb);
nuclear@9 73 glutKeyboardUpFunc(keyb_up);
nuclear@37 74 glutSpecialFunc(skeyb);
nuclear@2 75 glutMouseFunc(mouse);
nuclear@9 76 glutMotionFunc(motion);
nuclear@9 77 glutSpaceballButtonFunc(sball_button);
nuclear@9 78 glutSpaceballMotionFunc(sball_motion);
nuclear@2 79
nuclear@2 80 if(!init()) {
nuclear@2 81 return 1;
nuclear@2 82 }
nuclear@2 83 atexit(cleanup);
nuclear@29 84 signal(SIGINT, sighandler);
nuclear@29 85 signal(SIGSEGV, sighandler);
nuclear@29 86 signal(SIGILL, sighandler);
nuclear@29 87 signal(SIGTERM, sighandler);
nuclear@29 88 signal(SIGFPE, sighandler);
nuclear@2 89
nuclear@2 90 glutMainLoop();
nuclear@2 91 }
nuclear@2 92
nuclear@2 93 static bool init()
nuclear@2 94 {
nuclear@32 95 width = glutGet(GLUT_WINDOW_WIDTH) / opt_imgscale;
nuclear@32 96 height = glutGet(GLUT_WINDOW_HEIGHT) / opt_imgscale;
nuclear@32 97
nuclear@37 98 //if(!(font = dtx_open_font("/usr/share/fonts/opentype/linux-libertine/LinLibertine_R.otf", FONTSZ))) {
nuclear@32 99 if(!(font = dtx_open_font_glyphmap("data/serif.glyphmap"))) {
nuclear@32 100 fprintf(stderr, "warning: failed to load font!\n");
nuclear@32 101 }
nuclear@5 102
nuclear@37 103 //dtx_font *confont = dtx_open_font("/usr/share/fonts/truetype/droid/DroidSansMono.ttf", 14);
nuclear@37 104 dtx_font *confont = dtx_open_font_glyphmap("data/mono.glyphmap");
nuclear@37 105 if(confont) {
nuclear@37 106 con.set_font(confont, 14);
nuclear@37 107 } else {
nuclear@37 108 con.set_font(font, FONTSZ);
nuclear@37 109 }
nuclear@37 110 con.set_command_func(con_parse);
nuclear@37 111
nuclear@4 112 if(!(erb = erb_init())) {
nuclear@4 113 return false;
nuclear@4 114 }
nuclear@4 115 erb_setopti(erb, ERB_OPT_WIDTH, width);
nuclear@4 116 erb_setopti(erb, ERB_OPT_HEIGHT, height);
nuclear@4 117
nuclear@32 118 if(opt_samples != -1) {
nuclear@32 119 erb_setopti(erb, ERB_OPT_MAX_SAMPLES, opt_samples);
nuclear@32 120 }
nuclear@32 121 if(opt_iter != -1) {
nuclear@32 122 erb_setopti(erb, ERB_OPT_MAX_ITER, opt_iter);
nuclear@32 123 }
nuclear@32 124 if(opt_threads != -1) {
nuclear@32 125 erb_setopti(erb, ERB_OPT_NUM_THREADS, opt_threads);
nuclear@32 126 }
nuclear@32 127
nuclear@19 128 for(size_t i=0; i<sfiles.size(); i++) {
nuclear@19 129 printf("loading scene file: %s\n", sfiles[i]);
nuclear@19 130 if(erb_load_scene(erb, sfiles[i]) == -1) {
nuclear@19 131 return false;
nuclear@19 132 }
nuclear@4 133 }
nuclear@4 134
nuclear@21 135 if(!sfiles.empty()) {
nuclear@32 136 begin_frame(0);
nuclear@21 137 }
nuclear@4 138
nuclear@8 139 glEnable(GL_TEXTURE_2D);
nuclear@2 140 return true;
nuclear@2 141 }
nuclear@2 142
nuclear@2 143 static void cleanup()
nuclear@2 144 {
nuclear@29 145 save_image("final.png");
nuclear@4 146 erb_destroy(erb);
nuclear@2 147 }
nuclear@2 148
nuclear@32 149 static void begin_frame(long tm)
nuclear@32 150 {
nuclear@41 151 printf("rendering frame (t=%ld) ...\n", tm);
nuclear@32 152
nuclear@32 153 render_pending = true;
nuclear@32 154 glutIdleFunc(idle);
nuclear@32 155 erb_begin_frame(erb, 0);
nuclear@32 156
nuclear@32 157 start_time = steady_clock::now();
nuclear@32 158 }
nuclear@32 159
nuclear@32 160 static void end_frame()
nuclear@32 161 {
nuclear@32 162 if(!render_pending) return;
nuclear@32 163
nuclear@32 164 auto dur = steady_clock::now() - start_time;
nuclear@32 165 long full_msec = duration_cast<milliseconds>(dur).count();
nuclear@32 166 long msec, sec, min, hr, days;
nuclear@32 167
nuclear@32 168 msec = full_msec;
nuclear@41 169 con.printf("done in ");
nuclear@32 170 if((sec = msec / 1000) > 0) {
nuclear@32 171 msec %= 1000;
nuclear@32 172 if((min = sec / 60) > 0) {
nuclear@32 173 sec %= 60;
nuclear@32 174 if((hr = min / 60) > 0) {
nuclear@32 175 min %= 60;
nuclear@32 176 if((days = hr / 24) > 0) {
nuclear@32 177 hr %= 24;
nuclear@41 178 con.printf("%ld days ", days);
nuclear@32 179 }
nuclear@41 180 con.printf("%ld hours ", hr);
nuclear@32 181 }
nuclear@41 182 con.printf("%ld min ", min);
nuclear@32 183 }
nuclear@41 184 con.printf("%ld sec ", sec);
nuclear@32 185 }
nuclear@41 186 con.printf("%ld ms (%ld total msec)\n", msec, full_msec);
nuclear@32 187
nuclear@32 188 render_pending = false;
nuclear@32 189 glutIdleFunc(0);
nuclear@32 190 }
nuclear@32 191
nuclear@2 192 static void resize_rtarget(int xsz, int ysz)
nuclear@2 193 {
nuclear@2 194 static unsigned char *defpix;
nuclear@2 195
nuclear@41 196 if(render_pending) {
nuclear@41 197 erb_end_frame(erb);
nuclear@41 198 }
nuclear@41 199
nuclear@32 200 win_width = xsz;
nuclear@32 201 win_height = ysz;
nuclear@32 202
nuclear@32 203 width = xsz / opt_imgscale;
nuclear@32 204 height = ysz / opt_imgscale;
nuclear@2 205
nuclear@41 206 if(width > rtex_width || height > rtex_height) {
nuclear@41 207 rtex_width = next_pow2(width);
nuclear@41 208 rtex_height = next_pow2(height);
nuclear@2 209
nuclear@41 210 printf("resizing framebuffer texture: %dx%d\n", rtex_width, rtex_height);
nuclear@2 211
nuclear@41 212 if(!rtex) {
nuclear@41 213 glGenTextures(1, &rtex);
nuclear@41 214 }
nuclear@41 215
nuclear@41 216 delete [] defpix;
nuclear@41 217 defpix = new unsigned char[rtex_width * rtex_height * 4];
nuclear@41 218 unsigned char *ptr = defpix;
nuclear@41 219 for(int i=0; i<rtex_height; i++) {
nuclear@41 220 for(int j=0; j<rtex_width; j++) {
nuclear@41 221 bool chess = ((i >> 4) & 1) == ((j >> 4) & 1);
nuclear@41 222
nuclear@41 223 int val = chess ? 64 : 48;
nuclear@41 224
nuclear@41 225 *ptr++ = val;
nuclear@41 226 *ptr++ = val;
nuclear@41 227 *ptr++ = val;
nuclear@41 228 *ptr++ = 255;
nuclear@41 229 }
nuclear@41 230 }
nuclear@41 231
nuclear@41 232 glBindTexture(GL_TEXTURE_2D, rtex);
nuclear@41 233 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
nuclear@41 234 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
nuclear@41 235 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F_ARB, rtex_width, rtex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, defpix);
nuclear@2 236 }
nuclear@2 237
nuclear@41 238 if(render_pending) {
nuclear@41 239 begin_frame(0);
nuclear@2 240 }
nuclear@2 241 }
nuclear@2 242
nuclear@2 243 static void update_rect(int x, int y, int xsz, int ysz, float *pixels)
nuclear@2 244 {
nuclear@2 245 glBindTexture(GL_TEXTURE_2D, rtex);
nuclear@2 246 glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, xsz, ysz, GL_RGBA, GL_FLOAT, pixels);
nuclear@2 247 }
nuclear@2 248
nuclear@4 249 static void idle()
nuclear@4 250 {
nuclear@4 251 glutPostRedisplay();
nuclear@4 252 }
nuclear@4 253
nuclear@2 254 static void display()
nuclear@2 255 {
nuclear@32 256 static struct erb_render_status status;
nuclear@32 257
nuclear@4 258 if(render_pending) {
nuclear@8 259 if(erb_render(erb, 64) == 0) {
nuclear@32 260 end_frame();
nuclear@4 261 }
nuclear@4 262 update_rect(0, 0, width, height, erb_get_framebuffer(erb));
nuclear@32 263 erb_get_status(erb, &status);
nuclear@4 264 }
nuclear@4 265
nuclear@2 266 float maxu = (float)width / (float)rtex_width;
nuclear@2 267 float maxv = (float)height / (float)rtex_height;
nuclear@2 268
nuclear@34 269 glEnable(GL_TEXTURE_2D);
nuclear@34 270 glBindTexture(GL_TEXTURE_2D, rtex);
nuclear@34 271
nuclear@2 272 glBegin(GL_QUADS);
nuclear@32 273 glColor4f(1, 1, 1, 1);
nuclear@2 274 glTexCoord2f(0, maxv); glVertex2f(-1, -1);
nuclear@2 275 glTexCoord2f(maxu, maxv); glVertex2f(1, -1);
nuclear@2 276 glTexCoord2f(maxu, 0); glVertex2f(1, 1);
nuclear@2 277 glTexCoord2f(0, 0); glVertex2f(-1, 1);
nuclear@2 278 glEnd();
nuclear@2 279
nuclear@37 280 // draw the console
nuclear@37 281 con.update();
nuclear@37 282 con.draw();
nuclear@37 283
nuclear@32 284 // draw progress information etc...
nuclear@32 285 if(show_status) {
nuclear@32 286 display_statusbar(status);
nuclear@32 287 }
nuclear@32 288
nuclear@2 289 glutSwapBuffers();
nuclear@2 290 assert(glGetError() == GL_NO_ERROR);
nuclear@2 291 }
nuclear@2 292
nuclear@32 293 static void display_statusbar(const erb_render_status &status)
nuclear@32 294 {
nuclear@32 295 if(!font) return;
nuclear@37 296 dtx_use_font(font, FONTSZ);
nuclear@32 297
nuclear@32 298 bool show_progress = opt_samples > 0;
nuclear@32 299
nuclear@32 300 glDisable(GL_TEXTURE_2D);
nuclear@32 301
nuclear@32 302 glMatrixMode(GL_PROJECTION);
nuclear@32 303 glPushMatrix();
nuclear@32 304 glLoadIdentity();
nuclear@32 305 glOrtho(0, win_width, 0, win_height, -1, 1);
nuclear@32 306
nuclear@32 307 glMatrixMode(GL_MODELVIEW);
nuclear@32 308 glPushMatrix();
nuclear@32 309 glLoadIdentity();
nuclear@32 310
nuclear@34 311 dtx_box bbox;
nuclear@34 312 dtx_glyph_box('Q', &bbox);
nuclear@34 313
nuclear@34 314 // draw progress/status bar
nuclear@34 315 int bar_height = bbox.height + 4;
nuclear@32 316 int prog_width = show_progress ? status.progress_percent * win_width / 100 : 0;
nuclear@32 317
nuclear@32 318 glBegin(GL_QUADS);
nuclear@32 319 glColor4f(0, 0, 0, 1);
nuclear@32 320 glVertex2f(prog_width, 0);
nuclear@32 321 glVertex2f(win_width, 0);
nuclear@34 322 glVertex2f(win_width, bar_height);
nuclear@34 323 glVertex2f(prog_width, bar_height);
nuclear@32 324
nuclear@32 325 glColor4f(0.25, 0, 0, 1);
nuclear@32 326 glVertex2f(0, 0);
nuclear@32 327 glVertex2f(prog_width, 0);
nuclear@34 328 glVertex2f(prog_width, bar_height);
nuclear@34 329 glVertex2f(0, bar_height);
nuclear@32 330 glEnd();
nuclear@32 331
nuclear@34 332 // draw the text
nuclear@34 333 glTranslatef(bbox.x + 2, bbox.y + 2, 0);
nuclear@32 334
nuclear@32 335 glColor4f(1, 1, 1, 1);
nuclear@32 336
nuclear@32 337 if(opt_samples > 0) {
nuclear@34 338 dtx_printf("samples: %ld / %ld", status.samples, status.max_samples);
nuclear@32 339
nuclear@34 340 glLoadIdentity();
nuclear@34 341 glTranslatef(win_width - dtx_string_width("progress: 100%") - 2, bbox.y + 2, 0);
nuclear@34 342 dtx_printf("progress: %ld%%", status.progress_percent);
nuclear@32 343 } else {
nuclear@34 344 dtx_printf("samples: %ld", status.samples);
nuclear@32 345 }
nuclear@34 346
nuclear@34 347 // samples/sec display
nuclear@34 348 static long paths_per_sec, prev_msec, prev_paths;
nuclear@34 349
nuclear@34 350 long msec = duration_cast<milliseconds>(steady_clock::now() - start_time).count();
nuclear@34 351 long dt = msec - prev_msec;
nuclear@34 352
nuclear@34 353 if(dt >= 1500) { // average over 1.5 seconds
nuclear@34 354 long paths = status.samples * width * height;
nuclear@34 355 if(prev_msec > 0 && prev_paths <= paths) { // check valid interval (not a restart or whatever)
nuclear@34 356 paths_per_sec = 1000 * (paths - prev_paths) / dt;
nuclear@34 357 }
nuclear@34 358 prev_msec = msec;
nuclear@34 359 prev_paths = paths;
nuclear@34 360 }
nuclear@34 361
nuclear@34 362 glLoadIdentity();
nuclear@34 363 glTranslatef((win_width - dtx_string_width("paths/s: 999999")) / 2, bbox.y + 2, 0);
nuclear@34 364 if(paths_per_sec) {
nuclear@34 365 dtx_printf("paths/s: %ld", paths_per_sec);
nuclear@34 366 } else {
nuclear@34 367 dtx_printf("paths/s: ???");
nuclear@34 368 }
nuclear@34 369
nuclear@32 370 glPopMatrix();
nuclear@32 371 glMatrixMode(GL_PROJECTION);
nuclear@32 372 glPopMatrix();
nuclear@32 373 }
nuclear@32 374
nuclear@29 375 static void save_image(const char *fname)
nuclear@29 376 {
nuclear@29 377 float *fb = erb_get_framebuffer(erb);
nuclear@29 378
nuclear@29 379 if(img_save_pixels(fname ? fname : "output.png", fb, width, height, IMG_FMT_RGBAF) == -1) {
nuclear@29 380 fprintf(stderr, "failed to save image\n");
nuclear@29 381 }
nuclear@29 382 }
nuclear@29 383
nuclear@2 384 static void reshape(int x, int y)
nuclear@2 385 {
nuclear@2 386 glViewport(0, 0, x, y);
nuclear@2 387 resize_rtarget(x, y);
nuclear@4 388
nuclear@4 389 erb_setopti(erb, ERB_OPT_WIDTH, width);
nuclear@4 390 erb_setopti(erb, ERB_OPT_HEIGHT, height);
nuclear@2 391 }
nuclear@2 392
nuclear@2 393 static void keyb(unsigned char key, int x, int y)
nuclear@2 394 {
nuclear@2 395 switch(key) {
nuclear@2 396 case 27:
nuclear@37 397 if(con.is_visible()) {
nuclear@37 398 con.hide();
nuclear@37 399 glutPostRedisplay();
nuclear@37 400 } else {
nuclear@37 401 end_frame();
nuclear@37 402 exit(0);
nuclear@37 403 }
nuclear@37 404 break;
nuclear@4 405
nuclear@4 406 case ' ':
nuclear@37 407 if(!con.is_visible()) {
nuclear@37 408 begin_frame(0);
nuclear@37 409 } else {
nuclear@37 410 con.input_key(' ');
nuclear@37 411 glutPostRedisplay();
nuclear@37 412 }
nuclear@29 413 break;
nuclear@32 414
nuclear@34 415 case '`':
nuclear@37 416 con.set_visible(!con.is_visible());
nuclear@37 417 glutPostRedisplay();
nuclear@37 418 break;
nuclear@37 419
nuclear@37 420 case '~':
nuclear@32 421 show_status = !show_status;
nuclear@32 422 glutPostRedisplay();
nuclear@32 423 break;
nuclear@37 424
nuclear@37 425 default:
nuclear@37 426 // otherwise if the console is visible, let them through
nuclear@37 427 if(con.is_visible()) {
nuclear@37 428 con.input_key(key);
nuclear@37 429 glutPostRedisplay();
nuclear@37 430 return; // don't pass anything to the erb input handler
nuclear@37 431 }
nuclear@2 432 }
nuclear@9 433
nuclear@10 434 if(erb_input_keyboard(erb, key, true)) {
nuclear@9 435 glutPostRedisplay();
nuclear@9 436 }
nuclear@9 437 }
nuclear@9 438
nuclear@9 439 static void keyb_up(unsigned char key, int x, int y)
nuclear@9 440 {
nuclear@10 441 if(erb_input_keyboard(erb, key, false)) {
nuclear@9 442 glutPostRedisplay();
nuclear@9 443 }
nuclear@2 444 }
nuclear@2 445
nuclear@37 446 static void skeyb(int key, int x, int y)
nuclear@37 447 {
nuclear@37 448 if(key == GLUT_KEY_F12) {
nuclear@37 449 printf("saving image...\n");
nuclear@37 450 save_image();
nuclear@37 451 return;
nuclear@37 452 }
nuclear@37 453
nuclear@37 454 if(con.is_visible()) {
nuclear@37 455 switch(key) {
nuclear@37 456 case GLUT_KEY_F8:
nuclear@37 457 con.debug();
nuclear@37 458 return;
nuclear@37 459
nuclear@37 460 case GLUT_KEY_LEFT:
nuclear@37 461 con.input_key(Console::KEY_LEFT);
nuclear@37 462 break;
nuclear@37 463 case GLUT_KEY_RIGHT:
nuclear@37 464 con.input_key(Console::KEY_RIGHT);
nuclear@37 465 break;
nuclear@37 466 case GLUT_KEY_UP:
nuclear@37 467 con.input_key(Console::KEY_UP);
nuclear@37 468 break;
nuclear@37 469 case GLUT_KEY_DOWN:
nuclear@37 470 con.input_key(Console::KEY_DOWN);
nuclear@37 471 break;
nuclear@37 472 case GLUT_KEY_HOME:
nuclear@37 473 con.input_key(Console::KEY_HOME);
nuclear@37 474 break;
nuclear@37 475 case GLUT_KEY_END:
nuclear@37 476 con.input_key(Console::KEY_END);
nuclear@37 477 break;
nuclear@37 478 case GLUT_KEY_INSERT:
nuclear@37 479 con.input_key(Console::KEY_INS);
nuclear@37 480 break;
nuclear@37 481 case GLUT_KEY_PAGE_UP:
nuclear@37 482 con.input_key(Console::KEY_PGUP);
nuclear@37 483 break;
nuclear@37 484 case GLUT_KEY_PAGE_DOWN:
nuclear@37 485 con.input_key(Console::KEY_PGDOWN);
nuclear@37 486 break;
nuclear@37 487
nuclear@37 488 default:
nuclear@37 489 return;
nuclear@37 490 }
nuclear@37 491 glutPostRedisplay();
nuclear@37 492 }
nuclear@37 493 }
nuclear@37 494
nuclear@2 495 static void mouse(int bn, int st, int x, int y)
nuclear@2 496 {
nuclear@10 497 if(erb_input_mouse_button(erb, bn - GLUT_LEFT_BUTTON, st == GLUT_DOWN, x, y)) {
nuclear@9 498 glutPostRedisplay();
nuclear@9 499 }
nuclear@9 500 }
nuclear@9 501
nuclear@9 502 static void motion(int x, int y)
nuclear@9 503 {
nuclear@15 504 if(erb_input_mouse_motion(erb, x, y)) {
nuclear@9 505 glutPostRedisplay();
nuclear@9 506 }
nuclear@9 507 }
nuclear@9 508
nuclear@9 509 static void sball_button(int bn, int state)
nuclear@9 510 {
nuclear@10 511 if(erb_input_6dof_button(erb, bn, state == GLUT_DOWN)) {
nuclear@9 512 glutPostRedisplay();
nuclear@9 513 }
nuclear@9 514 }
nuclear@9 515
nuclear@9 516 static void sball_motion(int x, int y, int z)
nuclear@9 517 {
nuclear@10 518 if(erb_input_6dof_motion(erb, x / 65536.0, y / 65536.0, z / 65536.0)) {
nuclear@9 519 glutPostRedisplay();
nuclear@9 520 }
nuclear@2 521 }
nuclear@2 522
nuclear@2 523 static int next_pow2(int x)
nuclear@2 524 {
nuclear@2 525 int res = 2;
nuclear@2 526 while(res < x) {
nuclear@2 527 res <<= 1;
nuclear@2 528 }
nuclear@2 529 return res;
nuclear@2 530 }
nuclear@29 531
nuclear@29 532 static void sighandler(int s)
nuclear@29 533 {
nuclear@29 534 exit(0);
nuclear@29 535 }
nuclear@32 536
nuclear@32 537 static bool parse_args(int argc, char **argv)
nuclear@32 538 {
nuclear@32 539 for(int i=1; i<argc; i++) {
nuclear@32 540 if(argv[i][0] == '-') {
nuclear@32 541 if(strcmp(argv[i], "-samples") == 0) {
nuclear@32 542 opt_samples = atoi(argv[++i]);
nuclear@32 543 if(opt_samples <= 0) {
nuclear@32 544 fprintf(stderr, "invalid -samples option: %s\n", argv[i]);
nuclear@32 545 return false;
nuclear@32 546 }
nuclear@32 547
nuclear@32 548 } else if(strcmp(argv[i], "-iter") == 0) {
nuclear@32 549 opt_iter = atoi(argv[++i]);
nuclear@32 550 if(opt_iter <= 0) {
nuclear@32 551 fprintf(stderr, "invalid -iter option: %s\n", argv[i]);
nuclear@32 552 return false;
nuclear@32 553 }
nuclear@32 554
nuclear@32 555 } else if(strcmp(argv[i], "-threads") == 0) {
nuclear@32 556 opt_threads = atoi(argv[++i]);
nuclear@32 557 if(opt_threads <= 0) {
nuclear@32 558 fprintf(stderr, "invalid -threads option: %s\n", argv[i]);
nuclear@32 559 return false;
nuclear@32 560 }
nuclear@32 561
nuclear@32 562 } else if(strcmp(argv[i], "-scale") == 0) {
nuclear@32 563 opt_imgscale = atof(argv[++i]);
nuclear@32 564 if(opt_imgscale <= 0.0f) {
nuclear@32 565 fprintf(stderr, "invalid -scale option: %s\n", argv[i]);
nuclear@32 566 return false;
nuclear@32 567 }
nuclear@32 568
nuclear@32 569 } else {
nuclear@32 570 fprintf(stderr, "invalid option: %s\n", argv[i]);
nuclear@32 571 return false;
nuclear@32 572 }
nuclear@32 573 } else {
nuclear@32 574 sfiles.push_back(argv[i]);
nuclear@32 575 }
nuclear@32 576 }
nuclear@32 577
nuclear@32 578 return true;
nuclear@34 579 }
nuclear@37 580
nuclear@37 581 static void con_parse(const char *line)
nuclear@37 582 {
nuclear@40 583 int len = strlen(line);
nuclear@40 584 if(!len) return;
nuclear@40 585
nuclear@40 586 char *buf = (char*)alloca(len + 1);
nuclear@40 587 memcpy(buf, line, len + 1);
nuclear@37 588
nuclear@39 589 std::vector<char*> args;
nuclear@39 590 char *tok;
nuclear@39 591
nuclear@39 592 while((tok = strtok(buf, " \n\r\v\t"))) {
nuclear@39 593 buf = 0;
nuclear@39 594 args.push_back(tok);
nuclear@39 595 }
nuclear@39 596 args.push_back(0);
nuclear@39 597 int argc = args.size() - 1;
nuclear@39 598
nuclear@40 599 if(!args[0]) return;
nuclear@40 600
nuclear@40 601 if(strcmp(args[0], "exit") == 0 || strcmp(args[0], "quit") == 0) {
nuclear@39 602 exit(0);
nuclear@40 603
nuclear@40 604 } else if(strcmp(args[0], "clear") == 0) {
nuclear@40 605 erb_clear(erb);
nuclear@40 606
nuclear@40 607 } else if(strcmp(args[0], "stop") == 0) {
nuclear@40 608 erb_end_frame(erb);
nuclear@40 609
nuclear@40 610 } else if(strcmp(args[0], "render") == 0) {
nuclear@40 611 long tm = 0;
nuclear@40 612 if(args[1]) {
nuclear@40 613 char *endp;
nuclear@40 614 tm = strtol(args[1], &endp, 10);
nuclear@40 615 if(endp == args[1]) {
nuclear@40 616 con.printf("the argument to render must be a time value in milliseconds\n");
nuclear@40 617 return;
nuclear@40 618 }
nuclear@40 619 }
nuclear@40 620 begin_frame(tm);
nuclear@40 621
nuclear@41 622 } else if(strcmp(args[0], "samples") == 0) {
nuclear@41 623 if(args[1] && (opt_samples = atoi(args[1]))) {
nuclear@41 624 erb_setopti(erb, ERB_OPT_MAX_SAMPLES, opt_samples);
nuclear@41 625 con.printf("max samples is now %d\n", opt_samples);
nuclear@41 626 } else {
nuclear@41 627 con.printf("invalid samples command: %s\n", line);
nuclear@41 628 }
nuclear@41 629
nuclear@39 630 } else if(strcmp(args[0], "load") == 0) {
nuclear@39 631 for(int i=1; i<argc; i++) {
nuclear@39 632 if(erb_load_scene(erb, args[i]) == -1) {
nuclear@39 633 con.printf("failed to load scene: %s\n", args[1]);
nuclear@39 634 } else {
nuclear@39 635 begin_frame(0);
nuclear@39 636 }
nuclear@39 637 }
nuclear@39 638
nuclear@40 639 } else if(strcmp(args[0], "add") == 0) {
nuclear@40 640 if(erb_proc_cmd(erb, line + 4) == -1) {
nuclear@40 641 con.puts("invalid add command\n");
nuclear@39 642 } else {
nuclear@39 643 begin_frame(0);
nuclear@39 644 }
nuclear@40 645
nuclear@41 646 } else if(strcmp(args[0], "help") == 0) {
nuclear@41 647 con.printf("Available commands:\n");
nuclear@41 648 con.printf("render [anim time] begins rendering a frame with the given time (default: 0)\n");
nuclear@41 649 con.printf("stop stops rendering\n");
nuclear@41 650 con.printf("samples <count> sets the maximum number of samples per pixel\n");
nuclear@41 651 con.printf("iter <count> sets the maximum number of ray bounces\n");
nuclear@41 652 con.printf("load <filename> loads a scene file\n");
nuclear@41 653 con.printf("add <object command> adds another object to the scene. For valid object commands see test/scene\n");
nuclear@41 654
nuclear@40 655 } else {
nuclear@40 656 con.printf("unrecognized command: %s\n", args[0]);
nuclear@37 657 }
nuclear@37 658 }