| 1 | #ifndef lint | 
| 2 | static const char       RCSid[] = "$Id: rpmain.c,v 2.4 2003/06/26 00:58:10 schorsch Exp $"; | 
| 3 | #endif | 
| 4 | /* | 
| 5 | *  rpmain.c - main for rpict batch rendering program | 
| 6 | */ | 
| 7 |  | 
| 8 | #include "copyright.h" | 
| 9 |  | 
| 10 | #include  <sys/types.h> | 
| 11 | #include  <signal.h> | 
| 12 | #ifdef _WIN32 | 
| 13 | #include <process.h> /* getpid */ | 
| 14 | #endif | 
| 15 |  | 
| 16 | #include  "platform.h" | 
| 17 | #include  "ray.h" | 
| 18 | #include  "source.h" | 
| 19 | #include  "ambient.h" | 
| 20 | #include  "random.h" | 
| 21 | #include  "paths.h" | 
| 22 | #include  "view.h" | 
| 23 |  | 
| 24 | /* persistent processes define */ | 
| 25 | #ifdef  F_SETLKW | 
| 26 | #define  PERSIST        1               /* normal persist */ | 
| 27 | #define  PARALLEL       2               /* parallel persist */ | 
| 28 | #define  PCHILD         3               /* child of normal persist */ | 
| 29 | #endif | 
| 30 |  | 
| 31 | char  *progname;                        /* argv[0] */ | 
| 32 |  | 
| 33 | char  *octname;                         /* octree name */ | 
| 34 |  | 
| 35 | char  *sigerr[NSIG];                    /* signal error messages */ | 
| 36 |  | 
| 37 | char  *shm_boundary = NULL;             /* boundary of shared memory */ | 
| 38 |  | 
| 39 | char  *errfile = NULL;                  /* error output file */ | 
| 40 |  | 
| 41 | extern time_t  time(); | 
| 42 | extern time_t  tstart;                  /* start time */ | 
| 43 |  | 
| 44 | extern int  ralrm;                      /* seconds between reports */ | 
| 45 |  | 
| 46 | extern VIEW  ourview;                   /* viewing parameters */ | 
| 47 |  | 
| 48 | extern int  hresolu;                    /* horizontal resolution */ | 
| 49 | extern int  vresolu;                    /* vertical resolution */ | 
| 50 | extern double  pixaspect;               /* pixel aspect ratio */ | 
| 51 |  | 
| 52 | extern int  psample;                    /* pixel sample size */ | 
| 53 | extern double  maxdiff;                 /* max. sample difference */ | 
| 54 | extern double  dstrpix;                 /* square pixel distribution */ | 
| 55 |  | 
| 56 | extern double  mblur;                   /* motion blur parameter */ | 
| 57 |  | 
| 58 | void    onsig(); | 
| 59 | void    sigdie(); | 
| 60 | void    printdefaults(); | 
| 61 |  | 
| 62 |  | 
| 63 | int | 
| 64 | main(argc, argv) | 
| 65 | int  argc; | 
| 66 | char  *argv[]; | 
| 67 | { | 
| 68 | #define  check(ol,al)           if (argv[i][ol] || \ | 
| 69 | badarg(argc-i-1,argv+i+1,al)) \ | 
| 70 | goto badopt | 
| 71 | #define  bool(olen,var)         switch (argv[i][olen]) { \ | 
| 72 | case '\0': var = !var; break; \ | 
| 73 | case 'y': case 'Y': case 't': case 'T': \ | 
| 74 | case '+': case '1': var = 1; break; \ | 
| 75 | case 'n': case 'N': case 'f': case 'F': \ | 
| 76 | case '-': case '0': var = 0; break; \ | 
| 77 | default: goto badopt; } | 
| 78 | char  *err; | 
| 79 | char  *recover = NULL; | 
| 80 | char  *outfile = NULL; | 
| 81 | char  *zfile = NULL; | 
| 82 | int  loadflags = ~IO_FILES; | 
| 83 | int  seqstart = 0; | 
| 84 | int  persist = 0; | 
| 85 | int  duped1; | 
| 86 | int  rval; | 
| 87 | int  i; | 
| 88 | /* record start time */ | 
| 89 | tstart = time((time_t *)NULL); | 
| 90 | /* global program name */ | 
| 91 | progname = argv[0] = fixargv0(argv[0]); | 
| 92 | /* option city */ | 
| 93 | for (i = 1; i < argc; i++) { | 
| 94 | /* expand arguments */ | 
| 95 | while ((rval = expandarg(&argc, &argv, i)) > 0) | 
| 96 | ; | 
| 97 | if (rval < 0) { | 
| 98 | sprintf(errmsg, "cannot expand '%s'", argv[i]); | 
| 99 | error(SYSTEM, errmsg); | 
| 100 | } | 
| 101 | if (argv[i] == NULL || argv[i][0] != '-') | 
| 102 | break;                  /* break from options */ | 
| 103 | if (!strcmp(argv[i], "-version")) { | 
| 104 | puts(VersionID); | 
| 105 | quit(0); | 
| 106 | } | 
| 107 | if (!strcmp(argv[i], "-defaults") || | 
| 108 | !strcmp(argv[i], "-help")) { | 
| 109 | printdefaults(); | 
| 110 | quit(0); | 
| 111 | } | 
| 112 | rval = getrenderopt(argc-i, argv+i); | 
| 113 | if (rval >= 0) { | 
| 114 | i += rval; | 
| 115 | continue; | 
| 116 | } | 
| 117 | rval = getviewopt(&ourview, argc-i, argv+i); | 
| 118 | if (rval >= 0) { | 
| 119 | i += rval; | 
| 120 | continue; | 
| 121 | } | 
| 122 | /* rpict options */ | 
| 123 | switch (argv[i][1]) { | 
| 124 | case 'v':                               /* view file */ | 
| 125 | if (argv[i][2] != 'f') | 
| 126 | goto badopt; | 
| 127 | check(3,"s"); | 
| 128 | rval = viewfile(argv[++i], &ourview, NULL); | 
| 129 | if (rval < 0) { | 
| 130 | sprintf(errmsg, | 
| 131 | "cannot open view file \"%s\"", | 
| 132 | argv[i]); | 
| 133 | error(SYSTEM, errmsg); | 
| 134 | } else if (rval == 0) { | 
| 135 | sprintf(errmsg, | 
| 136 | "bad view file \"%s\"", | 
| 137 | argv[i]); | 
| 138 | error(USER, errmsg); | 
| 139 | } | 
| 140 | break; | 
| 141 | case 'p':                               /* pixel */ | 
| 142 | switch (argv[i][2]) { | 
| 143 | case 's':                               /* sample */ | 
| 144 | check(3,"i"); | 
| 145 | psample = atoi(argv[++i]); | 
| 146 | break; | 
| 147 | case 't':                               /* threshold */ | 
| 148 | check(3,"f"); | 
| 149 | maxdiff = atof(argv[++i]); | 
| 150 | break; | 
| 151 | case 'j':                               /* jitter */ | 
| 152 | check(3,"f"); | 
| 153 | dstrpix = atof(argv[++i]); | 
| 154 | break; | 
| 155 | case 'a':                               /* aspect */ | 
| 156 | check(3,"f"); | 
| 157 | pixaspect = atof(argv[++i]); | 
| 158 | break; | 
| 159 | case 'm':                               /* motion */ | 
| 160 | check(3,"f"); | 
| 161 | mblur = atof(argv[++i]); | 
| 162 | break; | 
| 163 | default: | 
| 164 | goto badopt; | 
| 165 | } | 
| 166 | break; | 
| 167 | case 'x':                               /* x resolution */ | 
| 168 | check(2,"i"); | 
| 169 | hresolu = atoi(argv[++i]); | 
| 170 | break; | 
| 171 | case 'y':                               /* y resolution */ | 
| 172 | check(2,"i"); | 
| 173 | vresolu = atoi(argv[++i]); | 
| 174 | break; | 
| 175 | case 'S':                               /* slave index */ | 
| 176 | check(2,"i"); | 
| 177 | seqstart = atoi(argv[++i]); | 
| 178 | break; | 
| 179 | case 'o':                               /* output file */ | 
| 180 | check(2,"s"); | 
| 181 | outfile = argv[++i]; | 
| 182 | break; | 
| 183 | case 'z':                               /* z file */ | 
| 184 | check(2,"s"); | 
| 185 | zfile = argv[++i]; | 
| 186 | break; | 
| 187 | case 'r':                               /* recover file */ | 
| 188 | if (argv[i][2] == 'o') {                /* +output */ | 
| 189 | check(3,"s"); | 
| 190 | outfile = argv[i+1]; | 
| 191 | } else | 
| 192 | check(2,"s"); | 
| 193 | recover = argv[++i]; | 
| 194 | break; | 
| 195 | case 't':                               /* timer */ | 
| 196 | check(2,"i"); | 
| 197 | ralrm = atoi(argv[++i]); | 
| 198 | break; | 
| 199 | #ifdef  PERSIST | 
| 200 | case 'P':                               /* persist file */ | 
| 201 | if (argv[i][2] == 'P') { | 
| 202 | check(3,"s"); | 
| 203 | persist = PARALLEL; | 
| 204 | } else { | 
| 205 | check(2,"s"); | 
| 206 | persist = PERSIST; | 
| 207 | } | 
| 208 | persistfile(argv[++i]); | 
| 209 | break; | 
| 210 | #endif | 
| 211 | case 'w':                               /* warnings */ | 
| 212 | rval = erract[WARNING].pf != NULL; | 
| 213 | bool(2,rval); | 
| 214 | if (rval) erract[WARNING].pf = wputs; | 
| 215 | else erract[WARNING].pf = NULL; | 
| 216 | break; | 
| 217 | case 'e':                               /* error file */ | 
| 218 | check(2,"s"); | 
| 219 | errfile = argv[++i]; | 
| 220 | break; | 
| 221 | default: | 
| 222 | goto badopt; | 
| 223 | } | 
| 224 | } | 
| 225 | err = setview(&ourview);        /* set viewing parameters */ | 
| 226 | if (err != NULL) | 
| 227 | error(USER, err); | 
| 228 | /* initialize object types */ | 
| 229 | initotypes(); | 
| 230 | /* initialize urand */ | 
| 231 | initurand(2048); | 
| 232 | /* set up signal handling */ | 
| 233 | sigdie(SIGINT, "Interrupt"); | 
| 234 | #ifdef SIGHUP | 
| 235 | sigdie(SIGHUP, "Hangup"); | 
| 236 | #endif | 
| 237 | sigdie(SIGTERM, "Terminate"); | 
| 238 | #ifdef SIGPIPE | 
| 239 | sigdie(SIGPIPE, "Broken pipe"); | 
| 240 | #endif | 
| 241 | #ifdef SIGALRM | 
| 242 | sigdie(SIGALRM, "Alarm clock"); | 
| 243 | #endif | 
| 244 | #ifdef  SIGXCPU | 
| 245 | sigdie(SIGXCPU, "CPU limit exceeded"); | 
| 246 | sigdie(SIGXFSZ, "File size exceeded"); | 
| 247 | #endif | 
| 248 | /* open error file */ | 
| 249 | if (errfile != NULL) { | 
| 250 | if (freopen(errfile, "a", stderr) == NULL) | 
| 251 | quit(2); | 
| 252 | fprintf(stderr, "**************\n*** PID %5d: ", | 
| 253 | getpid()); | 
| 254 | printargs(argc, argv, stderr); | 
| 255 | putc('\n', stderr); | 
| 256 | fflush(stderr); | 
| 257 | } | 
| 258 | #ifdef  NICE | 
| 259 | nice(NICE);                     /* lower priority */ | 
| 260 | #endif | 
| 261 | /* get octree */ | 
| 262 | if (i == argc) | 
| 263 | octname = NULL; | 
| 264 | else if (i == argc-1) | 
| 265 | octname = argv[i]; | 
| 266 | else | 
| 267 | goto badopt; | 
| 268 | if (seqstart > 0 && octname == NULL) | 
| 269 | error(USER, "missing octree argument"); | 
| 270 | /* set up output */ | 
| 271 | #ifdef  PERSIST | 
| 272 | if (persist) { | 
| 273 | if (recover != NULL) | 
| 274 | error(USER, "persist option used with recover file"); | 
| 275 | if (seqstart <= 0) | 
| 276 | error(USER, "persist option only for sequences"); | 
| 277 | if (outfile == NULL) | 
| 278 | duped1 = dup(fileno(stdout));   /* don't lose our output */ | 
| 279 | openheader(); | 
| 280 | } else | 
| 281 | #endif | 
| 282 | if (outfile != NULL) | 
| 283 | openheader(); | 
| 284 | #ifdef  _WIN32 | 
| 285 | SET_FILE_BINARY(stdout); | 
| 286 | if (octname == NULL) | 
| 287 | SET_FILE_BINARY(stdin); | 
| 288 | #endif | 
| 289 | readoct(octname, loadflags, &thescene, NULL); | 
| 290 | nsceneobjs = nobjects; | 
| 291 |  | 
| 292 | if (loadflags & IO_INFO) {      /* print header */ | 
| 293 | printargs(i, argv, stdout); | 
| 294 | printf("SOFTWARE= %s\n", VersionID); | 
| 295 | } | 
| 296 |  | 
| 297 | marksources();                  /* find and mark sources */ | 
| 298 |  | 
| 299 | setambient();                   /* initialize ambient calculation */ | 
| 300 |  | 
| 301 | #ifdef  PERSIST | 
| 302 | if (persist) { | 
| 303 | fflush(stdout); | 
| 304 | if (outfile == NULL) {          /* reconnect stdout */ | 
| 305 | dup2(duped1, fileno(stdout)); | 
| 306 | close(duped1); | 
| 307 | } | 
| 308 | if (persist == PARALLEL) {      /* multiprocessing */ | 
| 309 | preload_objs();         /* preload scene */ | 
| 310 | shm_boundary = (char *)malloc(16); | 
| 311 | strcpy(shm_boundary, "SHM_BOUNDARY"); | 
| 312 | while ((rval=fork()) == 0) {    /* keep on forkin' */ | 
| 313 | pflock(1); | 
| 314 | pfhold(); | 
| 315 | tstart = time((time_t *)NULL); | 
| 316 | } | 
| 317 | if (rval < 0) | 
| 318 | error(SYSTEM, "cannot fork child for persist function"); | 
| 319 | pfdetach();             /* parent exits */ | 
| 320 | } | 
| 321 | } | 
| 322 | runagain: | 
| 323 | if (persist) | 
| 324 | if (outfile == NULL)                    /* if out to stdout */ | 
| 325 | dupheader();                    /* send header */ | 
| 326 | else                                    /* if out to file */ | 
| 327 | duped1 = dup(fileno(stdout));   /* hang onto pipe */ | 
| 328 | #endif | 
| 329 | /* batch render picture(s) */ | 
| 330 | rpict(seqstart, outfile, zfile, recover); | 
| 331 | /* flush ambient file */ | 
| 332 | ambsync(); | 
| 333 | #ifdef  PERSIST | 
| 334 | if (persist == PERSIST) {       /* first run-through */ | 
| 335 | if ((rval=fork()) == 0) {       /* child loops until killed */ | 
| 336 | pflock(1); | 
| 337 | persist = PCHILD; | 
| 338 | } else {                        /* original process exits */ | 
| 339 | if (rval < 0) | 
| 340 | error(SYSTEM, "cannot fork child for persist function"); | 
| 341 | pfdetach();             /* parent exits */ | 
| 342 | } | 
| 343 | } | 
| 344 | if (persist == PCHILD) {        /* wait for a signal then go again */ | 
| 345 | if (outfile != NULL) | 
| 346 | close(duped1);          /* release output handle */ | 
| 347 | pfhold(); | 
| 348 | tstart = time((time_t *)NULL);  /* reinitialize */ | 
| 349 | raynum = nrays = 0; | 
| 350 | goto runagain; | 
| 351 | } | 
| 352 | #endif | 
| 353 | quit(0); | 
| 354 |  | 
| 355 | badopt: | 
| 356 | sprintf(errmsg, "command line error at '%s'", argv[i]); | 
| 357 | error(USER, errmsg); | 
| 358 |  | 
| 359 | #undef  check | 
| 360 | #undef  bool | 
| 361 | } | 
| 362 |  | 
| 363 |  | 
| 364 | void | 
| 365 | wputs(s)                                /* warning output function */ | 
| 366 | char    *s; | 
| 367 | { | 
| 368 | int  lasterrno = errno; | 
| 369 | eputs(s); | 
| 370 | errno = lasterrno; | 
| 371 | } | 
| 372 |  | 
| 373 |  | 
| 374 | void | 
| 375 | eputs(s)                                /* put string to stderr */ | 
| 376 | register char  *s; | 
| 377 | { | 
| 378 | static int  midline = 0; | 
| 379 |  | 
| 380 | if (!*s) | 
| 381 | return; | 
| 382 | if (!midline++) { | 
| 383 | fputs(progname, stderr); | 
| 384 | fputs(": ", stderr); | 
| 385 | } | 
| 386 | fputs(s, stderr); | 
| 387 | if (s[strlen(s)-1] == '\n') { | 
| 388 | fflush(stderr); | 
| 389 | midline = 0; | 
| 390 | } | 
| 391 | } | 
| 392 |  | 
| 393 |  | 
| 394 | void | 
| 395 | onsig(signo)                            /* fatal signal */ | 
| 396 | int  signo; | 
| 397 | { | 
| 398 | static int  gotsig = 0; | 
| 399 |  | 
| 400 | if (gotsig++)                   /* two signals and we're gone! */ | 
| 401 | _exit(signo); | 
| 402 |  | 
| 403 | #ifdef SIGALRM /* XXX how critical is this? */ | 
| 404 | alarm(15);                      /* allow 15 seconds to clean up */ | 
| 405 | signal(SIGALRM, SIG_DFL);       /* make certain we do die */ | 
| 406 | #endif | 
| 407 | eputs("signal - "); | 
| 408 | eputs(sigerr[signo]); | 
| 409 | eputs("\n"); | 
| 410 | quit(3); | 
| 411 | } | 
| 412 |  | 
| 413 |  | 
| 414 | void | 
| 415 | sigdie(signo, msg)                      /* set fatal signal */ | 
| 416 | int  signo; | 
| 417 | char  *msg; | 
| 418 | { | 
| 419 | if (signal(signo, onsig) == SIG_IGN) | 
| 420 | signal(signo, SIG_IGN); | 
| 421 | sigerr[signo] = msg; | 
| 422 | } | 
| 423 |  | 
| 424 |  | 
| 425 | void | 
| 426 | printdefaults()                 /* print default values to stdout */ | 
| 427 | { | 
| 428 | register char  *cp; | 
| 429 |  | 
| 430 | printf("-vt%c\t\t\t\t# view type %s\n", ourview.type, | 
| 431 | ourview.type==VT_PER ? "perspective" : | 
| 432 | ourview.type==VT_PAR ? "parallel" : | 
| 433 | ourview.type==VT_HEM ? "hemispherical" : | 
| 434 | ourview.type==VT_ANG ? "angular" : | 
| 435 | ourview.type==VT_CYL ? "cylindrical" : | 
| 436 | "unknown"); | 
| 437 | printf("-vp %f %f %f\t# view point\n", | 
| 438 | ourview.vp[0], ourview.vp[1], ourview.vp[2]); | 
| 439 | printf("-vd %f %f %f\t# view direction\n", | 
| 440 | ourview.vdir[0], ourview.vdir[1], ourview.vdir[2]); | 
| 441 | printf("-vu %f %f %f\t# view up\n", | 
| 442 | ourview.vup[0], ourview.vup[1], ourview.vup[2]); | 
| 443 | printf("-vh %f\t\t\t# view horizontal size\n", ourview.horiz); | 
| 444 | printf("-vv %f\t\t\t# view vertical size\n", ourview.vert); | 
| 445 | printf("-vo %f\t\t\t# view fore clipping plane\n", ourview.vfore); | 
| 446 | printf("-va %f\t\t\t# view aft clipping plane\n", ourview.vaft); | 
| 447 | printf("-vs %f\t\t\t# view shift\n", ourview.hoff); | 
| 448 | printf("-vl %f\t\t\t# view lift\n", ourview.voff); | 
| 449 | printf("-x  %-9d\t\t\t# x resolution\n", hresolu); | 
| 450 | printf("-y  %-9d\t\t\t# y resolution\n", vresolu); | 
| 451 | printf("-pa %f\t\t\t# pixel aspect ratio\n", pixaspect); | 
| 452 | printf("-pj %f\t\t\t# pixel jitter\n", dstrpix); | 
| 453 | printf("-pm %f\t\t\t# pixel motion\n", mblur); | 
| 454 | printf("-ps %-9d\t\t\t# pixel sample\n", psample); | 
| 455 | printf("-pt %f\t\t\t# pixel threshold\n", maxdiff); | 
| 456 | printf("-t  %-9d\t\t\t# time between reports\n", ralrm); | 
| 457 | printf(erract[WARNING].pf != NULL ? | 
| 458 | "-w+\t\t\t\t# warning messages on\n" : | 
| 459 | "-w-\t\t\t\t# warning messages off\n"); | 
| 460 | print_rdefaults(); | 
| 461 | } |