ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/rt/rxpmain.cpp
Revision: 2.1
Committed: Wed Aug 14 20:05:23 2024 UTC (8 months, 2 weeks ago) by greg
Branch: MAIN
Log Message:
feat(rxpict): Added new C++ picture rendering tool with multi-processing and spectral output

File Contents

# Content
1 #ifndef lint
2 static const char RCSid[] = "$Id: rpmain.c,v 2.33 2024/04/08 17:08:15 greg Exp $";
3 #endif
4 /*
5 * rxpmain.cpp - main for rxpict batch rendering program
6 */
7
8 #include "copyright.h"
9
10 #include <time.h>
11 #include <signal.h>
12
13 #include "rtprocess.h" /* getpid() */
14 #include "platform.h"
15 #include "RpictSimulManager.h"
16
17 extern char *progname; /* argv[0] */
18 const char *sigerr[NSIG]; /* signal error messages */
19 char *errfile = NULL; /* error output file */
20
21 VIEW ourview = STDVIEW; /* view parameters */
22 int hresolu = 512; /* horizontal resolution */
23 int vresolu = 512; /* vertical resolution */
24 double pixaspect = 1.0; /* pixel aspect ratio */
25 int hres, vres; /* current image resolution for srcdraw.c */
26
27 int psample = 4; /* pixel sample size */
28 double maxdiff = .05; /* max. difference for interpolation */
29 double dstrpix = 0.67; /* square pixel distribution */
30
31 double mblur = 0.; /* motion blur parameter */
32
33 double dblur = 0.; /* depth-of-field blur parameter */
34
35 int nproc = 1; /* number of processes to run */
36
37 int ralrm = 0; /* seconds between reports */
38
39 double pctdone = 0.0; /* percentage done */
40 time_t tlastrept = 0L; /* time at last report */
41 time_t tstart; /* starting time */
42
43 RenderDataType dtype = RDTrgbe; // output data flags
44
45 RpictSimulManager myRPmanager; // global simulation manager
46
47 static void onsig(int signo);
48 static void sigdie(int signo, const char *msg);
49 static void printdefaults(void);
50
51 /* rxpict additional features */
52 #define RXPICT_FEATURES "Recovery\nIrradianceCalc\nViewTypes=v,l,a,h,s,c\n" \
53 "ParticipatingMedia=Mist\n" \
54 "HessianAmbientCache\nAmbientAveraging\nAmbientValueSharing\n" \
55 "PixelJitter\nPixelSampling\nPixelMotion\nPixelDepthOfField\n" \
56 "SmallSourceDrawing\nViewSequence\nProgressReporting\n" \
57 "AdaptiveShadowTesting\nOutputs=v,l\n" \
58 "OutputCS=RGB,XYZ,prims,spec\n"
59
60
61 void
62 quit(int code) /* quit program -- called from ray_done() */
63 {
64 if (ray_pnprocs < 0)
65 _exit(code); /* avoid flush in child */
66
67 int ec = myRPmanager.Cleanup();
68
69 if (ec) code = ec;
70
71 exit(code);
72 }
73
74
75 int
76 main(int argc, char *argv[])
77 {
78 #define check(ol,al) if (argv[i][ol] || \
79 badarg(argc-i-1,argv+i+1,al)) \
80 goto badopt
81 #define check_bool(olen,var) switch (argv[i][olen]) { \
82 case '\0': var = !var; break; \
83 case 'y': case 'Y': case 't': case 'T': \
84 case '+': case '1': var = 1; break; \
85 case 'n': case 'N': case 'f': case 'F': \
86 case '-': case '0': var = 0; break; \
87 default: goto badopt; }
88 RGBPRIMS our_prims; /* private output color primitives */
89 int seqstart = 0;
90 char *recover = NULL;
91 char *outfile = NULL;
92 char *zfile = NULL;
93 int outfmt = 'c';
94 int rval;
95 int i;
96 /* record start time */
97 tstart = time(NULL);
98 /* global program name */
99 progname = argv[0];
100 /* feature check only? */
101 strcat(RFeatureList, RXPICT_FEATURES);
102 if (argc > 1 && !strcmp(argv[1], "-features"))
103 return feature_status(argc-2, argv+2);
104 /* option city */
105 for (i = 1; i < argc; i++) {
106 /* expand arguments */
107 while ((rval = expandarg(&argc, &argv, i)) > 0)
108 ;
109 if (rval < 0) {
110 sprintf(errmsg, "cannot expand '%s'", argv[i]);
111 error(SYSTEM, errmsg);
112 }
113 if (argv[i] == NULL || argv[i][0] != '-')
114 break; /* break from options */
115 if (!strcmp(argv[i], "-version")) {
116 puts(VersionID);
117 quit(0);
118 }
119 if (!strcmp(argv[i], "-defaults") ||
120 !strcmp(argv[i], "-help")) {
121 printdefaults();
122 quit(0);
123 }
124 rval = getrenderopt(argc-i, argv+i);
125 if (rval >= 0) {
126 i += rval;
127 continue;
128 }
129 rval = getviewopt(&ourview, argc-i, argv+i);
130 if (rval >= 0) {
131 i += rval;
132 continue;
133 }
134 /* rxpict options */
135 switch (argv[i][1]) {
136 case 'v': /* view file */
137 if (argv[i][2] != 'f')
138 goto badopt;
139 check(3,"s");
140 rval = viewfile(argv[++i], &ourview, NULL);
141 if (rval < 0) {
142 sprintf(errmsg,
143 "cannot open view file \"%s\"",
144 argv[i]);
145 error(SYSTEM, errmsg);
146 } else if (rval == 0) {
147 sprintf(errmsg,
148 "bad view file \"%s\"",
149 argv[i]);
150 error(USER, errmsg);
151 }
152 break;
153 case 'n': /* number of processes */
154 check(2,"i");
155 nproc = atoi(argv[++i]);
156 if (nproc < 0 && (nproc += RadSimulManager::GetNCores()) <= 0)
157 nproc = 1;
158 break;
159 case 'f': /* output format */
160 if ((argv[i][2] != 'c') & (argv[i][2] != 'f')
161 || argv[i][3])
162 goto badopt;
163 outfmt = argv[i][2];
164 break;
165 case 'p': /* pixel */
166 switch (argv[i][2]) {
167 case 's': /* sample */
168 check(3,"i");
169 psample = atoi(argv[++i]);
170 if (psample < 1) psample = 1;
171 break;
172 case 't': /* threshold */
173 check(3,"f");
174 maxdiff = atof(argv[++i]);
175 break;
176 case 'j': /* jitter */
177 check(3,"f");
178 dstrpix = atof(argv[++i]);
179 break;
180 case 'a': /* aspect */
181 check(3,"f");
182 pixaspect = atof(argv[++i]);
183 break;
184 case 'm': /* motion */
185 check(3,"f");
186 mblur = atof(argv[++i]);
187 mblur *= (mblur > 0);
188 break;
189 case 'd': /* aperture */
190 check(3,"f");
191 dblur = atof(argv[++i]);
192 dblur *= (dblur > 0);
193 break;
194 case 'R': /* standard RGB output */
195 if (strcmp(argv[i]+2, "RGB"))
196 goto badopt;
197 myRPmanager.prims = stdprims;
198 dtype = RDTnewCT(dtype, RDTrgbe);
199 break;
200 case 'X': /* XYZ output */
201 if (strcmp(argv[i]+2, "XYZ"))
202 goto badopt;
203 myRPmanager.prims = xyzprims;
204 dtype = RDTnewCT(dtype, RDTxyze);
205 break;
206 case 'c': /* chromaticities */
207 check(3,"ffffffff");
208 rval = 0;
209 for (int j = 0; j < 8; j++) {
210 our_prims[0][j] = atof(argv[++i]);
211 rval |= fabs(our_prims[0][j]-stdprims[0][j]) > .001;
212 }
213 if (rval) {
214 if (!colorprimsOK(our_prims))
215 error(USER, "illegal primary chromaticities");
216 myRPmanager.prims = our_prims;
217 } else
218 myRPmanager.prims = stdprims;
219 dtype = RDTnewCT(dtype, RDTrgbe);
220 break;
221 default:
222 goto badopt;
223 }
224 break;
225 case 'd': /* reference depth */
226 if (argv[i][2] || !myRPmanager.SetReferenceDepth(argv[++i]))
227 goto badopt;
228 dtype = RDTnewDT(dtype, RDTdshort);
229 break;
230 case 'x': /* x resolution */
231 check(2,"i");
232 hresolu = atoi(argv[++i]);
233 break;
234 case 'y': /* y resolution */
235 check(2,"i");
236 vresolu = atoi(argv[++i]);
237 break;
238 case 'S': /* start index */
239 check(2,"i");
240 seqstart = atoi(argv[++i]);
241 seqstart *= (seqstart > 0);
242 break;
243 case 'o': /* output file */
244 check(2,"s");
245 outfile = argv[++i];
246 break;
247 case 'z': /* z file */
248 check(2,"s");
249 zfile = argv[++i];
250 break;
251 case 'r': /* recover file */
252 if (argv[i][2] == 'o') { /* +output */
253 check(3,"s");
254 outfile = argv[i+1];
255 } else
256 check(2,"s");
257 recover = argv[++i];
258 break;
259 #if MAXCSAMP>3
260 case 'c': /* output spectral results */
261 if (argv[i][2] != 'o')
262 goto badopt;
263 rval = (myRPmanager.prims == NULL);
264 check_bool(3,rval);
265 if (rval)
266 myRPmanager.prims = NULL;
267 else if (myRPmanager.prims == NULL)
268 myRPmanager.prims = stdprims;
269 dtype = RDTnewCT(dtype, rval ? RDTscolr : RDTrgbe);
270 break;
271 #endif
272 case 't': /* timer */
273 check(2,"i");
274 ralrm = atoi(argv[++i]);
275 break;
276 case 'w': /* warnings */
277 rval = erract[WARNING].pf != NULL;
278 check_bool(2,rval);
279 if (rval) erract[WARNING].pf = wputs;
280 else erract[WARNING].pf = NULL;
281 break;
282 case 'e': /* error file */
283 check(2,"s");
284 errfile = argv[++i];
285 break;
286 default:
287 goto badopt;
288 }
289 }
290 if (maxdiff <= FTINY) /* check for useless sampling */
291 psample = 1;
292 if (zfile == NULL) /* set up depth output */
293 dtype = RDTnewDT(dtype, RDTnone);
294 else if (!RDTdepthT(dtype))
295 dtype = RDTnewDT(dtype, RDTdfloat);
296 /* check pixel output type */
297 if ((myRPmanager.prims == NULL) & (NCSAMP == 3)) {
298 myRPmanager.prims = stdprims;
299 dtype = RDTnewCT(dtype, RDTrgbe);
300 }
301 if (outfmt == 'f')
302 switch (RDTcolorT(dtype)) {
303 case RDTrgbe:
304 dtype = RDTnewCT(dtype, RDTrgb);
305 break;
306 case RDTxyze:
307 dtype = RDTnewCT(dtype, RDTxyz);
308 break;
309 case RDTscolr:
310 dtype = RDTnewCT(dtype, RDTscolor);
311 break;
312 case RDTrgb:
313 case RDTxyz:
314 case RDTscolor:
315 break;
316 default:
317 error(INTERNAL, "botched color output type");
318 }
319 /* set up signal handling */
320 sigdie(SIGINT, "Interrupt");
321 #ifdef SIGHUP
322 sigdie(SIGHUP, "Hangup");
323 #endif
324 sigdie(SIGTERM, "Terminate");
325 #ifdef SIGPIPE
326 sigdie(SIGPIPE, "Broken pipe");
327 #endif
328 #ifdef SIGALRM
329 sigdie(SIGALRM, "Alarm clock");
330 #endif
331 #ifdef SIGXCPU
332 sigdie(SIGXCPU, "CPU limit exceeded");
333 sigdie(SIGXFSZ, "File size exceeded");
334 #endif
335 /* open error file */
336 if (errfile != NULL) {
337 if (freopen(errfile, "a", stderr) == NULL)
338 quit(2);
339 fprintf(stderr, "**************\n*** PID %5d: ",
340 getpid());
341 printargs(argc, argv, stderr);
342 putc('\n', stderr);
343 fflush(stderr);
344 }
345 #ifdef NICE
346 nice(NICE); /* lower priority */
347 #endif
348 if (i < argc-1)
349 goto badopt;
350 // load octree
351 if (!myRPmanager.LoadOctree(argv[i]))
352 error(USER, "missing octree argument");
353 // add new header info
354 myRPmanager.AddHeader(i, argv);
355 {
356 char buf[128] = "SOFTWARE= ";
357 strcpy(buf+10, VersionID);
358 myRPmanager.AddHeader(buf);
359 }
360 // start our engines
361 nproc = myRPmanager.SetThreadCount(nproc);
362 // batch render picture(s)
363 rpict(seqstart, outfile, zfile, recover);
364
365 quit(0); // clean up and exit
366
367 badopt:
368 sprintf(errmsg, "command line error at '%s'", argv[i]);
369 error(USER, errmsg);
370 return 1; /* pro forma return */
371
372 #undef check
373 #undef check_bool
374 }
375
376
377 void
378 wputs( /* warning output function */
379 const char *s
380 )
381 {
382 int lasterrno = errno;
383 eputs(s);
384 errno = lasterrno;
385 }
386
387
388 void
389 eputs( /* put string to stderr */
390 const char *s
391 )
392 {
393 static int midline = 0;
394
395 if (!*s)
396 return;
397 if (!midline++) {
398 fputs(progname, stderr);
399 fputs(": ", stderr);
400 }
401 fputs(s, stderr);
402 if (s[strlen(s)-1] == '\n') {
403 fflush(stderr);
404 midline = 0;
405 }
406 }
407
408
409 static void
410 onsig( /* fatal signal */
411 int signo
412 )
413 {
414 static int gotsig = 0;
415
416 if (gotsig++) /* two signals and we're gone! */
417 _exit(signo);
418
419 #ifdef SIGALRM /* XXX how critical is this? */
420 alarm(30); /* allow 30 seconds to clean up */
421 signal(SIGALRM, SIG_DFL); /* make certain we do die */
422 #endif
423 eputs("signal - ");
424 eputs(sigerr[signo]);
425 eputs("\n");
426 quit(3);
427 }
428
429
430 static void
431 sigdie( /* set fatal signal */
432 int signo,
433 const char *msg
434 )
435 {
436 if (signal(signo, onsig) == SIG_IGN)
437 signal(signo, SIG_IGN);
438 sigerr[signo] = msg;
439 }
440
441
442 static void
443 printdefaults(void) /* print default values to stdout */
444 {
445 printf("-n %-2d\t\t\t\t# number of rendering processes\n", nproc);
446 printf("-vt%c\t\t\t\t# view type %s\n", ourview.type,
447 ourview.type==VT_PER ? "perspective" :
448 ourview.type==VT_PAR ? "parallel" :
449 ourview.type==VT_HEM ? "hemispherical" :
450 ourview.type==VT_ANG ? "angular" :
451 ourview.type==VT_CYL ? "cylindrical" :
452 ourview.type==VT_PLS ? "planisphere" :
453 "unknown");
454 printf("-vp %f %f %f\t# view point\n",
455 ourview.vp[0], ourview.vp[1], ourview.vp[2]);
456 printf("-vd %f %f %f\t# view direction\n",
457 ourview.vdir[0], ourview.vdir[1], ourview.vdir[2]);
458 printf("-vu %f %f %f\t# view up\n",
459 ourview.vup[0], ourview.vup[1], ourview.vup[2]);
460 printf("-vh %f\t\t\t# view horizontal size\n", ourview.horiz);
461 printf("-vv %f\t\t\t# view vertical size\n", ourview.vert);
462 printf("-vo %f\t\t\t# view fore clipping plane\n", ourview.vfore);
463 printf("-va %f\t\t\t# view aft clipping plane\n", ourview.vaft);
464 printf("-vs %f\t\t\t# view shift\n", ourview.hoff);
465 printf("-vl %f\t\t\t# view lift\n", ourview.voff);
466 printf("-x %-9d\t\t\t# x resolution\n", hresolu);
467 printf("-y %-9d\t\t\t# y resolution\n", vresolu);
468 if (myRPmanager.prims == stdprims)
469 printf("-pRGB\t\t\t\t# standard RGB color output\n");
470 else if (myRPmanager.prims == xyzprims)
471 printf("-pXYZ\t\t\t\t# CIE XYZ color output\n");
472 else if (myRPmanager.prims != NULL)
473 printf("-pc %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\t# output color primaries and white point\n",
474 myRPmanager.prims[RED][0], myRPmanager.prims[RED][1],
475 myRPmanager.prims[GRN][0], myRPmanager.prims[GRN][1],
476 myRPmanager.prims[BLU][0], myRPmanager.prims[BLU][1],
477 myRPmanager.prims[WHT][0], myRPmanager.prims[WHT][1]);
478 if (NCSAMP > 3)
479 printf(myRPmanager.prims != NULL ? "-co-\t\t\t\t# output tristimulus colors\n" :
480 "-co+\t\t\t\t# output spectral values\n");
481 printf("-pa %f\t\t\t# pixel aspect ratio\n", pixaspect);
482 printf("-pj %f\t\t\t# pixel jitter\n", dstrpix);
483 printf("-pm %f\t\t\t# pixel motion\n", mblur);
484 printf("-pd %f\t\t\t# pixel depth-of-field\n", dblur);
485 printf("-ps %-9d\t\t\t# pixel sample\n", psample);
486 printf("-pt %f\t\t\t# pixel threshold\n", maxdiff);
487 printf("-t %-9d\t\t\t# time between reports\n", ralrm);
488 printf(erract[WARNING].pf != NULL ?
489 "-w+\t\t\t\t# warning messages on\n" :
490 "-w-\t\t\t\t# warning messages off\n");
491 print_rdefaults();
492 }
493
494
495 // my progress report call-back
496 static void
497 progReporter(double pct)
498 {
499 static time_t lastReportTime = 0;
500 time_t tnow = time(NULL);
501
502 if (tnow - lastReportTime < ralrm)
503 return; // too soon, my Precious...
504
505 sprintf(errmsg, "%7.3f%% done after %7.3f hours\n", pct, (tnow-tstart)/3600.);
506 eputs(errmsg);
507 // reset timer at 100%
508 lastReportTime = tnow * (pct < 100.-FTINY);
509 }
510
511
512 // Principal function for rpict execution (prototype in ray.h for some reason)
513 void
514 rpict(int seq, char *pout, char *zout, char *prvr)
515 /*
516 * If seq is greater than zero, then we will render a sequence of
517 * images based on view parameter strings read from the standard input.
518 * If pout is NULL, then all images will be sent to the standard ouput.
519 * If seq is greater than zero and prvr is an integer, then it is the
520 * frame number at which rendering should begin. Preceeding view parameter
521 * strings will be skipped in the input.
522 * Note that pout and zout should contain %d format specifications for
523 * sequenced file naming.
524 */
525 {
526 char fbuf[256], dbuf[256];
527
528 if (!zout ^ !RDTdepthT(dtype))
529 error(INTERNAL, "output depth type requires spec and vice versa");
530
531 if (prvr && isint(prvr)) { // sequence recovery?
532 if (!seq)
533 error(USER, "sequence recovery requires starting frame");
534 if (!pout)
535 error(USER, "need output spec for sequence recovery");
536 int rno = atoi(prvr);
537 if (rno < seq)
538 error(USER, "recovery frame before starting frame");
539 while (seq <= rno) {
540 if (!fgets(fbuf, sizeof(fbuf), stdin))
541 error(USER, "recovery frame past end of sequence");
542 seq += (isview(fbuf) && sscanview(&ourview, fbuf) > 0);
543 }
544 sprintf(prvr=fbuf, pout, rno);
545 }
546 if (ralrm > 0)
547 myRPmanager.prCB = progReporter;
548 else
549 myRPmanager.prCB = NULL;
550 if (prvr) { // recovering partial render?
551 if ((seq > 0) & (prvr != fbuf))
552 error(USER, "recover spec must be number in sequence");
553 if (zout) sprintf(dbuf, zout, seq);
554 if (ralrm > 0) {
555 sprintf(errmsg, "resuming partial rendering '%s'\n", prvr);
556 eputs(errmsg);
557 }
558 dtype = myRPmanager.ResumeFrame(prvr, zout ? dbuf : zout);
559 if (!dtype)
560 error(USER, "ResumeFrame() failed");
561 if (!seq)
562 return; // all done if we're running a sequence
563 }
564 do {
565 if (prvr) // have view from sequence recovery?
566 prvr = NULL;
567 else if (seq) { // else read next view in sequence
568 while (fgets(fbuf, sizeof(fbuf), stdin))
569 if (isview(fbuf) && sscanview(&ourview, fbuf) > 0)
570 break;
571 if (feof(stdin))
572 return; // reached end of view input
573 }
574 // get (indexed) output file name(s)
575 if (pout) sprintf(fbuf, pout, seq);
576 if (zout) sprintf(dbuf, zout, seq);
577
578 if (ralrm > 0) { // start report if requested
579 if (myRPmanager.NThreads() > 1)
580 sprintf(errmsg, "%2d processes ", myRPmanager.NThreads());
581 else
582 sprintf(errmsg, "PID %6d ", getpid());
583 eputs(errmsg);
584 if (seq) {
585 sprintf(errmsg, "rendering frame %5d ", seq);
586 eputs(errmsg);
587 } else
588 eputs("rendering picture ");
589 if (pout) {
590 sprintf(errmsg, "to '%s'\n", fbuf);
591 eputs(errmsg);
592 } else
593 eputs("to stdout\n");
594 if (zout) {
595 sprintf(errmsg, "\twith %s depth map in '%s'\n",
596 RDTdepthT(dtype)==RDTdshort ?
597 "encoded 16-bit" : "raw float",
598 dbuf);
599 eputs(errmsg);
600 }
601 }
602 // set up view and size
603 int xydim[2] = {hresolu, vresolu};
604 if (!myRPmanager.NewFrame(ourview, xydim, &pixaspect))
605 error(USER, "NewFrame() failed");
606
607 myRPmanager.frameNo = seq;
608
609 errno = 0; // render frame, skipping if it exists
610 if (!myRPmanager.RenderFrame(pout ? fbuf : pout, dtype,
611 zout ? dbuf : zout)
612 && !seq | (errno != EEXIST))
613 error(USER, "RenderFrame() failed");
614 } while (seq++); // all done if we're running a sequence
615 }