ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/util/ranimate.c
Revision: 2.60
Committed: Sat Jun 7 05:09:46 2025 UTC (2 weeks ago) by greg
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 2.59: +1 -2 lines
Log Message:
refactor: Put some declarations into "paths.h" and included in "platform.h"

File Contents

# User Rev Content
1 greg 2.1 #ifndef lint
2 greg 2.60 static const char RCSid[] = "$Id: ranimate.c,v 2.59 2025/06/03 21:31:51 greg Exp $";
3 greg 2.1 #endif
4     /*
5     * Radiance animation control program
6 greg 2.29 *
7     * The main difference between this program and ranimove is that
8     * we have many optimizations here for camera motion in static
9     * environments, calling rpict and pinterp on multiple processors,
10     * where ranimove puts its emphasis on object motion, and does
11     * not use any external programs for image generation.
12     *
13     * See the ranimate(1) man page for further details.
14     */
15    
16 greg 2.31 #include "copyright.h"
17 greg 2.1
18 schorsch 2.43 #include <stdio.h>
19 greg 2.2 #include <ctype.h>
20 greg 2.1 #include <sys/stat.h>
21 schorsch 2.43 #include <time.h>
22 schorsch 2.46 #include <signal.h>
23 schorsch 2.39
24 schorsch 2.43 #include "platform.h"
25 schorsch 2.50 #include "rtprocess.h"
26 greg 2.47 #include "standard.h"
27 greg 2.1 #include "view.h"
28     #include "vars.h"
29 greg 2.2 #include "netproc.h"
30 greg 2.29 /* default blur samples */
31     #ifndef DEF_NBLUR
32     #define DEF_NBLUR 5
33     #endif
34 greg 2.10 /* default remote shell */
35 greg 2.49 #ifndef REMSH
36     #define REMSH "ssh"
37     #endif
38 gwlarson 2.27 /* input variables (alphabetical by name) */
39     #define ANIMATE 0 /* animation command */
40     #define ARCHIVE 1 /* archiving command */
41     #define BASENAME 2 /* output image base name */
42 greg 2.48 #define DBLUR 3 /* depth of field blur */
43     #define DIRECTORY 4 /* working (sub)directory */
44     #define DISKSPACE 5 /* how much disk space to use */
45     #define END 6 /* ending frame number */
46     #define EXPOSURE 7 /* how to compute exposure */
47     #define HOST 8 /* rendering host machine */
48     #define INTERP 9 /* # frames to interpolate */
49     #define MBLUR 10 /* motion blur parameters */
50     #define NEXTANIM 11 /* next animation file */
51     #define OCTREE 12 /* octree file name */
52     #define OVERSAMP 13 /* # times to oversample image */
53     #define PFILT 14 /* pfilt options */
54     #define PINTERP 15 /* pinterp options */
55     #define RENDER 16 /* rendering options */
56     #define RESOLUTION 17 /* desired final resolution */
57     #define RIF 18 /* rad input file */
58     #define RSH 19 /* remote shell script or program */
59     #define RTRACE 20 /* use rtrace with pinterp? */
60     #define START 21 /* starting frame number */
61     #define TRANSFER 22 /* frame transfer command */
62     #define VIEWFILE 23 /* animation frame views */
63 greg 2.1
64 greg 2.48 int NVARS = 24; /* total number of variables */
65 greg 2.1
66     VARIABLE vv[] = { /* variable-value pairs */
67 gwlarson 2.27 {"ANIMATE", 2, 0, NULL, onevalue},
68     {"ARCHIVE", 2, 0, NULL, onevalue},
69 greg 2.58 {"BASENAME", 3, 0, NULL, strvalue},
70 greg 2.48 {"DBLUR", 2, 0, NULL, onevalue},
71 greg 2.58 {"DIRECTORY", 3, 0, NULL, strvalue},
72 gwlarson 2.27 {"DISKSPACE", 3, 0, NULL, fltvalue},
73 greg 2.1 {"END", 2, 0, NULL, intvalue},
74 gwlarson 2.27 {"EXPOSURE", 3, 0, NULL, onevalue},
75     {"host", 4, 0, NULL, NULL},
76     {"INTERPOLATE", 3, 0, NULL, intvalue},
77     {"MBLUR", 2, 0, NULL, onevalue},
78 greg 2.58 {"NEXTANIM", 3, 0, NULL, strvalue},
79     {"OCTREE", 3, 0, NULL, strvalue},
80 greg 2.5 {"OVERSAMPLE", 2, 0, NULL, fltvalue},
81 gwlarson 2.27 {"pfilt", 2, 0, NULL, catvalues},
82     {"pinterp", 2, 0, NULL, catvalues},
83     {"render", 3, 0, NULL, catvalues},
84 greg 2.1 {"RESOLUTION", 3, 0, NULL, onevalue},
85 greg 2.58 {"RIF", 3, 0, NULL, strvalue},
86 greg 2.10 {"RSH", 3, 0, NULL, onevalue},
87 gwlarson 2.27 {"RTRACE", 2, 0, NULL, boolvalue},
88     {"START", 2, 0, NULL, intvalue},
89     {"TRANSFER", 2, 0, NULL, onevalue},
90 greg 2.58 {"VIEWFILE", 2, 0, NULL, strvalue},
91 greg 2.1 };
92    
93     #define SFNAME "STATUS" /* status file name */
94    
95     struct {
96     char host[64]; /* control host name */
97 schorsch 2.50 RT_PID pid; /* control process id */
98 greg 2.1 char cfname[128]; /* control file name */
99     int rnext; /* next frame to render */
100     int fnext; /* next frame to filter */
101     int tnext; /* next frame to transfer */
102     } astat; /* animation status */
103    
104     char *cfname; /* our control file name */
105    
106     int nowarn = 0; /* turn warnings off? */
107     int silent = 0; /* silent mode? */
108     int noaction = 0; /* take no action? */
109    
110 greg 2.10 char *remsh; /* remote shell program/script */
111 greg 2.21 char rendopt[2048]; /* rendering options */
112 greg 2.1 char rresopt[32]; /* rendering resolution options */
113     char fresopt[32]; /* filter resolution options */
114     int pfiltalways; /* always use pfilt? */
115    
116 greg 2.7 char arcargs[10240]; /* files to archive */
117     char *arcfirst, *arcnext; /* pointers to first and next argument */
118    
119 greg 2.2 struct pslot {
120 schorsch 2.50 RT_PID pid; /* process ID (0 if empty) */
121 greg 2.2 int fout; /* output frame number */
122     int (*rcvf)(); /* recover function */
123     } *pslot; /* process slots */
124     int npslots; /* number of process slots */
125    
126 greg 2.4 #define phostname(ps) ((ps)->hostname[0] ? (ps)->hostname : astat.host)
127 greg 2.18 PSERVER *lastpserver; /* last process server with error */
128    
129 schorsch 2.50 static struct pslot * findpslot(RT_PID pid);
130 schorsch 2.43 static void checkdir(void);
131     static VIEW * getview(int n);
132    
133     static char * dirfile(char *df, register char *path);
134     static char * getexp(int n);
135 greg 2.48 static int getblur(double *mbf, double *dbf);
136 schorsch 2.43 static int getastat(void);
137     static void getradfile(char *rfargs);
138     static void badvalue(int vc);
139     static int rmfile(char *fn);
140     static int runcom(char *cs);
141     static int pruncom(char *com, char *ppins, int maxcopies);
142     static void bwait(int ncoms);
143 schorsch 2.50 static RT_PID bruncom(char *com, int fout, int (*rf)());
144 schorsch 2.43 static int serverdown(void);
145 schorsch 2.46 static pscompfunc donecom;
146 schorsch 2.43 static int countviews(void);
147     static int dofilt(int frame, int rvr);
148     static void archive(void);
149     static int frecover(int frame);
150     static int recover(int frame);
151     static void sethosts(void);
152     static void walkwait(int first, int last, char *vfn);
153     static void animrend(int frame, VIEW *vp);
154     static void transferframes(void);
155     static void filterframes(void);
156     static void renderframes(int nframes);
157     static void animate(void);
158     static void setdefaults(void);
159     static void putastat(void);
160 greg 2.1
161 greg 2.12
162 schorsch 2.46 int
163     main(
164     int argc,
165     char *argv[]
166     )
167 greg 2.1 {
168     int explicate = 0;
169     int i;
170 greg 2.59 /* set global progname */
171     fixargv0(argv[0]);
172     /* get arguments */
173 greg 2.1 for (i = 1; i < argc && argv[i][0] == '-'; i++)
174     switch (argv[i][1]) {
175     case 'e': /* print variables */
176     explicate++;
177     break;
178     case 'w': /* turn off warnings */
179     nowarn++;
180     break;
181     case 's': /* silent mode */
182     silent++;
183     break;
184     case 'n': /* take no action */
185     noaction++;
186     break;
187     default:
188     goto userr;
189     }
190     if (i != argc-1)
191     goto userr;
192     cfname = argv[i];
193     /* load variables */
194     loadvars(cfname);
195 greg 2.22 /* check variables */
196     checkvalues();
197 greg 2.1 /* did we get DIRECTORY? */
198     checkdir();
199     /* check status */
200     if (getastat() < 0) {
201     fprintf(stderr, "%s: exiting\n", progname);
202     quit(1);
203     }
204     /* pfilt always if options given */
205     pfiltalways = vdef(PFILT);
206     /* load RIF if any */
207     if (vdef(RIF))
208     getradfile(vval(RIF));
209     /* set defaults */
210     setdefaults();
211     /* print variables */
212     if (explicate)
213     printvars(stdout);
214 greg 2.2 /* set up process servers */
215     sethosts();
216 greg 2.1 /* run animation */
217     animate();
218     /* all done */
219     if (vdef(NEXTANIM)) {
220 greg 2.51 char *fullp;
221 greg 2.1 argv[i] = vval(NEXTANIM); /* just change input file */
222     if (!silent)
223     printargs(argc, argv, stdout);
224 greg 2.51 fflush(stdout);
225     if ((fullp = getpath(argv[0],getenv("PATH"),X_OK)) == NULL)
226     fprintf(stderr, "%s: command not found\n", argv[0]);
227 greg 2.11 else
228 greg 2.51 execv(fullp, argv);
229 greg 2.11 quit(1);
230 greg 2.1 }
231     quit(0);
232 greg 2.51 return(0); /* pro forma return */
233 greg 2.1 userr:
234     fprintf(stderr, "Usage: %s [-s][-n][-w][-e] anim_file\n", progname);
235     quit(1);
236 schorsch 2.46 return 1; /* pro forma return */
237 greg 2.1 }
238    
239    
240 schorsch 2.43 static int
241     getastat(void) /* check/set animation status */
242 greg 2.1 {
243 greg 2.12 char sfname[256];
244 greg 2.1 FILE *fp;
245    
246 greg 2.12 sprintf(sfname, "%s/%s", vval(DIRECTORY), SFNAME);
247     if ((fp = fopen(sfname, "r")) == NULL) {
248 greg 2.1 if (errno != ENOENT) {
249 greg 2.12 perror(sfname);
250 greg 2.1 return(-1);
251     }
252     astat.rnext = astat.fnext = astat.tnext = 0;
253     goto setours;
254     }
255     if (fscanf(fp, "Control host: %s\n", astat.host) != 1)
256     goto fmterr;
257     if (fscanf(fp, "Control PID: %d\n", &astat.pid) != 1)
258     goto fmterr;
259     if (fscanf(fp, "Control file: %s\n", astat.cfname) != 1)
260     goto fmterr;
261     if (fscanf(fp, "Next render: %d\n", &astat.rnext) != 1)
262     goto fmterr;
263     if (fscanf(fp, "Next filter: %d\n", &astat.fnext) != 1)
264     goto fmterr;
265     if (fscanf(fp, "Next transfer: %d\n", &astat.tnext) != 1)
266     goto fmterr;
267     fclose(fp);
268     if (astat.pid != 0) { /* thinks it's still running */
269 greg 2.9 if (strcmp(myhostname(), astat.host)) {
270 greg 2.1 fprintf(stderr,
271     "%s: process %d may still be running on host %s\n",
272     progname, astat.pid, astat.host);
273     return(-1);
274     }
275     if (kill(astat.pid, 0) != -1 || errno != ESRCH) {
276     fprintf(stderr, "%s: process %d is still running\n",
277     progname, astat.pid);
278     return(-1);
279     }
280     /* assume it is dead */
281     }
282 greg 2.20 if (strcmp(cfname, astat.cfname) && astat.pid != 0) { /* other's */
283 greg 2.1 fprintf(stderr, "%s: unfinished job \"%s\"\n",
284     progname, astat.cfname);
285     return(-1);
286     }
287 greg 2.12 /* check control file mods. */
288     if (!nowarn && fdate(cfname) > fdate(sfname))
289     fprintf(stderr,
290     "%s: warning - control file modified since last run\n",
291     progname);
292 greg 2.1 setours: /* set our values */
293 greg 2.9 strcpy(astat.host, myhostname());
294 greg 2.1 astat.pid = getpid();
295     strcpy(astat.cfname, cfname);
296     return(0);
297     fmterr:
298     fprintf(stderr, "%s: format error in status file \"%s\"\n",
299 greg 2.12 progname, sfname);
300 greg 2.1 fclose(fp);
301     return(-1);
302     }
303    
304    
305 schorsch 2.43 static void
306     putastat(void) /* put out current status */
307 greg 2.1 {
308     char buf[256];
309     FILE *fp;
310    
311 greg 2.2 if (noaction)
312     return;
313 greg 2.1 sprintf(buf, "%s/%s", vval(DIRECTORY), SFNAME);
314     if ((fp = fopen(buf, "w")) == NULL) {
315     perror(buf);
316     quit(1);
317     }
318     fprintf(fp, "Control host: %s\n", astat.host);
319     fprintf(fp, "Control PID: %d\n", astat.pid);
320     fprintf(fp, "Control file: %s\n", astat.cfname);
321     fprintf(fp, "Next render: %d\n", astat.rnext);
322     fprintf(fp, "Next filter: %d\n", astat.fnext);
323     fprintf(fp, "Next transfer: %d\n", astat.tnext);
324     fclose(fp);
325     }
326    
327    
328 schorsch 2.43 static void
329     checkdir(void) /* make sure we have our directory */
330 greg 2.1 {
331     struct stat stb;
332    
333     if (!vdef(DIRECTORY)) {
334     fprintf(stderr, "%s: %s undefined\n",
335     progname, vnam(DIRECTORY));
336     quit(1);
337     }
338     if (stat(vval(DIRECTORY), &stb) == -1) {
339     if (errno == ENOENT && mkdir(vval(DIRECTORY), 0777) == 0)
340     return;
341     perror(vval(DIRECTORY));
342     quit(1);
343     }
344     if (!(stb.st_mode & S_IFDIR)) {
345     fprintf(stderr, "%s: not a directory\n", vval(DIRECTORY));
346     quit(1);
347     }
348     }
349    
350    
351 schorsch 2.43 static void
352     setdefaults(void) /* set default values */
353 greg 2.1 {
354 greg 2.10 extern char *atos();
355 greg 2.19 int decades;
356 greg 2.1 char buf[256];
357    
358 greg 2.2 if (vdef(ANIMATE)) {
359     vval(OCTREE) = NULL;
360     vdef(OCTREE) = 0;
361     } else if (!vdef(OCTREE)) {
362 greg 2.1 fprintf(stderr, "%s: either %s or %s must be defined\n",
363     progname, vnam(OCTREE), vnam(ANIMATE));
364     quit(1);
365     }
366     if (!vdef(VIEWFILE)) {
367     fprintf(stderr, "%s: %s undefined\n", progname, vnam(VIEWFILE));
368     quit(1);
369     }
370 greg 2.2 if (!vdef(HOST)) {
371     vval(HOST) = LHOSTNAME;
372     vdef(HOST)++;
373     }
374 greg 2.1 if (!vdef(START)) {
375     vval(START) = "1";
376     vdef(START)++;
377     }
378     if (!vdef(END)) {
379 greg 2.5 sprintf(buf, "%d", countviews()+vint(START)-1);
380 greg 2.1 vval(END) = savqstr(buf);
381     vdef(END)++;
382     }
383 greg 2.5 if (vint(END) < vint(START)) {
384     fprintf(stderr, "%s: ending frame less than starting frame\n",
385     progname);
386     quit(1);
387     }
388 greg 2.1 if (!vdef(BASENAME)) {
389 greg 2.19 decades = (int)log10((double)vint(END)) + 1;
390     if (decades < 3) decades = 3;
391     sprintf(buf, "%s/frame%%0%dd", vval(DIRECTORY), decades);
392 greg 2.1 vval(BASENAME) = savqstr(buf);
393     vdef(BASENAME)++;
394     }
395     if (!vdef(RESOLUTION)) {
396     vval(RESOLUTION) = "640";
397     vdef(RESOLUTION)++;
398     }
399     if (!vdef(OVERSAMP)) {
400     vval(OVERSAMP) = "2";
401     vdef(OVERSAMP)++;
402     }
403     if (!vdef(INTERP)) {
404     vval(INTERP) = "0";
405     vdef(INTERP)++;
406     }
407     if (!vdef(MBLUR)) {
408     vval(MBLUR) = "0";
409     vdef(MBLUR)++;
410     }
411     if (!vdef(RTRACE)) {
412     vval(RTRACE) = "F";
413     vdef(RTRACE)++;
414     }
415     if (!vdef(DISKSPACE)) {
416     if (!nowarn)
417     fprintf(stderr,
418 greg 2.55 "%s: warning - no %s setting, assuming 1000 Mbytes available\n",
419 greg 2.1 progname, vnam(DISKSPACE));
420 greg 2.55 vval(DISKSPACE) = "1000";
421 greg 2.1 vdef(DISKSPACE)++;
422     }
423 greg 2.10 if (!vdef(RSH)) {
424     vval(RSH) = REMSH;
425     vdef(RSH)++;
426     }
427     /* locate remote shell program */
428     atos(buf, sizeof(buf), vval(RSH));
429     if ((remsh = getpath(buf, getenv("PATH"), X_OK)) != NULL)
430     remsh = savqstr(remsh);
431     else
432     remsh = vval(RSH); /* will generate error if used */
433    
434 greg 2.1 /* append rendering options */
435     if (vdef(RENDER))
436     sprintf(rendopt+strlen(rendopt), " %s", vval(RENDER));
437     }
438    
439    
440 schorsch 2.43 static void
441     sethosts(void) /* set up process servers */
442 greg 2.2 {
443     extern char *iskip();
444     char buf[256], *dir, *uname;
445     int np;
446     register char *cp;
447     int i;
448    
449     npslots = 0;
450     if (noaction)
451     return;
452     for (i = 0; i < vdef(HOST); i++) { /* add each host */
453     dir = uname = NULL;
454     np = 1;
455     strcpy(cp=buf, nvalue(HOST, i)); /* copy to buffer */
456     cp = sskip(cp); /* skip host name */
457     while (isspace(*cp))
458     *cp++ = '\0';
459     if (*cp) { /* has # processes? */
460     np = atoi(cp);
461     if ((cp = iskip(cp)) == NULL || (*cp && !isspace(*cp)))
462     badvalue(HOST);
463     while (isspace(*cp))
464     cp++;
465     if (*cp) { /* has directory? */
466     dir = cp;
467     cp = sskip(cp); /* skip dir. */
468     while (isspace(*cp))
469     *cp++ = '\0';
470     if (*cp) { /* has user? */
471     uname = cp;
472     if (*sskip(cp))
473     badvalue(HOST);
474     }
475     }
476     }
477     if (addpserver(buf, dir, uname, np) == NULL) {
478     if (!nowarn)
479     fprintf(stderr,
480     "%s: cannot execute on host \"%s\"\n",
481     progname, buf);
482     } else
483     npslots += np;
484     }
485     if (npslots == 0) {
486     fprintf(stderr, "%s: no working process servers\n", progname);
487     quit(1);
488     }
489     pslot = (struct pslot *)calloc(npslots, sizeof(struct pslot));
490     if (pslot == NULL) {
491     perror("malloc");
492     quit(1);
493     }
494     }
495    
496 schorsch 2.43 static void
497     getradfile(char *rfargs) /* run rad and get needed variables */
498 greg 2.1 {
499     static short mvar[] = {OCTREE,PFILT,RESOLUTION,EXPOSURE,-1};
500     char combuf[256];
501     register int i;
502     register char *cp;
503 schorsch 2.46 char *pippt = NULL;
504 greg 2.1 /* create rad command */
505     sprintf(rendopt, " @%s/render.opt", vval(DIRECTORY));
506     sprintf(combuf,
507     "rad -v 0 -s -e -w %s OPTFILE=%s | egrep '^[ \t]*(NOMATCH",
508 greg 2.7 rfargs, rendopt+2);
509 greg 2.1 cp = combuf;
510 gregl 2.23 while (*cp) {
511     if (*cp == '|') pippt = cp;
512     cp++;
513     } /* match unset variables */
514 greg 2.1 for (i = 0; mvar[i] >= 0; i++)
515     if (!vdef(mvar[i])) {
516     *cp++ = '|';
517     strcpy(cp, vnam(mvar[i]));
518     while (*cp) cp++;
519 gregl 2.23 pippt = NULL;
520 greg 2.1 }
521 gregl 2.23 if (pippt != NULL)
522 schorsch 2.39 strcpy(pippt, "> " NULL_DEVICE); /* nothing to match */
523 gregl 2.23 else {
524     sprintf(cp, ")[ \t]*=' > %s/radset.var", vval(DIRECTORY));
525     cp += 11; /* point to file name */
526     }
527 gwlarson 2.28 system(combuf); /* ignore exit code */
528 gregl 2.23 if (pippt == NULL) { /* load variables and remove file */
529     loadvars(cp);
530     unlink(cp);
531     }
532 greg 2.1 }
533    
534    
535 schorsch 2.43 static void
536     animate(void) /* run animation */
537 greg 2.1 {
538     int xres, yres;
539     float pa, mult;
540     int frames_batch;
541     register int i;
542     double d1, d2;
543     /* compute rpict resolution */
544     i = sscanf(vval(RESOLUTION), "%d %d %f", &xres, &yres, &pa);
545     mult = vflt(OVERSAMP);
546     if (i == 3) {
547 greg 2.16 sprintf(rresopt, "-x %d -y %d -pa %.3f", (int)(mult*xres),
548 greg 2.1 (int)(mult*yres), pa);
549 greg 2.16 sprintf(fresopt, "-x %d -y %d -pa %.3f", xres, yres, pa);
550 greg 2.1 } else if (i) {
551     if (i == 1) yres = xres;
552     sprintf(rresopt, "-x %d -y %d", (int)(mult*xres),
553     (int)(mult*yres));
554     sprintf(fresopt, "-x %d -y %d -pa 1", xres, yres);
555     } else
556     badvalue(RESOLUTION);
557     /* consistency checks */
558     if (vdef(ANIMATE)) {
559     if (vint(INTERP)) {
560     if (!nowarn)
561     fprintf(stderr,
562     "%s: resetting %s=0 for animation\n",
563     progname, vnam(INTERP));
564     vval(INTERP) = "0";
565     }
566 greg 2.29 if (strcmp(vval(MBLUR),"0")) { /* can't handle this */
567 greg 2.1 if (!nowarn)
568     fprintf(stderr,
569     "%s: resetting %s=0 for animation\n",
570     progname, vnam(MBLUR));
571     vval(MBLUR) = "0";
572     }
573     }
574     /* figure # frames per batch */
575     d1 = mult*xres*mult*yres*4; /* space for orig. picture */
576 greg 2.48 if ((i=vint(INTERP)) || getblur(NULL, NULL) > 1)
577 greg 2.13 d1 += mult*xres*mult*yres*sizeof(float); /* Z-buffer */
578 greg 2.1 d2 = xres*yres*4; /* space for final picture */
579     frames_batch = (i+1)*(vflt(DISKSPACE)*1048576.-d1)/(d1+i*d2);
580     if (frames_batch < i+2) {
581     fprintf(stderr, "%s: insufficient disk space allocated\n",
582     progname);
583     quit(1);
584     }
585 greg 2.7 /* initialize archive argument list */
586 gwlarson 2.25 i = vdef(ARCHIVE) ? strlen(vval(ARCHIVE))+132 : 132;
587 greg 2.7 arcnext = arcfirst = arcargs + i;
588 greg 2.1 /* initialize status file */
589     if (astat.rnext == 0)
590     astat.rnext = astat.fnext = astat.tnext = vint(START);
591     putastat();
592     /* render in batches */
593 greg 2.2 while (astat.tnext <= vint(END)) {
594 greg 2.1 renderframes(frames_batch);
595     filterframes();
596     transferframes();
597     }
598     /* mark status as finished */
599     astat.pid = 0;
600     putastat();
601     /* close open files */
602     getview(0);
603     getexp(0);
604     }
605    
606    
607 schorsch 2.43 static void
608     renderframes(int nframes) /* render next nframes frames */
609 greg 2.1 {
610     static char vendbuf[16];
611     VIEW *vp;
612     FILE *fp = NULL;
613     char vfname[128];
614     int lastframe;
615     register int i;
616    
617     if (astat.tnext < astat.rnext) /* other work to do first */
618     return;
619     /* create batch view file */
620     if (!vdef(ANIMATE)) {
621     sprintf(vfname, "%s/anim.vf", vval(DIRECTORY));
622     if ((fp = fopen(vfname, "w")) == NULL) {
623     perror(vfname);
624     quit(1);
625     }
626     }
627     /* bound batch properly */
628     lastframe = astat.rnext + nframes - 1;
629     if ((lastframe-1) % (vint(INTERP)+1)) /* need even interval */
630     lastframe += vint(INTERP)+1 - ((lastframe-1)%(vint(INTERP)+1));
631     if (lastframe > vint(END)) /* check for end */
632     lastframe = vint(END);
633     /* render each view */
634     for (i = astat.rnext; i <= lastframe; i++) {
635     if ((vp = getview(i)) == NULL) {
636     if (!nowarn)
637     fprintf(stderr,
638     "%s: ran out of views before last frame\n",
639     progname);
640     sprintf(vval(END)=vendbuf, "%d", i-1);
641     lastframe = i - 1;
642     break;
643     }
644     if (vdef(ANIMATE)) /* animate frame */
645     animrend(i, vp);
646     else { /* else record it */
647     fputs(VIEWSTR, fp);
648     fprintview(vp, fp);
649     putc('\n', fp);
650     }
651     }
652     if (vdef(ANIMATE)) /* wait for renderings to finish */
653 greg 2.2 bwait(0);
654 greg 2.1 else { /* else if walk-through */
655     fclose(fp); /* close view file */
656     walkwait(astat.rnext, lastframe, vfname); /* walk it */
657     unlink(vfname); /* remove view file */
658     }
659     astat.rnext = i; /* update status */
660     putastat();
661     }
662    
663    
664 schorsch 2.43 static void
665     filterframes(void) /* catch up with filtering */
666 greg 2.1 {
667     register int i;
668    
669     if (astat.tnext < astat.fnext) /* other work to do first */
670     return;
671     /* filter each view */
672 greg 2.38 for (i = astat.fnext; i < astat.rnext; i++)
673     dofilt(i, 0);
674    
675 greg 2.2 bwait(0); /* wait for filter processes */
676 greg 2.7 archive(); /* archive originals */
677 greg 2.1 astat.fnext = i; /* update status */
678     putastat();
679     }
680    
681    
682 schorsch 2.43 static void
683     transferframes(void) /* catch up with picture transfers */
684 greg 2.1 {
685 gwlarson 2.25 char combuf[10240], *fbase;
686 greg 2.1 register char *cp;
687     register int i;
688    
689     if (astat.tnext >= astat.fnext) /* nothing to do, yet */
690     return;
691     if (!vdef(TRANSFER)) { /* no transfer function -- leave 'em */
692     astat.tnext = astat.fnext;
693     putastat(); /* update status */
694     return;
695     }
696 gwlarson 2.25 strcpy(combuf, "cd "); /* start transfer command */
697     fbase = dirfile(cp = combuf+3, vval(BASENAME));
698     if (*cp) {
699     while (*++cp) ;
700     *cp++ = ';'; *cp++ = ' ';
701     } else
702     cp = combuf;
703     strcpy(cp, vval(TRANSFER));
704     while (*cp) cp++;
705 greg 2.1 /* make argument list */
706     for (i = astat.tnext; i < astat.fnext; i++) {
707     *cp++ = ' ';
708 gwlarson 2.25 sprintf(cp, fbase, i);
709 greg 2.1 while (*cp) cp++;
710 greg 2.54 strcpy(cp, ".hdr");
711 greg 2.1 cp += 4;
712     }
713     if (runcom(combuf)) { /* transfer frames */
714     fprintf(stderr, "%s: error running transfer command\n",
715     progname);
716     quit(1);
717     }
718     astat.tnext = i; /* update status */
719     putastat();
720     }
721    
722    
723 schorsch 2.43 static void
724     animrend( /* start animation frame */
725     int frame,
726     VIEW *vp
727     )
728 greg 2.1 {
729     char combuf[2048];
730     char fname[128];
731    
732     sprintf(fname, vval(BASENAME), frame);
733     strcat(fname, ".unf");
734     if (access(fname, F_OK) == 0)
735     return;
736 greg 2.2 sprintf(combuf, "%s %d | rpict%s%s -w0 %s > %s", vval(ANIMATE), frame,
737 greg 2.1 rendopt, viewopt(vp), rresopt, fname);
738 greg 2.2 bruncom(combuf, frame, recover); /* run in background */
739 greg 2.1 }
740    
741    
742 schorsch 2.43 static void
743     walkwait( /* walk-through frames */
744     int first,
745     int last,
746     char *vfn
747     )
748 greg 2.1 {
749 greg 2.48 double mblurf, dblurf;
750     int nblur = getblur(&mblurf, &dblurf);
751 greg 2.1 char combuf[2048];
752 greg 2.29 register char *inspoint;
753 greg 2.1 register int i;
754    
755     if (!noaction && vint(INTERP)) /* create dummy frames */
756     for (i = first; i <= last; i++)
757     if (i < vint(END) && (i-1) % (vint(INTERP)+1)) {
758     sprintf(combuf, vval(BASENAME), i);
759     strcat(combuf, ".unf");
760     close(open(combuf, O_RDONLY|O_CREAT, 0666));
761     }
762     /* create command */
763 greg 2.35 sprintf(combuf, "rpict%s%s -w0", rendopt,
764     viewopt(getview(first>1 ? first-1 : 1)));
765 greg 2.29 inspoint = combuf;
766     while (*inspoint) inspoint++;
767     if (nblur) {
768 greg 2.48 sprintf(inspoint, " -pm %.3f", mblurf/nblur);
769     while (*inspoint) inspoint++;
770     sprintf(inspoint, " -pd %.3f", dblurf/nblur);
771 greg 2.29 while (*inspoint) inspoint++;
772     }
773     if (nblur > 1 || vint(INTERP)) {
774     sprintf(inspoint, " -z %s.zbf", vval(BASENAME));
775     while (*inspoint) inspoint++;
776     }
777     sprintf(inspoint, " -o %s.unf %s -S %d",
778 greg 2.4 vval(BASENAME), rresopt, first);
779 greg 2.29 while (*inspoint) inspoint++;
780 greg 2.4 sprintf(inspoint, " %s < %s", vval(OCTREE), vfn);
781 greg 2.2 /* run in parallel */
782 greg 2.7 i = (last-first+1)/(vint(INTERP)+1);
783     if (i < 1) i = 1;
784     if (pruncom(combuf, inspoint, i)) {
785 greg 2.2 fprintf(stderr, "%s: error rendering frames %d through %d\n",
786 greg 2.1 progname, first, last);
787     quit(1);
788     }
789     if (!noaction && vint(INTERP)) /* remove dummy frames */
790     for (i = first; i <= last; i++)
791     if (i < vint(END) && (i-1) % (vint(INTERP)+1)) {
792     sprintf(combuf, vval(BASENAME), i);
793     strcat(combuf, ".unf");
794     unlink(combuf);
795     }
796     }
797    
798    
799 schorsch 2.43 static int
800 greg 2.44 recover(int frame) /* recover the specified frame */
801 greg 2.1 {
802 greg 2.2 static int *rfrm; /* list of recovered frames */
803     static int nrfrms = 0;
804 greg 2.48 double mblurf, dblurf;
805     int nblur = getblur(&mblurf, &dblurf);
806 greg 2.1 char combuf[2048];
807     char fname[128];
808     register char *cp;
809 greg 2.2 register int i;
810     /* check to see if recovered already */
811     for (i = nrfrms; i--; )
812     if (rfrm[i] == frame)
813     return(0);
814     /* build command */
815 greg 2.1 sprintf(fname, vval(BASENAME), frame);
816     if (vdef(ANIMATE))
817 greg 2.2 sprintf(combuf, "%s %d | rpict%s -w0",
818 greg 2.1 vval(ANIMATE), frame, rendopt);
819     else
820 greg 2.2 sprintf(combuf, "rpict%s -w0", rendopt);
821 greg 2.29 cp = combuf;
822     while (*cp) cp++;
823     if (nblur) {
824 greg 2.48 sprintf(cp, " -pm %.3f", mblurf/nblur);
825     while (*cp) cp++;
826     sprintf(cp, " -pd %.3f", dblurf/nblur);
827 greg 2.29 while (*cp) cp++;
828     }
829     if (nblur > 1 || vint(INTERP)) {
830 greg 2.1 sprintf(cp, " -z %s.zbf", fname);
831     while (*cp) cp++;
832     }
833     sprintf(cp, " -ro %s.unf", fname);
834     while (*cp) cp++;
835     if (!vdef(ANIMATE)) {
836     *cp++ = ' ';
837     strcpy(cp, vval(OCTREE));
838     }
839 greg 2.2 if (runcom(combuf)) /* run command */
840     return(1);
841     /* add frame to recovered list */
842     if (nrfrms)
843 greg 2.32 rfrm = (int *)realloc((void *)rfrm, (nrfrms+1)*sizeof(int));
844 greg 2.2 else
845     rfrm = (int *)malloc(sizeof(int));
846     if (rfrm == NULL) {
847     perror("malloc");
848 greg 2.1 quit(1);
849     }
850 greg 2.2 rfrm[nrfrms++] = frame;
851     return(0);
852 greg 2.1 }
853    
854    
855 schorsch 2.43 static int
856     frecover(int frame) /* recover filtered frame */
857 greg 2.2 {
858 greg 2.38 if (dofilt(frame, 2) && dofilt(frame, 1))
859 greg 2.2 return(1);
860     return(0);
861     }
862    
863    
864 schorsch 2.43 static void
865     archive(void) /* archive and remove renderings */
866 greg 2.1 {
867 greg 2.2 #define RMCOML (sizeof(rmcom)-1)
868     static char rmcom[] = "rm -f";
869 gwlarson 2.25 char basedir[128];
870     int dlen, alen;
871     register int j;
872 greg 2.1
873 greg 2.7 if (arcnext == arcfirst)
874     return; /* nothing to do */
875 gwlarson 2.25 dirfile(basedir, vval(BASENAME));
876     dlen = strlen(basedir);
877 greg 2.2 if (vdef(ARCHIVE)) { /* run archive command */
878 gwlarson 2.25 alen = strlen(vval(ARCHIVE));
879     if (dlen) {
880     j = alen + dlen + 5;
881     strncpy(arcfirst-j, "cd ", 3);
882     strncpy(arcfirst-j+3, basedir, dlen);
883     (arcfirst-j)[dlen+3] = ';'; (arcfirst-j)[dlen+4] = ' ';
884     } else
885     j = alen;
886     strncpy(arcfirst-alen, vval(ARCHIVE), alen);
887     if (runcom(arcfirst-j)) {
888 greg 2.7 fprintf(stderr, "%s: error running archive command\n",
889     progname);
890 greg 2.2 quit(1);
891     }
892 greg 2.1 }
893 gwlarson 2.25 if (dlen) {
894     j = RMCOML + dlen + 5;
895     strncpy(arcfirst-j, "cd ", 3);
896     strncpy(arcfirst-j+3, basedir, dlen);
897     (arcfirst-j)[dlen+3] = ';'; (arcfirst-j)[dlen+4] = ' ';
898     } else
899     j = RMCOML;
900 greg 2.2 /* run remove command */
901 greg 2.7 strncpy(arcfirst-RMCOML, rmcom, RMCOML);
902 gwlarson 2.25 runcom(arcfirst-j);
903 greg 2.7 arcnext = arcfirst; /* reset argument list */
904 greg 2.2 #undef RMCOML
905 greg 2.1 }
906    
907    
908 schorsch 2.43 static int
909     dofilt( /* filter frame */
910     int frame,
911     int rvr
912     )
913 greg 2.1 {
914 greg 2.6 static int iter = 0;
915 greg 2.48 double mblurf, dblurf;
916     int nblur = getblur(&mblurf, &dblurf);
917 greg 2.38 VIEW *vp = getview(frame);
918     char *ep = getexp(frame);
919 gwlarson 2.25 char fnbefore[128], fnafter[128], *fbase;
920 greg 2.6 char combuf[1024], fname0[128], fname1[128];
921 greg 2.7 int usepinterp, usepfilt, nora_rgbe;
922 greg 2.2 int frseq[2];
923 greg 2.1 /* check what is needed */
924 greg 2.38 if (vp == NULL) {
925     fprintf(stderr,
926     "%s: unexpected error reading view for frame %d\n",
927     progname, frame);
928     quit(1);
929     }
930 greg 2.29 usepinterp = (nblur > 1);
931 schorsch 2.41 usepfilt = pfiltalways | (ep==NULL);
932 greg 2.7 if (ep != NULL && !strcmp(ep, "1"))
933     ep = "+0";
934     nora_rgbe = strcmp(vval(OVERSAMP),"1") || ep==NULL ||
935     *ep != '+' || *ep != '-' || !isint(ep);
936 greg 2.1 /* compute rendered views */
937 greg 2.2 frseq[0] = frame - ((frame-1) % (vint(INTERP)+1));
938     frseq[1] = frseq[0] + vint(INTERP) + 1;
939 gwlarson 2.25 fbase = dirfile(NULL, vval(BASENAME));
940 greg 2.2 if (frseq[1] > vint(END))
941     frseq[1] = vint(END);
942     if (frseq[1] == frame) { /* pfilt only */
943     frseq[0] = frseq[1];
944 greg 2.1 usepinterp = 0; /* update what's needed */
945 greg 2.7 usepfilt |= nora_rgbe;
946     } else if (frseq[0] == frame) { /* no interpolation needed */
947     if (!rvr && frame > 1+vint(INTERP)) { /* archive previous */
948 greg 2.52 if (arcnext - arcargs +
949     strlen(fbase) >= sizeof(arcargs)-8) {
950     fprintf(stderr,
951     "%s: too many filenames in archive command -- reduce %s variable\n",
952     progname, vnam(DISKSPACE));
953     quit(1);
954     }
955 greg 2.7 *arcnext++ = ' ';
956 gwlarson 2.25 sprintf(arcnext, fbase, frame-vint(INTERP)-1);
957 greg 2.7 while (*arcnext) arcnext++;
958     strcpy(arcnext, ".unf");
959     arcnext += 4;
960 greg 2.13 if (usepinterp || vint(INTERP)) { /* and Z-buf */
961 greg 2.7 *arcnext++ = ' ';
962 gwlarson 2.25 sprintf(arcnext, fbase, frame-vint(INTERP)-1);
963 greg 2.7 while (*arcnext) arcnext++;
964     strcpy(arcnext, ".zbf");
965     arcnext += 4;
966     }
967     }
968     if (!usepinterp) /* update what's needed */
969     usepfilt |= nora_rgbe;
970 greg 2.2 } else /* interpolation needed */
971 greg 2.1 usepinterp++;
972 greg 2.2 if (frseq[1] >= astat.rnext) /* next batch unavailable */
973     frseq[1] = frseq[0];
974     sprintf(fnbefore, vval(BASENAME), frseq[0]);
975     sprintf(fnafter, vval(BASENAME), frseq[1]);
976     if (rvr == 1 && recover(frseq[0])) /* recover before frame? */
977     return(1);
978     /* generate command */
979 greg 2.1 if (usepinterp) { /* using pinterp */
980 greg 2.2 if (rvr == 2 && recover(frseq[1])) /* recover after? */
981     return(1);
982 greg 2.48 if (nblur > 1) { /* with pdmblur */
983 greg 2.6 sprintf(fname0, "%s/vw0%c", vval(DIRECTORY),
984     'a'+(iter%26));
985     sprintf(fname1, "%s/vw1%c", vval(DIRECTORY),
986     'a'+(iter%26));
987 gwlarson 2.25 if (!noaction) {
988     FILE *fp; /* motion blurring */
989     if ((fp = fopen(fname0, "w")) == NULL) {
990     perror(fname0); quit(1);
991     }
992     fputs(VIEWSTR, fp);
993     fprintview(vp, fp);
994     putc('\n', fp); fclose(fp);
995     if ((vp = getview(frame+1)) == NULL) {
996     fprintf(stderr,
997     "%s: unexpected error reading view for frame %d\n",
998     progname, frame+1);
999     quit(1);
1000     }
1001     if ((fp = fopen(fname1, "w")) == NULL) {
1002     perror(fname1); quit(1);
1003     }
1004     fputs(VIEWSTR, fp);
1005     fprintview(vp, fp);
1006     putc('\n', fp); fclose(fp);
1007 greg 2.1 }
1008     sprintf(combuf,
1009 greg 2.48 "(pmdblur %.3f %.3f %d %s %s; rm -f %s %s) | pinterp -B -a",
1010     mblurf, dblurf, nblur,
1011 greg 2.6 fname0, fname1, fname0, fname1);
1012     iter++;
1013 greg 2.1 } else /* no blurring */
1014     strcpy(combuf, "pinterp");
1015     strcat(combuf, viewopt(vp));
1016     if (vbool(RTRACE))
1017 greg 2.2 sprintf(combuf+strlen(combuf), " -ff -fr '%s -w0 %s'",
1018 greg 2.21 rendopt+1, vval(OCTREE));
1019 greg 2.1 if (vdef(PINTERP))
1020     sprintf(combuf+strlen(combuf), " %s", vval(PINTERP));
1021     if (usepfilt)
1022     sprintf(combuf+strlen(combuf), " %s", rresopt);
1023     else
1024 greg 2.33 sprintf(combuf+strlen(combuf), " -a %s -e %s",
1025 greg 2.1 fresopt, ep);
1026     sprintf(combuf+strlen(combuf), " %s.unf %s.zbf",
1027     fnbefore, fnbefore);
1028 greg 2.2 if (frseq[1] != frseq[0])
1029 greg 2.1 sprintf(combuf+strlen(combuf), " %s.unf %s.zbf",
1030     fnafter, fnafter);
1031     if (usepfilt) { /* also pfilt */
1032     if (vdef(PFILT))
1033     sprintf(combuf+strlen(combuf), " | pfilt %s",
1034     vval(PFILT));
1035     else
1036     strcat(combuf, " | pfilt");
1037     if (ep != NULL)
1038     sprintf(combuf+strlen(combuf), " -1 -e %s %s",
1039     ep, fresopt);
1040     else
1041     sprintf(combuf+strlen(combuf), " %s", fresopt);
1042     }
1043     } else if (usepfilt) { /* pfilt only */
1044 greg 2.2 if (rvr == 2)
1045     return(1);
1046 greg 2.1 if (vdef(PFILT))
1047     sprintf(combuf, "pfilt %s", vval(PFILT));
1048     else
1049     strcpy(combuf, "pfilt");
1050     if (ep != NULL)
1051     sprintf(combuf+strlen(combuf), " -1 -e %s %s %s.unf",
1052     ep, fresopt, fnbefore);
1053     else
1054     sprintf(combuf+strlen(combuf), " %s %s.unf",
1055     fresopt, fnbefore);
1056     } else { /* else just check it */
1057 greg 2.2 if (rvr == 2)
1058     return(1);
1059 greg 2.7 sprintf(combuf, "ra_rgbe -e %s -r %s.unf", ep, fnbefore);
1060 greg 2.1 }
1061     /* output file name */
1062 greg 2.6 sprintf(fname0, vval(BASENAME), frame);
1063 greg 2.54 sprintf(combuf+strlen(combuf), " > %s.hdr", fname0);
1064 greg 2.2 if (rvr) /* in recovery */
1065     return(runcom(combuf));
1066     bruncom(combuf, frame, frecover); /* else run in background */
1067     return(0);
1068 greg 2.1 }
1069    
1070    
1071 schorsch 2.43 static VIEW *
1072     getview(int n) /* get view number n */
1073 greg 2.1 {
1074     static FILE *viewfp = NULL; /* view file pointer */
1075     static int viewnum = 0; /* current view number */
1076     static VIEW curview = STDVIEW; /* current view */
1077     char linebuf[256];
1078    
1079     if (n == 0) { /* signal to close file and clean up */
1080     if (viewfp != NULL) {
1081     fclose(viewfp);
1082     viewfp = NULL;
1083     viewnum = 0;
1084 schorsch 2.40 curview = stdview;
1085 greg 2.1 }
1086     return(NULL);
1087     }
1088 gwlarson 2.24 if (viewfp == NULL) { /* open file */
1089 greg 2.1 if ((viewfp = fopen(vval(VIEWFILE), "r")) == NULL) {
1090     perror(vval(VIEWFILE));
1091     quit(1);
1092     }
1093 gwlarson 2.24 } else if (n > 0 && n < viewnum) { /* rewind file */
1094 greg 2.8 if (viewnum == 1 && feof(viewfp))
1095     return(&curview); /* just one view */
1096 greg 2.1 if (fseek(viewfp, 0L, 0) == EOF) {
1097     perror(vval(VIEWFILE));
1098     quit(1);
1099     }
1100 schorsch 2.40 curview = stdview;
1101 greg 2.1 viewnum = 0;
1102     }
1103 gwlarson 2.24 if (n < 0) { /* get next view */
1104     register int c = getc(viewfp);
1105     if (c == EOF)
1106     return((VIEW *)NULL); /* that's it */
1107     ungetc(c, viewfp);
1108     n = viewnum + 1;
1109     }
1110 greg 2.1 while (n > viewnum) { /* scan to desired view */
1111     if (fgets(linebuf, sizeof(linebuf), viewfp) == NULL)
1112 greg 2.15 return(viewnum==1 ? &curview : (VIEW *)NULL);
1113 greg 2.1 if (isview(linebuf) && sscanview(&curview, linebuf) > 0)
1114     viewnum++;
1115     }
1116     return(&curview); /* return it */
1117     }
1118    
1119    
1120 schorsch 2.43 static int
1121     countviews(void) /* count views in view file */
1122 greg 2.1 {
1123 gwlarson 2.24 int n;
1124 greg 2.1
1125 gwlarson 2.24 if (getview(n=1) == NULL)
1126     return(0);
1127     while (getview(-1) != NULL)
1128 greg 2.1 n++;
1129     return(n);
1130     }
1131    
1132    
1133 schorsch 2.43 static char *
1134     getexp(int n) /* get exposure for nth frame */
1135 greg 2.1 {
1136     static char expval[32];
1137     static FILE *expfp = NULL;
1138     static long *exppos;
1139     static int curfrm;
1140     register char *cp;
1141    
1142     if (n == 0) { /* signal to close file */
1143     if (expfp != NULL) {
1144     fclose(expfp);
1145 greg 2.29 free((void *)exppos);
1146 greg 2.1 expfp = NULL;
1147     }
1148     return(NULL);
1149 greg 2.14 } else if (n > vint(END)) /* request past end (error?) */
1150     return(NULL);
1151 greg 2.1 if (!vdef(EXPOSURE)) /* no setting (auto) */
1152     return(NULL);
1153     if (isflt(vval(EXPOSURE))) /* always the same */
1154     return(vval(EXPOSURE));
1155     if (expfp == NULL) { /* open exposure file */
1156     if ((expfp = fopen(vval(EXPOSURE), "r")) == NULL) {
1157     fprintf(stderr,
1158     "%s: cannot open exposure file \"%s\"\n",
1159     progname, vval(EXPOSURE));
1160     quit(1);
1161     }
1162     curfrm = vint(END) + 1; /* init lookup tab. */
1163     exppos = (long *)malloc(curfrm*sizeof(long *));
1164     if (exppos == NULL) {
1165     perror(progname);
1166     quit(1);
1167     }
1168     while (curfrm--)
1169     exppos[curfrm] = -1L;
1170     curfrm = 0;
1171     }
1172     /* find position in file */
1173     if (n-1 != curfrm && n != curfrm && exppos[n-1] >= 0 &&
1174     fseek(expfp, exppos[curfrm=n-1], 0) == EOF) {
1175     fprintf(stderr, "%s: seek error on exposure file\n", progname);
1176     quit(1);
1177     }
1178     while (n > curfrm) { /* read exposure */
1179     if (exppos[curfrm] < 0)
1180     exppos[curfrm] = ftell(expfp);
1181     if (fgets(expval, sizeof(expval), expfp) == NULL) {
1182     fprintf(stderr, "%s: too few exposures\n",
1183     vval(EXPOSURE));
1184     quit(1);
1185     }
1186     curfrm++;
1187     cp = fskip(expval); /* check format */
1188 greg 2.14 if (cp != NULL)
1189     while (isspace(*cp))
1190     *cp++ = '\0';
1191     if (cp == NULL || *cp) {
1192 greg 2.1 fprintf(stderr,
1193     "%s: exposure format error on line %d\n",
1194     vval(EXPOSURE), curfrm);
1195     quit(1);
1196     }
1197     }
1198     return(expval); /* return value */
1199     }
1200    
1201    
1202 schorsch 2.43 static struct pslot *
1203 schorsch 2.50 findpslot(RT_PID pid) /* find or allocate a process slot */
1204 greg 2.2 {
1205     register struct pslot *psempty = NULL;
1206     register int i;
1207    
1208     for (i = 0; i < npslots; i++) { /* look for match */
1209     if (pslot[i].pid == pid)
1210     return(pslot+i);
1211     if (psempty == NULL && pslot[i].pid == 0)
1212     psempty = pslot+i;
1213     }
1214     return(psempty); /* return emtpy slot (error if NULL) */
1215     }
1216    
1217    
1218 schorsch 2.43 static int
1219     donecom( /* clean up after finished process */
1220 schorsch 2.46 PSERVER *ps,
1221     int pn,
1222     int status
1223 schorsch 2.43 )
1224 greg 2.2 {
1225 schorsch 2.43 register NETPROC *pp;
1226 greg 2.18 register struct pslot *psl;
1227 greg 2.2
1228     pp = ps->proc + pn;
1229 greg 2.3 if (pp->elen) { /* pass errors */
1230 greg 2.2 if (ps->hostname[0])
1231 greg 2.3 fprintf(stderr, "%s: ", ps->hostname);
1232     fprintf(stderr, "Error output from: %s\n", pp->com);
1233 greg 2.2 fputs(pp->errs, stderr);
1234     fflush(stderr);
1235     if (ps->hostname[0])
1236     status = 1; /* because rsh doesn't return status */
1237     }
1238 greg 2.18 lastpserver = NULL;
1239     psl = findpslot(pp->pid); /* check for bruncom() slot */
1240     if (psl->pid) {
1241     if (status) {
1242     if (psl->rcvf != NULL) /* attempt recovery */
1243     status = (*psl->rcvf)(psl->fout);
1244     if (status) {
1245     fprintf(stderr,
1246     "%s: error rendering frame %d\n",
1247     progname, psl->fout);
1248     quit(1);
1249     }
1250     lastpserver = ps;
1251     }
1252     psl->pid = 0; /* free process slot */
1253     } else if (status)
1254     lastpserver = ps;
1255 greg 2.2 freestr(pp->com); /* free command string */
1256     return(status);
1257     }
1258    
1259    
1260 schorsch 2.43 static int
1261     serverdown(void) /* check status of last process server */
1262 greg 2.2 {
1263 greg 2.18 if (lastpserver == NULL || !lastpserver->hostname[0])
1264     return(0);
1265 greg 2.2 if (pserverOK(lastpserver)) /* server still up? */
1266     return(0);
1267     delpserver(lastpserver); /* else delete it */
1268     if (pslist == NULL) {
1269     fprintf(stderr, "%s: all process servers are down\n",
1270     progname);
1271     quit(1);
1272     }
1273     return(1);
1274     }
1275    
1276    
1277 schorsch 2.50 static RT_PID
1278 schorsch 2.43 bruncom( /* run a command in the background */
1279     char *com,
1280     int fout,
1281     int (*rf)()
1282     )
1283 greg 2.2 {
1284 schorsch 2.50 RT_PID pid;
1285 greg 2.2 register struct pslot *psl;
1286    
1287 greg 2.5 if (noaction) {
1288     if (!silent)
1289     printf("\t%s\n", com); /* echo command */
1290 greg 2.2 return(0);
1291 greg 2.5 }
1292 greg 2.18 com = savestr(com); /* else start it when we can */
1293     while ((pid = startjob(NULL, com, donecom)) == -1)
1294 greg 2.2 bwait(1);
1295 greg 2.5 if (!silent) { /* echo command */
1296 greg 2.3 PSERVER *ps;
1297 schorsch 2.50 RT_PID psn = pid;
1298 greg 2.4 ps = findjob(&psn);
1299 greg 2.5 printf("\t%s\n", com);
1300 greg 2.4 printf("\tProcess started on %s\n", phostname(ps));
1301 greg 2.3 fflush(stdout);
1302     }
1303 greg 2.2 psl = findpslot(pid); /* record info. in appropriate slot */
1304     psl->pid = pid;
1305     psl->fout = fout;
1306     psl->rcvf = rf;
1307     return(pid);
1308     }
1309    
1310    
1311 schorsch 2.43 static void
1312     bwait(int ncoms) /* wait for batch job(s) to finish */
1313 greg 2.2 {
1314     int status;
1315    
1316     if (noaction)
1317     return;
1318     while ((status = wait4job(NULL, -1)) != -1) {
1319 greg 2.18 serverdown(); /* update server status */
1320     if (--ncoms == 0)
1321     break; /* done enough */
1322 greg 2.2 }
1323     }
1324    
1325    
1326 schorsch 2.43 static int
1327     pruncom( /* run a command in parallel over network */
1328     char *com,
1329     char *ppins,
1330     int maxcopies
1331     )
1332 greg 2.2 {
1333     int retstatus = 0;
1334 greg 2.3 int hostcopies;
1335 greg 2.18 char buf[10240], *com1, *s;
1336 greg 2.2 int status;
1337 greg 2.17 int pfd;
1338     register int n;
1339 greg 2.2 register PSERVER *ps;
1340    
1341 greg 2.3 if (!silent)
1342 greg 2.6 printf("\t%s\n", com); /* echo command */
1343 greg 2.3 if (noaction)
1344 greg 2.2 return(0);
1345 greg 2.3 fflush(stdout);
1346 greg 2.2 /* start jobs on each server */
1347 greg 2.3 for (ps = pslist; ps != NULL; ps = ps->next) {
1348     hostcopies = 0;
1349 greg 2.4 if (maxcopies > 1 && ps->nprocs > 1 && ppins != NULL) {
1350 greg 2.17 strcpy(com1=buf, com); /* build -PP command */
1351 greg 2.4 sprintf(com1+(ppins-com), " -PP %s/%s.persist",
1352     vval(DIRECTORY), phostname(ps));
1353 greg 2.36 unlink(com1+(ppins-com)+5);
1354 greg 2.4 strcat(com1, ppins);
1355 greg 2.17 } else
1356 greg 2.4 com1 = com;
1357 greg 2.18 while (maxcopies > 0) {
1358     s = savestr(com1);
1359     if (startjob(ps, s, donecom) != -1) {
1360     sleep(20);
1361     hostcopies++;
1362     maxcopies--;
1363     } else {
1364     freestr(s);
1365     break;
1366     }
1367 greg 2.2 }
1368 greg 2.3 if (!silent && hostcopies) {
1369     if (hostcopies > 1)
1370     printf("\t%d duplicate processes", hostcopies);
1371     else
1372     printf("\tProcess");
1373 greg 2.4 printf(" started on %s\n", phostname(ps));
1374 greg 2.3 fflush(stdout);
1375     }
1376     }
1377 greg 2.2 /* wait for jobs to finish */
1378     while ((status = wait4job(NULL, -1)) != -1)
1379 greg 2.18 retstatus += status && !serverdown();
1380 greg 2.17 /* terminate parallel rpict's */
1381 greg 2.18 for (ps = pslist; ps != NULL; ps = ps->next) {
1382     sprintf(buf, "%s/%s.persist", vval(DIRECTORY), phostname(ps));
1383 greg 2.17 if ((pfd = open(buf, O_RDONLY)) >= 0) {
1384     n = read(pfd, buf, sizeof(buf)-1); /* get PID */
1385     buf[n] = '\0';
1386     close(pfd);
1387     for (n = 0; buf[n] && !isspace(buf[n]); n++)
1388     ;
1389     /* terminate */
1390     sprintf(buf, "kill -ALRM %d", atoi(buf+n));
1391 greg 2.18 wait4job(ps, startjob(ps, buf, NULL));
1392 greg 2.17 }
1393     }
1394 greg 2.2 return(retstatus);
1395     }
1396    
1397    
1398 schorsch 2.43 static int
1399     runcom(char *cs) /* run a command locally and wait for it */
1400 greg 2.1 {
1401     if (!silent) /* echo it */
1402     printf("\t%s\n", cs);
1403     if (noaction)
1404     return(0);
1405     fflush(stdout); /* flush output and pass to shell */
1406     return(system(cs));
1407     }
1408    
1409    
1410 schorsch 2.43 static int
1411     rmfile(char *fn) /* remove a file */
1412 greg 2.1 {
1413     if (!silent)
1414 schorsch 2.56 #if defined(_WIN32) || defined(_WIN64)
1415 greg 2.1 printf("\tdel %s\n", fn);
1416     #else
1417     printf("\trm -f %s\n", fn);
1418     #endif
1419     if (noaction)
1420     return(0);
1421     return(unlink(fn));
1422     }
1423    
1424    
1425 schorsch 2.43 static void
1426     badvalue(int vc) /* report bad variable value and exit */
1427 greg 2.1 {
1428     fprintf(stderr, "%s: bad value for variable '%s'\n",
1429     progname, vnam(vc));
1430     quit(1);
1431 gwlarson 2.25 }
1432    
1433    
1434 schorsch 2.43 static char *
1435     dirfile( /* separate path into directory and file */
1436     char *df,
1437     register char *path
1438     )
1439 gwlarson 2.25 {
1440     register int i;
1441     int psep;
1442    
1443     for (i = 0, psep = -1; path[i]; i++)
1444     if (path[i] == '/')
1445     psep = i;
1446 schorsch 2.40 if (df != NULL) {
1447 gwlarson 2.25 if (psep == 0) {
1448     df[0] = '/';
1449     df[1] = '\0';
1450     } else if (psep > 0) {
1451     strncpy(df, path, psep);
1452     df[psep] = '\0';
1453     } else
1454     df[0] = '\0';
1455 schorsch 2.40 }
1456 gwlarson 2.25 return(path+psep+1);
1457 greg 2.29 }
1458    
1459    
1460 schorsch 2.43 static int
1461 greg 2.48 getblur(double *mbf, double *dbf) /* get # blur samples (and fraction) */
1462 greg 2.29 {
1463 greg 2.48 double mblurf, dblurf;
1464     int nmblur, ndblur;
1465 greg 2.29 char *s;
1466 greg 2.48 /* get motion blur */
1467 greg 2.57 if (vdef(ANIMATE) || !vdef(MBLUR) || (mblurf = atof(vval(MBLUR))) < 0.0)
1468 greg 2.48 mblurf = 0.0;
1469     if (mbf != NULL)
1470     *mbf = mblurf;
1471     if (mblurf <= FTINY)
1472     nmblur = 0;
1473     else if (!*(s = sskip(vval(MBLUR))))
1474     nmblur = DEF_NBLUR;
1475     else if ((nmblur = atoi(s)) <= 0)
1476     nmblur = 1;
1477     /* get depth-of-field blur */
1478     if (!vdef(DBLUR) || (dblurf = atof(vval(DBLUR))) < 0.0)
1479     dblurf = 0.0;
1480     if (dbf != NULL)
1481     *dbf = dblurf;
1482     if (dblurf <= FTINY)
1483     ndblur = 0;
1484     else if (!*(s = sskip(vval(DBLUR))))
1485     ndblur = DEF_NBLUR;
1486     else if ((ndblur = atoi(s)) <= 0)
1487     ndblur = 1;
1488     if ((nmblur == 1) & (ndblur == 1))
1489 greg 2.29 return(1);
1490 greg 2.48 /* return combined samples */
1491     return(nmblur + ndblur);
1492 greg 2.1 }