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