rayzor

annotate src/main.cc @ 19:252999cd1a3f

added reflection and refraction
author John Tsiombikas <nuclear@member.fsf.org>
date Tue, 15 Apr 2014 00:59:37 +0300
parents 859ccadca671
children
rev   line source
nuclear@1 1 #include <stdio.h>
nuclear@1 2 #include <stdlib.h>
nuclear@1 3 #include <string.h>
nuclear@7 4 #include <signal.h>
nuclear@14 5 #include <errno.h>
nuclear@17 6 #include <float.h>
nuclear@14 7 #include <direct.h>
nuclear@1 8 #include "inttypes.h"
nuclear@1 9 #include "gfx.h"
nuclear@1 10 #include "keyb.h"
nuclear@1 11 #include "mouse.h"
nuclear@1 12 #include "logger.h"
nuclear@6 13 #include "scene.h"
nuclear@9 14 #include "rayzor.h"
nuclear@9 15 #include "screen.h"
nuclear@9 16 #include "modeller.h"
nuclear@9 17 #include "renderer.h"
nuclear@9 18 #include "scrman.h"
nuclear@10 19 #include "timer.h"
nuclear@10 20
nuclear@10 21 #ifdef __DOS__
nuclear@10 22 #undef USE_ASM_SWAPBUF
nuclear@10 23 #endif
nuclear@10 24
nuclear@10 25 #ifdef USE_ASM_SWAPBUF
nuclear@10 26 // defined in swapbuf.asm
nuclear@10 27 extern "C" void swap_buffers_asm(void *dest, void *src, int xsz, int ysz, int bpp);
nuclear@10 28 #endif
nuclear@1 29
nuclear@6 30 static bool init();
nuclear@6 31 static void cleanup();
nuclear@1 32 static void display();
nuclear@1 33 static void swap_buffers();
nuclear@9 34 static void draw_cursor(uint32_t *buf, int mx, int my);
nuclear@14 35 static void screenshot();
nuclear@1 36 static void handle_keyboard();
nuclear@1 37 static void handle_mouse();
nuclear@1 38 static bool parse_args(int argc, char **argv);
nuclear@7 39 static void sig(int s);
nuclear@1 40
nuclear@9 41 uint32_t *fb_pixels;
nuclear@9 42 int fb_width = 640;
nuclear@9 43 int fb_height = 480;
nuclear@9 44 int fb_bpp = 32;
nuclear@9 45 Scene *scene;
nuclear@9 46
nuclear@6 47 static bool novideo;
nuclear@9 48 static void *fb;
nuclear@1 49 static int rbits, gbits, bbits;
nuclear@1 50 static int rshift, gshift, bshift;
nuclear@1 51 static unsigned int rmask, gmask, bmask;
nuclear@1 52
nuclear@10 53 static bool use_asm_swap = true;
nuclear@7 54 static bool use_mouse;
nuclear@9 55 static int mouse_x, mouse_y;
nuclear@1 56 static bool quit;
nuclear@14 57 static bool cap_shot;
nuclear@1 58
nuclear@1 59 int main(int argc, char **argv)
nuclear@1 60 {
nuclear@10 61 unsigned long start_msec, msec;
nuclear@10 62 unsigned long nframes = 0;
nuclear@10 63
nuclear@1 64 if(!parse_args(argc, argv)) {
nuclear@1 65 return 1;
nuclear@1 66 }
nuclear@6 67 if(!init()) {
nuclear@1 68 return 1;
nuclear@1 69 }
nuclear@1 70
nuclear@10 71 start_msec = get_msec();
nuclear@10 72
nuclear@1 73 // main loop
nuclear@1 74 for(;;) {
nuclear@1 75 handle_keyboard();
nuclear@1 76 handle_mouse();
nuclear@1 77 if(quit) break;
nuclear@1 78
nuclear@1 79 display();
nuclear@10 80 ++nframes;
nuclear@6 81
nuclear@6 82 if(novideo) break;
nuclear@1 83 }
nuclear@1 84
nuclear@10 85 msec = get_msec() - start_msec;
nuclear@10 86
nuclear@6 87 cleanup();
nuclear@10 88
nuclear@10 89 printf("Average framerate: %g\n", (float)nframes / ((float)msec / 1000.0f));
nuclear@1 90 printf("Thank you for using Rayzor!\n");
nuclear@1 91 return 0;
nuclear@1 92 }
nuclear@1 93
nuclear@9 94 void quit_app()
nuclear@9 95 {
nuclear@9 96 quit = true;
nuclear@9 97 }
nuclear@9 98
nuclear@6 99 static bool init()
nuclear@6 100 {
nuclear@7 101 signal(SIGINT, sig);
nuclear@7 102 signal(SIGSEGV, sig);
nuclear@7 103 signal(SIGILL, sig);
nuclear@7 104 signal(SIGFPE, sig);
nuclear@7 105
nuclear@18 106 #ifdef __WATCOM__
nuclear@17 107 // mask all fpe except invalid op
nuclear@17 108 _control87(~_EM_INVALID, _MCW_EM);
nuclear@18 109 #endif
nuclear@17 110
nuclear@10 111 init_timer(128);
nuclear@10 112
nuclear@6 113 if(!novideo) {
nuclear@6 114 if(kb_init(32) == -1) {
nuclear@6 115 fprintf(stderr, "failed to initialize keyboard driver\n");
nuclear@6 116 return false;
nuclear@6 117 }
nuclear@6 118
nuclear@9 119 if(!(fb = set_video_mode(fb_width, fb_height, fb_bpp))) {
nuclear@6 120 set_text_mode();
nuclear@9 121 fprintf(stderr, "failed to set video mode: %dx%d %dbpp\n", fb_width, fb_height, fb_bpp);
nuclear@6 122 return false;
nuclear@6 123 }
nuclear@9 124 fb_bpp = get_color_depth();
nuclear@6 125 get_color_bits(&rbits, &gbits, &bbits);
nuclear@6 126 get_color_shift(&rshift, &gshift, &bshift);
nuclear@6 127 get_color_mask(&rmask, &gmask, &bmask);
nuclear@6 128
nuclear@10 129 printlog("video resolution: %dx%d\n", fb_width, fb_height);
nuclear@9 130 printlog("bpp: %d (%d %d %d)\n", fb_bpp, rbits, gbits, bbits);
nuclear@6 131 printlog("shift: %d %d %d\n", rshift, gshift, bshift);
nuclear@6 132 printlog("mask: %x %x %x\n", rmask, gmask, bmask);
nuclear@7 133
nuclear@7 134 if(have_mouse()) {
nuclear@7 135 use_mouse = true;
nuclear@9 136 set_mouse_limits(0, 0, fb_width - 1, fb_height - 1);
nuclear@7 137 }
nuclear@6 138 } else {
nuclear@6 139 logger_output(stdout);
nuclear@6 140 printlog("novideo (debug) mode\n");
nuclear@10 141 fb_bpp = 24;
nuclear@6 142 rbits = gbits = bbits = 8;
nuclear@6 143 }
nuclear@6 144
nuclear@9 145 fb_pixels = new uint32_t[fb_width * fb_height * 4];
nuclear@9 146 if(!fb_pixels) {
nuclear@6 147 return false;
nuclear@6 148 }
nuclear@6 149
nuclear@9 150 scene = new Scene;
nuclear@6 151
nuclear@6 152 Sphere *sph = new Sphere;
nuclear@17 153 sph->mtl.diffuse = Vector3(1.0, 0.3, 0.1);
nuclear@17 154 sph->mtl.roughness = 0.4;
nuclear@12 155 scene->add(sph);
nuclear@12 156
nuclear@12 157 Box *box = new Box;
nuclear@17 158 box->mtl.diffuse = Vector3(0.1, 0.4, 1.0);
nuclear@19 159 box->mtl.specular = Vector3(0.3, 0.6, 1.0);
nuclear@19 160 box->mtl.roughness = 0.2;
nuclear@19 161 box->mtl.reflect = 0.8;
nuclear@17 162 box->set_position(Vector3(0, -1.1, 0));
nuclear@17 163 box->set_scaling(Vector3(4, 0.1, 4));
nuclear@12 164 scene->add(box);
nuclear@17 165
nuclear@17 166 Light *lt = new Light;
nuclear@17 167 lt->set_intensity(0.8);
nuclear@17 168 lt->set_position(Vector3(-10, 10, 10));
nuclear@17 169 scene->add(lt);
nuclear@6 170
nuclear@9 171 Modeller *modeller = new Modeller;
nuclear@9 172 if(!modeller->init()) {
nuclear@9 173 return false;
nuclear@9 174 }
nuclear@9 175 add_screen(modeller);
nuclear@9 176
nuclear@9 177 Renderer *renderer = new Renderer;
nuclear@9 178 if(!renderer->init()) {
nuclear@9 179 return false;
nuclear@9 180 }
nuclear@9 181 add_screen(renderer);
nuclear@9 182
nuclear@9 183 activate_screen(modeller); // start the modeller screen
nuclear@6 184 return true;
nuclear@6 185 }
nuclear@6 186
nuclear@6 187 static void cleanup()
nuclear@6 188 {
nuclear@9 189 delete scene;
nuclear@9 190 delete [] fb_pixels;
nuclear@6 191
nuclear@9 192 destroy_screens();
nuclear@6 193
nuclear@6 194 if(!novideo) {
nuclear@6 195 set_text_mode();
nuclear@6 196 kb_shutdown();
nuclear@6 197 }
nuclear@6 198 }
nuclear@6 199
nuclear@1 200 static void display()
nuclear@1 201 {
nuclear@9 202 Screen *scr = active_screen();
nuclear@9 203 if(scr) {
nuclear@9 204 scr->update();
nuclear@9 205 scr->draw();
nuclear@9 206 }
nuclear@6 207
nuclear@7 208 // draw the mouse cursor
nuclear@7 209 if(use_mouse) {
nuclear@9 210 draw_cursor(fb_pixels, mouse_x, mouse_y);
nuclear@7 211 }
nuclear@7 212
nuclear@14 213 if(cap_shot) {
nuclear@14 214 screenshot();
nuclear@14 215 cap_shot = false;
nuclear@14 216 }
nuclear@14 217
nuclear@6 218 if(!novideo) {
nuclear@10 219 wait_vsync();
nuclear@10 220 #ifdef USE_ASM_SWAPBUF
nuclear@10 221 swap_buffers_asm(fb, fb_pixels, fb_width, fb_height, fb_bpp);
nuclear@10 222 #else
nuclear@6 223 swap_buffers();
nuclear@10 224 #endif
nuclear@1 225 }
nuclear@1 226 }
nuclear@1 227
nuclear@1 228 #define PACK_RGB(r, g, b) \
nuclear@1 229 ((((r) << rshift) & rmask) | \
nuclear@1 230 (((g) << gshift) & gmask) | \
nuclear@1 231 (((b) << bshift) & bmask))
nuclear@1 232
nuclear@11 233 #define UNPACK_RED(c) (((c) >> 16) & 0xff)
nuclear@9 234 #define UNPACK_GREEN(c) (((c) >> 8) & 0xff)
nuclear@11 235 #define UNPACK_BLUE(c) ((c) & 0xff)
nuclear@9 236
nuclear@1 237 static void swap_buffers()
nuclear@1 238 {
nuclear@9 239 uint32_t *src = fb_pixels;
nuclear@9 240 int num_pixels = fb_width * fb_height;
nuclear@1 241
nuclear@9 242 switch(fb_bpp) {
nuclear@1 243 case 32:
nuclear@9 244 memcpy(fb, fb_pixels, num_pixels * 4);
nuclear@1 245 break;
nuclear@1 246
nuclear@1 247 case 24:
nuclear@9 248 {
nuclear@9 249 unsigned char *dest = (unsigned char*)fb;
nuclear@10 250 for(int i=0; i<num_pixels-1; i++) {
nuclear@10 251 *((uint32_t*)dest) = *src++;
nuclear@10 252 dest += 3;
nuclear@9 253 }
nuclear@10 254 *dest++ = UNPACK_RED(*src);
nuclear@10 255 *dest++ = UNPACK_GREEN(*src);
nuclear@10 256 *dest++ = UNPACK_BLUE(*src);
nuclear@9 257 }
nuclear@1 258 break;
nuclear@1 259
nuclear@1 260 case 16:
nuclear@1 261 {
nuclear@1 262 uint16_t *dest = (uint16_t*)fb;
nuclear@1 263 for(int i=0; i<num_pixels; i++) {
nuclear@9 264 uint32_t c = *src++;
nuclear@9 265 unsigned char r = UNPACK_RED(c);
nuclear@9 266 unsigned char g = UNPACK_GREEN(c);
nuclear@9 267 unsigned char b = UNPACK_BLUE(c);
nuclear@9 268
nuclear@11 269 *dest++ = (((r) << 8) & 0xf800) |
nuclear@11 270 (((g) << 3) & 0x7e0) |
nuclear@11 271 (((b) >> 3) & 0x1f);
nuclear@1 272 }
nuclear@1 273 }
nuclear@1 274 break;
nuclear@1 275
nuclear@1 276 default:
nuclear@1 277 break;
nuclear@1 278 }
nuclear@1 279 }
nuclear@1 280
nuclear@9 281 static void draw_cursor(uint32_t *buf, int mx, int my)
nuclear@7 282 {
nuclear@9 283 uint32_t *cptr = buf + my * fb_width + mx;
nuclear@7 284 int i, cw[2] = {4, 4}, ch[2] = {4, 4};
nuclear@7 285
nuclear@7 286 if(mx < cw[0]) cw[0] = mx;
nuclear@7 287 if(my < ch[0]) ch[0] = my;
nuclear@9 288 if(fb_width - mx < cw[1]) cw[1] = fb_width - mx - 1;
nuclear@9 289 if(fb_height - my < ch[1]) ch[1] = fb_height - my - 1;
nuclear@7 290
nuclear@7 291 for(i=1; i<cw[0]; i++) {
nuclear@9 292 int idx = -i;
nuclear@9 293 cptr[idx] = 0xffffff;
nuclear@7 294 }
nuclear@7 295 for(i=1; i<cw[1]; i++) {
nuclear@9 296 int idx = i;
nuclear@9 297 cptr[idx] = 0xffffff;
nuclear@7 298 }
nuclear@7 299 for(i=1; i<ch[0]; i++) {
nuclear@9 300 int idx = -i * fb_width;
nuclear@9 301 cptr[idx] = 0xffffff;
nuclear@7 302 }
nuclear@7 303 for(i=1; i<ch[1]; i++) {
nuclear@9 304 int idx = i * fb_width;
nuclear@9 305 cptr[idx] = 0xffffff;
nuclear@7 306 }
nuclear@7 307 }
nuclear@7 308
nuclear@14 309 #define PPM_COMMENT "screenshot saved by the rayzor modeller/renderer"
nuclear@14 310 static void screenshot()
nuclear@14 311 {
nuclear@14 312 static int shotidx = -1;
nuclear@14 313 FILE *fp;
nuclear@14 314 char fname[PATH_MAX];
nuclear@14 315
nuclear@14 316 if(shotidx == -1) {
nuclear@14 317 DIR *dir;
nuclear@14 318 struct dirent *dent;
nuclear@14 319
nuclear@14 320 shotidx = 0;
nuclear@14 321 if((dir = opendir("."))) {
nuclear@14 322 while((dent = readdir(dir))) {
nuclear@14 323 int i, num;
nuclear@14 324 for(i=0; dent->d_name[i]; i++) {
nuclear@14 325 fname[i] = tolower(dent->d_name[i]);
nuclear@14 326 }
nuclear@14 327 fname[i] = 0;
nuclear@14 328
nuclear@14 329 if(sscanf(fname, "shot%d.ppm", &num) == 1 && num > shotidx) {
nuclear@14 330 shotidx = num;
nuclear@14 331 }
nuclear@14 332 }
nuclear@14 333 closedir(dir);
nuclear@14 334 }
nuclear@14 335 }
nuclear@14 336
nuclear@14 337 sprintf(fname, "shot%04d.ppm", ++shotidx);
nuclear@14 338 if(!(fp = fopen(fname, "wb"))) {
nuclear@14 339 printlog("failed to save screenshot %s: %s\n", fname, strerror(errno));
nuclear@14 340 return;
nuclear@14 341 }
nuclear@14 342
nuclear@17 343 fprintf(fp, "P6\n# " PPM_COMMENT "\n%d %d\n255\n", fb_width, fb_height);
nuclear@14 344 for(int i=0; i<fb_width * fb_height; i++) {
nuclear@14 345 uint32_t c = fb_pixels[i];
nuclear@14 346 fputc(UNPACK_RED(c), fp);
nuclear@14 347 fputc(UNPACK_GREEN(c), fp);
nuclear@14 348 fputc(UNPACK_BLUE(c), fp);
nuclear@14 349 }
nuclear@14 350
nuclear@14 351 fclose(fp);
nuclear@14 352 }
nuclear@14 353
nuclear@1 354 static void handle_keyboard()
nuclear@1 355 {
nuclear@5 356 int key;
nuclear@9 357 Screen *scr = active_screen();
nuclear@1 358
nuclear@6 359 if(novideo) return;
nuclear@6 360
nuclear@5 361 while((key = kb_getkey()) != -1) {
nuclear@10 362 switch(key) {
nuclear@10 363 case '`':
nuclear@10 364 use_asm_swap = !use_asm_swap;
nuclear@10 365 break;
nuclear@10 366
nuclear@12 367 case 'q':
nuclear@12 368 case 'x':
nuclear@12 369 if(kb_isdown(KB_ALT)) {
nuclear@12 370 quit_app();
nuclear@12 371 }
nuclear@12 372 break;
nuclear@12 373
nuclear@14 374 case KB_F12:
nuclear@14 375 cap_shot = true;
nuclear@14 376 break;
nuclear@14 377
nuclear@10 378 default:
nuclear@10 379 break;
nuclear@10 380 }
nuclear@9 381 scr->handle_keyboard(key, true); // TODO also generate release events...
nuclear@1 382 }
nuclear@1 383 }
nuclear@1 384
nuclear@1 385 static void handle_mouse()
nuclear@1 386 {
nuclear@9 387 static int prev_mx, prev_my, prev_bnmask, bndiff;
nuclear@9 388 int mx, my, bnmask;
nuclear@9 389 Screen *scr = active_screen();
nuclear@7 390
nuclear@7 391 if(!use_mouse || novideo) return;
nuclear@7 392
nuclear@7 393 bnmask = read_mouse(&mx, &my);
nuclear@9 394 if(scr && (bndiff = bnmask ^ prev_bnmask)) {
nuclear@9 395 for(int i=0; i<8; i++) {
nuclear@9 396 int bit = 1 << i;
nuclear@9 397 if(bndiff & bit) {
nuclear@9 398 scr->handle_mbutton(i, bnmask & bit, mx, my);
nuclear@9 399 }
nuclear@9 400 }
nuclear@7 401 }
nuclear@9 402 prev_bnmask = bnmask;
nuclear@9 403
nuclear@9 404 if(scr && (mx != prev_mx || my != prev_my)) {
nuclear@9 405 scr->handle_mmotion(mx, my);
nuclear@7 406 }
nuclear@9 407 prev_mx = mx;
nuclear@9 408 prev_my = my;
nuclear@10 409
nuclear@10 410 mouse_x = mx;
nuclear@10 411 mouse_y = my;
nuclear@7 412 }
nuclear@7 413
nuclear@1 414
nuclear@1 415 static struct {
nuclear@1 416 int opt;
nuclear@1 417 const char *lopt;
nuclear@1 418 const char *desc;
nuclear@1 419 } options[] = {
nuclear@1 420 {'s', "size", "resolution <xres>x<yres>[:bpp]"},
nuclear@6 421 {'n', "novid", "don't switch video mode (for debugging)"},
nuclear@1 422 {'h', "help", "print usage information and exit"},
nuclear@1 423 {-1, 0, 0}
nuclear@1 424 };
nuclear@1 425
nuclear@1 426 static void print_usage(const char *argv0)
nuclear@1 427 {
nuclear@1 428 printf("%s usage\n", argv0);
nuclear@1 429 for(int i=0; options[i].opt != -1; i++) {
nuclear@1 430 printf(" -%c, -%s: %s\n", options[i].opt, options[i].lopt, options[i].desc);
nuclear@1 431 }
nuclear@1 432 exit(0);
nuclear@1 433 }
nuclear@1 434
nuclear@1 435 static bool parse_args(int argc, char **argv)
nuclear@1 436 {
nuclear@1 437 for(int i=1; i<argc; i++) {
nuclear@1 438 if(argv[i][0] == '-') {
nuclear@1 439 int opt = -1;
nuclear@1 440
nuclear@1 441 for(int j=0; options[j].opt != -1; j++) {
nuclear@1 442 if(argv[i][2] == 0) {
nuclear@1 443 if(argv[i][1] == options[j].opt) {
nuclear@1 444 opt = options[j].opt;
nuclear@1 445 break;
nuclear@1 446 }
nuclear@1 447 } else {
nuclear@1 448 if(strcmp(argv[i] + 1, options[j].lopt) == 0) {
nuclear@1 449 opt = options[j].opt;
nuclear@1 450 break;
nuclear@1 451 }
nuclear@1 452 }
nuclear@1 453 }
nuclear@1 454
nuclear@1 455 switch(opt) {
nuclear@1 456 case 's':
nuclear@9 457 if(sscanf(argv[++i], "%dx%d:%d", &fb_width, &fb_height, &fb_bpp) < 2) {
nuclear@1 458 fprintf(stderr, "%s must be followed by a resolution: WxH\n", argv[i - 1]);
nuclear@1 459 return false;
nuclear@1 460 }
nuclear@1 461 break;
nuclear@1 462
nuclear@6 463 case 'n':
nuclear@6 464 novideo = true;
nuclear@6 465 break;
nuclear@6 466
nuclear@1 467 case 'h':
nuclear@1 468 print_usage(argv[0]); // doesn't return
nuclear@1 469 break;
nuclear@1 470
nuclear@1 471 default:
nuclear@1 472 fprintf(stderr, "unknown option: %s\n", argv[i]);
nuclear@1 473 return false;
nuclear@1 474 }
nuclear@1 475 } else {
nuclear@1 476 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
nuclear@1 477 return false;
nuclear@1 478 }
nuclear@1 479 }
nuclear@1 480 return true;
nuclear@1 481 }
nuclear@7 482
nuclear@7 483 static void sig(int s)
nuclear@7 484 {
nuclear@7 485 cleanup();
nuclear@7 486 fprintf(stderr, "signal caught: %d\n", s);
nuclear@17 487
nuclear@18 488 #ifdef __WATCOM__
nuclear@17 489 if(s == SIGFPE) {
nuclear@17 490 unsigned int st = _status87();
nuclear@17 491 fprintf(stderr, "fpu status: %x\n", st);
nuclear@17 492 }
nuclear@18 493 #endif
nuclear@17 494
nuclear@9 495 exit(1);
nuclear@7 496 }