#ifndef lint
static const char RCSid[] = "$Id: ranimate.c,v 2.29 2003/02/22 02:07:30 greg Exp $";
#endif
/*
* Radiance animation control program
*
* The main difference between this program and ranimove is that
* we have many optimizations here for camera motion in static
* environments, calling rpict and pinterp on multiple processors,
* where ranimove puts its emphasis on object motion, and does
* not use any external programs for image generation.
*
* See the ranimate(1) man page for further details.
*/
/* ====================================================================
* The Radiance Software License, Version 1.0
*
* Copyright (c) 1990 - 2002 The Regents of the University of California,
* through Lawrence Berkeley National Laboratory. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes Radiance software
* (http://radsite.lbl.gov/)
* developed by the Lawrence Berkeley National Laboratory
* (http://www.lbl.gov/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Radiance," "Lawrence Berkeley National Laboratory"
* and "The Regents of the University of California" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact radiance@radsite.lbl.gov.
*
* 5. Products derived from this software may not be called "Radiance",
* nor may "Radiance" appear in their name, without prior written
* permission of Lawrence Berkeley National Laboratory.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Lawrence Berkeley National Laboratory OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of Lawrence Berkeley National Laboratory. For more
* information on Lawrence Berkeley National Laboratory, please see
* .
*/
#include "standard.h"
#include
#include
#include "view.h"
#include "vars.h"
#include "netproc.h"
/* default blur samples */
#ifndef DEF_NBLUR
#define DEF_NBLUR 5
#endif
/* default remote shell */
#ifdef _AUX_SOURCE
#define REMSH "remsh"
#else
#define REMSH "rsh"
#endif
/* input variables (alphabetical by name) */
#define ANIMATE 0 /* animation command */
#define ARCHIVE 1 /* archiving command */
#define BASENAME 2 /* output image base name */
#define DIRECTORY 3 /* working (sub)directory */
#define DISKSPACE 4 /* how much disk space to use */
#define END 5 /* ending frame number */
#define EXPOSURE 6 /* how to compute exposure */
#define HOST 7 /* rendering host machine */
#define INTERP 8 /* # frames to interpolate */
#define MBLUR 9 /* motion blur parameters */
#define NEXTANIM 10 /* next animation file */
#define OCTREE 11 /* octree file name */
#define OVERSAMP 12 /* # times to oversample image */
#define PFILT 13 /* pfilt options */
#define PINTERP 14 /* pinterp options */
#define RENDER 15 /* rendering options */
#define RESOLUTION 16 /* desired final resolution */
#define RIF 17 /* rad input file */
#define RSH 18 /* remote shell script or program */
#define RTRACE 19 /* use rtrace with pinterp? */
#define START 20 /* starting frame number */
#define TRANSFER 21 /* frame transfer command */
#define VIEWFILE 22 /* animation frame views */
int NVARS = 23; /* total number of variables */
VARIABLE vv[] = { /* variable-value pairs */
{"ANIMATE", 2, 0, NULL, onevalue},
{"ARCHIVE", 2, 0, NULL, onevalue},
{"BASENAME", 3, 0, NULL, onevalue},
{"DIRECTORY", 3, 0, NULL, onevalue},
{"DISKSPACE", 3, 0, NULL, fltvalue},
{"END", 2, 0, NULL, intvalue},
{"EXPOSURE", 3, 0, NULL, onevalue},
{"host", 4, 0, NULL, NULL},
{"INTERPOLATE", 3, 0, NULL, intvalue},
{"MBLUR", 2, 0, NULL, onevalue},
{"NEXTANIM", 3, 0, NULL, onevalue},
{"OCTREE", 3, 0, NULL, onevalue},
{"OVERSAMPLE", 2, 0, NULL, fltvalue},
{"pfilt", 2, 0, NULL, catvalues},
{"pinterp", 2, 0, NULL, catvalues},
{"render", 3, 0, NULL, catvalues},
{"RESOLUTION", 3, 0, NULL, onevalue},
{"RIF", 3, 0, NULL, onevalue},
{"RSH", 3, 0, NULL, onevalue},
{"RTRACE", 2, 0, NULL, boolvalue},
{"START", 2, 0, NULL, intvalue},
{"TRANSFER", 2, 0, NULL, onevalue},
{"VIEWFILE", 2, 0, NULL, onevalue},
};
#define SFNAME "STATUS" /* status file name */
struct {
char host[64]; /* control host name */
int pid; /* control process id */
char cfname[128]; /* control file name */
int rnext; /* next frame to render */
int fnext; /* next frame to filter */
int tnext; /* next frame to transfer */
} astat; /* animation status */
char *progname; /* our program name */
char *cfname; /* our control file name */
int nowarn = 0; /* turn warnings off? */
int silent = 0; /* silent mode? */
int noaction = 0; /* take no action? */
char *remsh; /* remote shell program/script */
char rendopt[2048]; /* rendering options */
char rresopt[32]; /* rendering resolution options */
char fresopt[32]; /* filter resolution options */
int pfiltalways; /* always use pfilt? */
char arcargs[10240]; /* files to archive */
char *arcfirst, *arcnext; /* pointers to first and next argument */
struct pslot {
int pid; /* process ID (0 if empty) */
int fout; /* output frame number */
int (*rcvf)(); /* recover function */
} *pslot; /* process slots */
int npslots; /* number of process slots */
#define phostname(ps) ((ps)->hostname[0] ? (ps)->hostname : astat.host)
struct pslot *findpslot();
PSERVER *lastpserver; /* last process server with error */
VIEW *getview();
char *getexp(), *dirfile();
int getblur();
extern time_t time();
main(argc, argv)
int argc;
char *argv[];
{
int explicate = 0;
int i;
progname = argv[0]; /* get arguments */
for (i = 1; i < argc && argv[i][0] == '-'; i++)
switch (argv[i][1]) {
case 'e': /* print variables */
explicate++;
break;
case 'w': /* turn off warnings */
nowarn++;
break;
case 's': /* silent mode */
silent++;
break;
case 'n': /* take no action */
noaction++;
break;
default:
goto userr;
}
if (i != argc-1)
goto userr;
cfname = argv[i];
/* load variables */
loadvars(cfname);
/* check variables */
checkvalues();
/* did we get DIRECTORY? */
checkdir();
/* check status */
if (getastat() < 0) {
fprintf(stderr, "%s: exiting\n", progname);
quit(1);
}
/* pfilt always if options given */
pfiltalways = vdef(PFILT);
/* load RIF if any */
if (vdef(RIF))
getradfile(vval(RIF));
/* set defaults */
setdefaults();
/* print variables */
if (explicate)
printvars(stdout);
/* set up process servers */
sethosts();
/* run animation */
animate();
/* all done */
if (vdef(NEXTANIM)) {
argv[i] = vval(NEXTANIM); /* just change input file */
if (!silent)
printargs(argc, argv, stdout);
if ((argv[0] = getpath(progname,getenv("PATH"),X_OK)) == NULL)
fprintf(stderr, "%s: command not found\n", progname);
else
execv(progname, argv);
quit(1);
}
quit(0);
userr:
fprintf(stderr, "Usage: %s [-s][-n][-w][-e] anim_file\n", progname);
quit(1);
}
getastat() /* check/set animation status */
{
char sfname[256];
FILE *fp;
sprintf(sfname, "%s/%s", vval(DIRECTORY), SFNAME);
if ((fp = fopen(sfname, "r")) == NULL) {
if (errno != ENOENT) {
perror(sfname);
return(-1);
}
astat.rnext = astat.fnext = astat.tnext = 0;
goto setours;
}
if (fscanf(fp, "Control host: %s\n", astat.host) != 1)
goto fmterr;
if (fscanf(fp, "Control PID: %d\n", &astat.pid) != 1)
goto fmterr;
if (fscanf(fp, "Control file: %s\n", astat.cfname) != 1)
goto fmterr;
if (fscanf(fp, "Next render: %d\n", &astat.rnext) != 1)
goto fmterr;
if (fscanf(fp, "Next filter: %d\n", &astat.fnext) != 1)
goto fmterr;
if (fscanf(fp, "Next transfer: %d\n", &astat.tnext) != 1)
goto fmterr;
fclose(fp);
if (astat.pid != 0) { /* thinks it's still running */
if (strcmp(myhostname(), astat.host)) {
fprintf(stderr,
"%s: process %d may still be running on host %s\n",
progname, astat.pid, astat.host);
return(-1);
}
if (kill(astat.pid, 0) != -1 || errno != ESRCH) {
fprintf(stderr, "%s: process %d is still running\n",
progname, astat.pid);
return(-1);
}
/* assume it is dead */
}
if (strcmp(cfname, astat.cfname) && astat.pid != 0) { /* other's */
fprintf(stderr, "%s: unfinished job \"%s\"\n",
progname, astat.cfname);
return(-1);
}
/* check control file mods. */
if (!nowarn && fdate(cfname) > fdate(sfname))
fprintf(stderr,
"%s: warning - control file modified since last run\n",
progname);
setours: /* set our values */
strcpy(astat.host, myhostname());
astat.pid = getpid();
strcpy(astat.cfname, cfname);
return(0);
fmterr:
fprintf(stderr, "%s: format error in status file \"%s\"\n",
progname, sfname);
fclose(fp);
return(-1);
}
putastat() /* put out current status */
{
char buf[256];
FILE *fp;
if (noaction)
return;
sprintf(buf, "%s/%s", vval(DIRECTORY), SFNAME);
if ((fp = fopen(buf, "w")) == NULL) {
perror(buf);
quit(1);
}
fprintf(fp, "Control host: %s\n", astat.host);
fprintf(fp, "Control PID: %d\n", astat.pid);
fprintf(fp, "Control file: %s\n", astat.cfname);
fprintf(fp, "Next render: %d\n", astat.rnext);
fprintf(fp, "Next filter: %d\n", astat.fnext);
fprintf(fp, "Next transfer: %d\n", astat.tnext);
fclose(fp);
}
checkdir() /* make sure we have our directory */
{
struct stat stb;
if (!vdef(DIRECTORY)) {
fprintf(stderr, "%s: %s undefined\n",
progname, vnam(DIRECTORY));
quit(1);
}
if (stat(vval(DIRECTORY), &stb) == -1) {
if (errno == ENOENT && mkdir(vval(DIRECTORY), 0777) == 0)
return;
perror(vval(DIRECTORY));
quit(1);
}
if (!(stb.st_mode & S_IFDIR)) {
fprintf(stderr, "%s: not a directory\n", vval(DIRECTORY));
quit(1);
}
}
setdefaults() /* set default values */
{
extern char *atos();
int decades;
char buf[256];
if (vdef(ANIMATE)) {
vval(OCTREE) = NULL;
vdef(OCTREE) = 0;
} else if (!vdef(OCTREE)) {
fprintf(stderr, "%s: either %s or %s must be defined\n",
progname, vnam(OCTREE), vnam(ANIMATE));
quit(1);
}
if (!vdef(VIEWFILE)) {
fprintf(stderr, "%s: %s undefined\n", progname, vnam(VIEWFILE));
quit(1);
}
if (!vdef(HOST)) {
vval(HOST) = LHOSTNAME;
vdef(HOST)++;
}
if (!vdef(START)) {
vval(START) = "1";
vdef(START)++;
}
if (!vdef(END)) {
sprintf(buf, "%d", countviews()+vint(START)-1);
vval(END) = savqstr(buf);
vdef(END)++;
}
if (vint(END) < vint(START)) {
fprintf(stderr, "%s: ending frame less than starting frame\n",
progname);
quit(1);
}
if (!vdef(BASENAME)) {
decades = (int)log10((double)vint(END)) + 1;
if (decades < 3) decades = 3;
sprintf(buf, "%s/frame%%0%dd", vval(DIRECTORY), decades);
vval(BASENAME) = savqstr(buf);
vdef(BASENAME)++;
}
if (!vdef(RESOLUTION)) {
vval(RESOLUTION) = "640";
vdef(RESOLUTION)++;
}
if (!vdef(OVERSAMP)) {
vval(OVERSAMP) = "2";
vdef(OVERSAMP)++;
}
if (!vdef(INTERP)) {
vval(INTERP) = "0";
vdef(INTERP)++;
}
if (!vdef(MBLUR)) {
vval(MBLUR) = "0";
vdef(MBLUR)++;
}
if (!vdef(RTRACE)) {
vval(RTRACE) = "F";
vdef(RTRACE)++;
}
if (!vdef(DISKSPACE)) {
if (!nowarn)
fprintf(stderr,
"%s: warning - no %s setting, assuming 100 Mbytes available\n",
progname, vnam(DISKSPACE));
vval(DISKSPACE) = "100";
vdef(DISKSPACE)++;
}
if (!vdef(RSH)) {
vval(RSH) = REMSH;
vdef(RSH)++;
}
/* locate remote shell program */
atos(buf, sizeof(buf), vval(RSH));
if ((remsh = getpath(buf, getenv("PATH"), X_OK)) != NULL)
remsh = savqstr(remsh);
else
remsh = vval(RSH); /* will generate error if used */
/* append rendering options */
if (vdef(RENDER))
sprintf(rendopt+strlen(rendopt), " %s", vval(RENDER));
}
sethosts() /* set up process servers */
{
extern char *iskip();
char buf[256], *dir, *uname;
int np;
register char *cp;
int i;
npslots = 0;
if (noaction)
return;
for (i = 0; i < vdef(HOST); i++) { /* add each host */
dir = uname = NULL;
np = 1;
strcpy(cp=buf, nvalue(HOST, i)); /* copy to buffer */
cp = sskip(cp); /* skip host name */
while (isspace(*cp))
*cp++ = '\0';
if (*cp) { /* has # processes? */
np = atoi(cp);
if ((cp = iskip(cp)) == NULL || (*cp && !isspace(*cp)))
badvalue(HOST);
while (isspace(*cp))
cp++;
if (*cp) { /* has directory? */
dir = cp;
cp = sskip(cp); /* skip dir. */
while (isspace(*cp))
*cp++ = '\0';
if (*cp) { /* has user? */
uname = cp;
if (*sskip(cp))
badvalue(HOST);
}
}
}
if (addpserver(buf, dir, uname, np) == NULL) {
if (!nowarn)
fprintf(stderr,
"%s: cannot execute on host \"%s\"\n",
progname, buf);
} else
npslots += np;
}
if (npslots == 0) {
fprintf(stderr, "%s: no working process servers\n", progname);
quit(1);
}
pslot = (struct pslot *)calloc(npslots, sizeof(struct pslot));
if (pslot == NULL) {
perror("malloc");
quit(1);
}
}
getradfile(rfargs) /* run rad and get needed variables */
char *rfargs;
{
static short mvar[] = {OCTREE,PFILT,RESOLUTION,EXPOSURE,-1};
char combuf[256];
register int i;
register char *cp;
char *pippt;
/* create rad command */
sprintf(rendopt, " @%s/render.opt", vval(DIRECTORY));
sprintf(combuf,
"rad -v 0 -s -e -w %s OPTFILE=%s | egrep '^[ \t]*(NOMATCH",
rfargs, rendopt+2);
cp = combuf;
while (*cp) {
if (*cp == '|') pippt = cp;
cp++;
} /* match unset variables */
for (i = 0; mvar[i] >= 0; i++)
if (!vdef(mvar[i])) {
*cp++ = '|';
strcpy(cp, vnam(mvar[i]));
while (*cp) cp++;
pippt = NULL;
}
if (pippt != NULL)
strcpy(pippt, "> /dev/null"); /* nothing to match */
else {
sprintf(cp, ")[ \t]*=' > %s/radset.var", vval(DIRECTORY));
cp += 11; /* point to file name */
}
system(combuf); /* ignore exit code */
if (pippt == NULL) { /* load variables and remove file */
loadvars(cp);
unlink(cp);
}
}
animate() /* run animation */
{
int xres, yres;
float pa, mult;
int frames_batch;
register int i;
double d1, d2;
/* compute rpict resolution */
i = sscanf(vval(RESOLUTION), "%d %d %f", &xres, &yres, &pa);
mult = vflt(OVERSAMP);
if (i == 3) {
sprintf(rresopt, "-x %d -y %d -pa %.3f", (int)(mult*xres),
(int)(mult*yres), pa);
sprintf(fresopt, "-x %d -y %d -pa %.3f", xres, yres, pa);
} else if (i) {
if (i == 1) yres = xres;
sprintf(rresopt, "-x %d -y %d", (int)(mult*xres),
(int)(mult*yres));
sprintf(fresopt, "-x %d -y %d -pa 1", xres, yres);
} else
badvalue(RESOLUTION);
/* consistency checks */
if (vdef(ANIMATE)) {
if (vint(INTERP)) {
if (!nowarn)
fprintf(stderr,
"%s: resetting %s=0 for animation\n",
progname, vnam(INTERP));
vval(INTERP) = "0";
}
if (strcmp(vval(MBLUR),"0")) { /* can't handle this */
if (!nowarn)
fprintf(stderr,
"%s: resetting %s=0 for animation\n",
progname, vnam(MBLUR));
vval(MBLUR) = "0";
}
}
/* figure # frames per batch */
d1 = mult*xres*mult*yres*4; /* space for orig. picture */
if ((i=vint(INTERP)) || getblur(NULL) > 1)
d1 += mult*xres*mult*yres*sizeof(float); /* Z-buffer */
d2 = xres*yres*4; /* space for final picture */
frames_batch = (i+1)*(vflt(DISKSPACE)*1048576.-d1)/(d1+i*d2);
if (frames_batch < i+2) {
fprintf(stderr, "%s: insufficient disk space allocated\n",
progname);
quit(1);
}
/* initialize archive argument list */
i = vdef(ARCHIVE) ? strlen(vval(ARCHIVE))+132 : 132;
arcnext = arcfirst = arcargs + i;
/* initialize status file */
if (astat.rnext == 0)
astat.rnext = astat.fnext = astat.tnext = vint(START);
putastat();
/* render in batches */
while (astat.tnext <= vint(END)) {
renderframes(frames_batch);
filterframes();
transferframes();
}
/* mark status as finished */
astat.pid = 0;
putastat();
/* close open files */
getview(0);
getexp(0);
}
renderframes(nframes) /* render next nframes frames */
int nframes;
{
static char vendbuf[16];
VIEW *vp;
FILE *fp = NULL;
char vfname[128];
int lastframe;
register int i;
if (astat.tnext < astat.rnext) /* other work to do first */
return;
/* create batch view file */
if (!vdef(ANIMATE)) {
sprintf(vfname, "%s/anim.vf", vval(DIRECTORY));
if ((fp = fopen(vfname, "w")) == NULL) {
perror(vfname);
quit(1);
}
}
/* bound batch properly */
lastframe = astat.rnext + nframes - 1;
if ((lastframe-1) % (vint(INTERP)+1)) /* need even interval */
lastframe += vint(INTERP)+1 - ((lastframe-1)%(vint(INTERP)+1));
if (lastframe > vint(END)) /* check for end */
lastframe = vint(END);
/* render each view */
for (i = astat.rnext; i <= lastframe; i++) {
if ((vp = getview(i)) == NULL) {
if (!nowarn)
fprintf(stderr,
"%s: ran out of views before last frame\n",
progname);
sprintf(vval(END)=vendbuf, "%d", i-1);
lastframe = i - 1;
break;
}
if (vdef(ANIMATE)) /* animate frame */
animrend(i, vp);
else { /* else record it */
fputs(VIEWSTR, fp);
fprintview(vp, fp);
putc('\n', fp);
}
}
if (vdef(ANIMATE)) /* wait for renderings to finish */
bwait(0);
else { /* else if walk-through */
fclose(fp); /* close view file */
walkwait(astat.rnext, lastframe, vfname); /* walk it */
unlink(vfname); /* remove view file */
}
astat.rnext = i; /* update status */
putastat();
}
filterframes() /* catch up with filtering */
{
VIEW *vp;
register int i;
if (astat.tnext < astat.fnext) /* other work to do first */
return;
/* filter each view */
for (i = astat.fnext; i < astat.rnext; i++) {
if ((vp = getview(i)) == NULL) { /* get view i */
fprintf(stderr,
"%s: unexpected error reading view for frame %d\n",
progname, i);
quit(1);
}
dofilt(i, vp, getexp(i), 0); /* filter frame */
}
bwait(0); /* wait for filter processes */
archive(); /* archive originals */
astat.fnext = i; /* update status */
putastat();
}
transferframes() /* catch up with picture transfers */
{
char combuf[10240], *fbase;
register char *cp;
register int i;
if (astat.tnext >= astat.fnext) /* nothing to do, yet */
return;
if (!vdef(TRANSFER)) { /* no transfer function -- leave 'em */
astat.tnext = astat.fnext;
putastat(); /* update status */
return;
}
strcpy(combuf, "cd "); /* start transfer command */
fbase = dirfile(cp = combuf+3, vval(BASENAME));
if (*cp) {
while (*++cp) ;
*cp++ = ';'; *cp++ = ' ';
} else
cp = combuf;
strcpy(cp, vval(TRANSFER));
while (*cp) cp++;
/* make argument list */
for (i = astat.tnext; i < astat.fnext; i++) {
*cp++ = ' ';
sprintf(cp, fbase, i);
while (*cp) cp++;
strcpy(cp, ".pic");
cp += 4;
}
if (runcom(combuf)) { /* transfer frames */
fprintf(stderr, "%s: error running transfer command\n",
progname);
quit(1);
}
astat.tnext = i; /* update status */
putastat();
}
animrend(frame, vp) /* start animation frame */
int frame;
VIEW *vp;
{
extern int recover();
char combuf[2048];
char fname[128];
sprintf(fname, vval(BASENAME), frame);
strcat(fname, ".unf");
if (access(fname, F_OK) == 0)
return;
sprintf(combuf, "%s %d | rpict%s%s -w0 %s > %s", vval(ANIMATE), frame,
rendopt, viewopt(vp), rresopt, fname);
bruncom(combuf, frame, recover); /* run in background */
}
walkwait(first, last, vfn) /* walk-through frames */
int first, last;
char *vfn;
{
double blurf;
int nblur = getblur(&blurf);
char combuf[2048];
register char *inspoint;
register int i;
if (!noaction && vint(INTERP)) /* create dummy frames */
for (i = first; i <= last; i++)
if (i < vint(END) && (i-1) % (vint(INTERP)+1)) {
sprintf(combuf, vval(BASENAME), i);
strcat(combuf, ".unf");
close(open(combuf, O_RDONLY|O_CREAT, 0666));
}
/* create command */
sprintf(combuf, "rpict%s%s -w0", rendopt,
viewopt(getview(first>1 ? first-1 : 1)));
inspoint = combuf;
while (*inspoint) inspoint++;
if (nblur) {
sprintf(inspoint, " -pm %.3f", blurf/nblur);
while (*inspoint) inspoint++;
}
if (nblur > 1 || vint(INTERP)) {
sprintf(inspoint, " -z %s.zbf", vval(BASENAME));
while (*inspoint) inspoint++;
}
sprintf(inspoint, " -o %s.unf %s -S %d",
vval(BASENAME), rresopt, first);
while (*inspoint) inspoint++;
sprintf(inspoint, " %s < %s", vval(OCTREE), vfn);
/* run in parallel */
i = (last-first+1)/(vint(INTERP)+1);
if (i < 1) i = 1;
if (pruncom(combuf, inspoint, i)) {
fprintf(stderr, "%s: error rendering frames %d through %d\n",
progname, first, last);
quit(1);
}
if (!noaction && vint(INTERP)) /* remove dummy frames */
for (i = first; i <= last; i++)
if (i < vint(END) && (i-1) % (vint(INTERP)+1)) {
sprintf(combuf, vval(BASENAME), i);
strcat(combuf, ".unf");
unlink(combuf);
}
}
int
recover(frame) /* recover the specified frame */
int frame;
{
static int *rfrm; /* list of recovered frames */
static int nrfrms = 0;
double blurf;
int nblur = getblur(&blurf);
char combuf[2048];
char fname[128];
register char *cp;
register int i;
/* check to see if recovered already */
for (i = nrfrms; i--; )
if (rfrm[i] == frame)
return(0);
/* build command */
sprintf(fname, vval(BASENAME), frame);
if (vdef(ANIMATE))
sprintf(combuf, "%s %d | rpict%s -w0",
vval(ANIMATE), frame, rendopt);
else
sprintf(combuf, "rpict%s -w0", rendopt);
cp = combuf;
while (*cp) cp++;
if (nblur) {
sprintf(cp, " -pm %.3f", blurf/nblur);
while (*cp) cp++;
}
if (nblur > 1 || vint(INTERP)) {
sprintf(cp, " -z %s.zbf", fname);
while (*cp) cp++;
}
sprintf(cp, " -ro %s.unf", fname);
while (*cp) cp++;
if (!vdef(ANIMATE)) {
*cp++ = ' ';
strcpy(cp, vval(OCTREE));
}
if (runcom(combuf)) /* run command */
return(1);
/* add frame to recovered list */
if (nrfrms)
rfrm = (int *)realloc((char *)rfrm, (nrfrms+1)*sizeof(int));
else
rfrm = (int *)malloc(sizeof(int));
if (rfrm == NULL) {
perror("malloc");
quit(1);
}
rfrm[nrfrms++] = frame;
return(0);
}
int
frecover(frame) /* recover filtered frame */
int frame;
{
VIEW *vp;
char *ex;
vp = getview(frame);
ex = getexp(frame);
if (dofilt(frame, vp, ex, 2) && dofilt(frame, vp, ex, 1))
return(1);
return(0);
}
archive() /* archive and remove renderings */
{
#define RMCOML (sizeof(rmcom)-1)
static char rmcom[] = "rm -f";
char basedir[128];
int dlen, alen;
register int j;
if (arcnext == arcfirst)
return; /* nothing to do */
dirfile(basedir, vval(BASENAME));
dlen = strlen(basedir);
if (vdef(ARCHIVE)) { /* run archive command */
alen = strlen(vval(ARCHIVE));
if (dlen) {
j = alen + dlen + 5;
strncpy(arcfirst-j, "cd ", 3);
strncpy(arcfirst-j+3, basedir, dlen);
(arcfirst-j)[dlen+3] = ';'; (arcfirst-j)[dlen+4] = ' ';
} else
j = alen;
strncpy(arcfirst-alen, vval(ARCHIVE), alen);
if (runcom(arcfirst-j)) {
fprintf(stderr, "%s: error running archive command\n",
progname);
quit(1);
}
}
if (dlen) {
j = RMCOML + dlen + 5;
strncpy(arcfirst-j, "cd ", 3);
strncpy(arcfirst-j+3, basedir, dlen);
(arcfirst-j)[dlen+3] = ';'; (arcfirst-j)[dlen+4] = ' ';
} else
j = RMCOML;
/* run remove command */
strncpy(arcfirst-RMCOML, rmcom, RMCOML);
runcom(arcfirst-j);
arcnext = arcfirst; /* reset argument list */
#undef RMCOML
}
int
dofilt(frame, vp, ep, rvr) /* filter frame */
int frame;
VIEW *vp;
char *ep;
int rvr;
{
extern int frecover();
static int iter = 0;
double blurf;
int nblur = getblur(&blurf);
char fnbefore[128], fnafter[128], *fbase;
char combuf[1024], fname0[128], fname1[128];
int usepinterp, usepfilt, nora_rgbe;
int frseq[2];
/* check what is needed */
usepinterp = (nblur > 1);
usepfilt = pfiltalways | ep==NULL;
if (ep != NULL && !strcmp(ep, "1"))
ep = "+0";
nora_rgbe = strcmp(vval(OVERSAMP),"1") || ep==NULL ||
*ep != '+' || *ep != '-' || !isint(ep);
/* compute rendered views */
frseq[0] = frame - ((frame-1) % (vint(INTERP)+1));
frseq[1] = frseq[0] + vint(INTERP) + 1;
fbase = dirfile(NULL, vval(BASENAME));
if (frseq[1] > vint(END))
frseq[1] = vint(END);
if (frseq[1] == frame) { /* pfilt only */
frseq[0] = frseq[1];
usepinterp = 0; /* update what's needed */
usepfilt |= nora_rgbe;
} else if (frseq[0] == frame) { /* no interpolation needed */
if (!rvr && frame > 1+vint(INTERP)) { /* archive previous */
*arcnext++ = ' ';
sprintf(arcnext, fbase, frame-vint(INTERP)-1);
while (*arcnext) arcnext++;
strcpy(arcnext, ".unf");
arcnext += 4;
if (usepinterp || vint(INTERP)) { /* and Z-buf */
*arcnext++ = ' ';
sprintf(arcnext, fbase, frame-vint(INTERP)-1);
while (*arcnext) arcnext++;
strcpy(arcnext, ".zbf");
arcnext += 4;
}
}
if (!usepinterp) /* update what's needed */
usepfilt |= nora_rgbe;
} else /* interpolation needed */
usepinterp++;
if (frseq[1] >= astat.rnext) /* next batch unavailable */
frseq[1] = frseq[0];
sprintf(fnbefore, vval(BASENAME), frseq[0]);
sprintf(fnafter, vval(BASENAME), frseq[1]);
if (rvr == 1 && recover(frseq[0])) /* recover before frame? */
return(1);
/* generate command */
if (usepinterp) { /* using pinterp */
if (rvr == 2 && recover(frseq[1])) /* recover after? */
return(1);
if (nblur > 1) { /* with pmblur */
sprintf(fname0, "%s/vw0%c", vval(DIRECTORY),
'a'+(iter%26));
sprintf(fname1, "%s/vw1%c", vval(DIRECTORY),
'a'+(iter%26));
if (!noaction) {
FILE *fp; /* motion blurring */
if ((fp = fopen(fname0, "w")) == NULL) {
perror(fname0); quit(1);
}
fputs(VIEWSTR, fp);
fprintview(vp, fp);
putc('\n', fp); fclose(fp);
if ((vp = getview(frame+1)) == NULL) {
fprintf(stderr,
"%s: unexpected error reading view for frame %d\n",
progname, frame+1);
quit(1);
}
if ((fp = fopen(fname1, "w")) == NULL) {
perror(fname1); quit(1);
}
fputs(VIEWSTR, fp);
fprintview(vp, fp);
putc('\n', fp); fclose(fp);
}
sprintf(combuf,
"(pmblur %.3f %d %s %s; rm -f %s %s) | pinterp -B",
blurf, nblur,
fname0, fname1, fname0, fname1);
iter++;
} else /* no blurring */
strcpy(combuf, "pinterp");
strcat(combuf, viewopt(vp));
if (vbool(RTRACE))
sprintf(combuf+strlen(combuf), " -ff -fr '%s -w0 %s'",
rendopt+1, vval(OCTREE));
if (vdef(PINTERP))
sprintf(combuf+strlen(combuf), " %s", vval(PINTERP));
if (usepfilt)
sprintf(combuf+strlen(combuf), " %s", rresopt);
else
sprintf(combuf+strlen(combuf), " %s -e %s",
fresopt, ep);
sprintf(combuf+strlen(combuf), " %s.unf %s.zbf",
fnbefore, fnbefore);
if (frseq[1] != frseq[0])
sprintf(combuf+strlen(combuf), " %s.unf %s.zbf",
fnafter, fnafter);
if (usepfilt) { /* also pfilt */
if (vdef(PFILT))
sprintf(combuf+strlen(combuf), " | pfilt %s",
vval(PFILT));
else
strcat(combuf, " | pfilt");
if (ep != NULL)
sprintf(combuf+strlen(combuf), " -1 -e %s %s",
ep, fresopt);
else
sprintf(combuf+strlen(combuf), " %s", fresopt);
}
} else if (usepfilt) { /* pfilt only */
if (rvr == 2)
return(1);
if (vdef(PFILT))
sprintf(combuf, "pfilt %s", vval(PFILT));
else
strcpy(combuf, "pfilt");
if (ep != NULL)
sprintf(combuf+strlen(combuf), " -1 -e %s %s %s.unf",
ep, fresopt, fnbefore);
else
sprintf(combuf+strlen(combuf), " %s %s.unf",
fresopt, fnbefore);
} else { /* else just check it */
if (rvr == 2)
return(1);
sprintf(combuf, "ra_rgbe -e %s -r %s.unf", ep, fnbefore);
}
/* output file name */
sprintf(fname0, vval(BASENAME), frame);
sprintf(combuf+strlen(combuf), " > %s.pic", fname0);
if (rvr) /* in recovery */
return(runcom(combuf));
bruncom(combuf, frame, frecover); /* else run in background */
return(0);
}
VIEW *
getview(n) /* get view number n */
int n;
{
static FILE *viewfp = NULL; /* view file pointer */
static int viewnum = 0; /* current view number */
static VIEW curview = STDVIEW; /* current view */
char linebuf[256];
if (n == 0) { /* signal to close file and clean up */
if (viewfp != NULL) {
fclose(viewfp);
viewfp = NULL;
viewnum = 0;
copystruct(&curview, &stdview);
}
return(NULL);
}
if (viewfp == NULL) { /* open file */
if ((viewfp = fopen(vval(VIEWFILE), "r")) == NULL) {
perror(vval(VIEWFILE));
quit(1);
}
} else if (n > 0 && n < viewnum) { /* rewind file */
if (viewnum == 1 && feof(viewfp))
return(&curview); /* just one view */
if (fseek(viewfp, 0L, 0) == EOF) {
perror(vval(VIEWFILE));
quit(1);
}
copystruct(&curview, &stdview);
viewnum = 0;
}
if (n < 0) { /* get next view */
register int c = getc(viewfp);
if (c == EOF)
return((VIEW *)NULL); /* that's it */
ungetc(c, viewfp);
n = viewnum + 1;
}
while (n > viewnum) { /* scan to desired view */
if (fgets(linebuf, sizeof(linebuf), viewfp) == NULL)
return(viewnum==1 ? &curview : (VIEW *)NULL);
if (isview(linebuf) && sscanview(&curview, linebuf) > 0)
viewnum++;
}
return(&curview); /* return it */
}
int
countviews() /* count views in view file */
{
int n;
if (getview(n=1) == NULL)
return(0);
while (getview(-1) != NULL)
n++;
return(n);
}
char *
getexp(n) /* get exposure for nth frame */
int n;
{
extern char *fskip();
static char expval[32];
static FILE *expfp = NULL;
static long *exppos;
static int curfrm;
register char *cp;
if (n == 0) { /* signal to close file */
if (expfp != NULL) {
fclose(expfp);
free((void *)exppos);
expfp = NULL;
}
return(NULL);
} else if (n > vint(END)) /* request past end (error?) */
return(NULL);
if (!vdef(EXPOSURE)) /* no setting (auto) */
return(NULL);
if (isflt(vval(EXPOSURE))) /* always the same */
return(vval(EXPOSURE));
if (expfp == NULL) { /* open exposure file */
if ((expfp = fopen(vval(EXPOSURE), "r")) == NULL) {
fprintf(stderr,
"%s: cannot open exposure file \"%s\"\n",
progname, vval(EXPOSURE));
quit(1);
}
curfrm = vint(END) + 1; /* init lookup tab. */
exppos = (long *)malloc(curfrm*sizeof(long *));
if (exppos == NULL) {
perror(progname);
quit(1);
}
while (curfrm--)
exppos[curfrm] = -1L;
curfrm = 0;
}
/* find position in file */
if (n-1 != curfrm && n != curfrm && exppos[n-1] >= 0 &&
fseek(expfp, exppos[curfrm=n-1], 0) == EOF) {
fprintf(stderr, "%s: seek error on exposure file\n", progname);
quit(1);
}
while (n > curfrm) { /* read exposure */
if (exppos[curfrm] < 0)
exppos[curfrm] = ftell(expfp);
if (fgets(expval, sizeof(expval), expfp) == NULL) {
fprintf(stderr, "%s: too few exposures\n",
vval(EXPOSURE));
quit(1);
}
curfrm++;
cp = fskip(expval); /* check format */
if (cp != NULL)
while (isspace(*cp))
*cp++ = '\0';
if (cp == NULL || *cp) {
fprintf(stderr,
"%s: exposure format error on line %d\n",
vval(EXPOSURE), curfrm);
quit(1);
}
}
return(expval); /* return value */
}
struct pslot *
findpslot(pid) /* find or allocate a process slot */
int pid;
{
register struct pslot *psempty = NULL;
register int i;
for (i = 0; i < npslots; i++) { /* look for match */
if (pslot[i].pid == pid)
return(pslot+i);
if (psempty == NULL && pslot[i].pid == 0)
psempty = pslot+i;
}
return(psempty); /* return emtpy slot (error if NULL) */
}
int
donecom(ps, pn, status) /* clean up after finished process */
PSERVER *ps;
int pn;
int status;
{
register PROC *pp;
register struct pslot *psl;
pp = ps->proc + pn;
if (pp->elen) { /* pass errors */
if (ps->hostname[0])
fprintf(stderr, "%s: ", ps->hostname);
fprintf(stderr, "Error output from: %s\n", pp->com);
fputs(pp->errs, stderr);
fflush(stderr);
if (ps->hostname[0])
status = 1; /* because rsh doesn't return status */
}
lastpserver = NULL;
psl = findpslot(pp->pid); /* check for bruncom() slot */
if (psl->pid) {
if (status) {
if (psl->rcvf != NULL) /* attempt recovery */
status = (*psl->rcvf)(psl->fout);
if (status) {
fprintf(stderr,
"%s: error rendering frame %d\n",
progname, psl->fout);
quit(1);
}
lastpserver = ps;
}
psl->pid = 0; /* free process slot */
} else if (status)
lastpserver = ps;
freestr(pp->com); /* free command string */
return(status);
}
int
serverdown() /* check status of last process server */
{
if (lastpserver == NULL || !lastpserver->hostname[0])
return(0);
if (pserverOK(lastpserver)) /* server still up? */
return(0);
delpserver(lastpserver); /* else delete it */
if (pslist == NULL) {
fprintf(stderr, "%s: all process servers are down\n",
progname);
quit(1);
}
return(1);
}
int
bruncom(com, fout, rf) /* run a command in the background */
char *com;
int fout;
int (*rf)();
{
int pid;
register struct pslot *psl;
if (noaction) {
if (!silent)
printf("\t%s\n", com); /* echo command */
return(0);
}
com = savestr(com); /* else start it when we can */
while ((pid = startjob(NULL, com, donecom)) == -1)
bwait(1);
if (!silent) { /* echo command */
PSERVER *ps;
int psn = pid;
ps = findjob(&psn);
printf("\t%s\n", com);
printf("\tProcess started on %s\n", phostname(ps));
fflush(stdout);
}
psl = findpslot(pid); /* record info. in appropriate slot */
psl->pid = pid;
psl->fout = fout;
psl->rcvf = rf;
return(pid);
}
bwait(ncoms) /* wait for batch job(s) to finish */
int ncoms;
{
int status;
if (noaction)
return;
while ((status = wait4job(NULL, -1)) != -1) {
serverdown(); /* update server status */
if (--ncoms == 0)
break; /* done enough */
}
}
int
pruncom(com, ppins, maxcopies) /* run a command in parallel over network */
char *com, *ppins;
int maxcopies;
{
int retstatus = 0;
int hostcopies;
char buf[10240], *com1, *s;
int status;
int pfd;
register int n;
register PSERVER *ps;
if (!silent)
printf("\t%s\n", com); /* echo command */
if (noaction)
return(0);
fflush(stdout);
/* start jobs on each server */
for (ps = pslist; ps != NULL; ps = ps->next) {
hostcopies = 0;
if (maxcopies > 1 && ps->nprocs > 1 && ppins != NULL) {
strcpy(com1=buf, com); /* build -PP command */
sprintf(com1+(ppins-com), " -PP %s/%s.persist",
vval(DIRECTORY), phostname(ps));
strcat(com1, ppins);
} else
com1 = com;
while (maxcopies > 0) {
s = savestr(com1);
if (startjob(ps, s, donecom) != -1) {
sleep(20);
hostcopies++;
maxcopies--;
} else {
freestr(s);
break;
}
}
if (!silent && hostcopies) {
if (hostcopies > 1)
printf("\t%d duplicate processes", hostcopies);
else
printf("\tProcess");
printf(" started on %s\n", phostname(ps));
fflush(stdout);
}
}
/* wait for jobs to finish */
while ((status = wait4job(NULL, -1)) != -1)
retstatus += status && !serverdown();
/* terminate parallel rpict's */
for (ps = pslist; ps != NULL; ps = ps->next) {
sprintf(buf, "%s/%s.persist", vval(DIRECTORY), phostname(ps));
if ((pfd = open(buf, O_RDONLY)) >= 0) {
n = read(pfd, buf, sizeof(buf)-1); /* get PID */
buf[n] = '\0';
close(pfd);
for (n = 0; buf[n] && !isspace(buf[n]); n++)
;
/* terminate */
sprintf(buf, "kill -ALRM %d", atoi(buf+n));
wait4job(ps, startjob(ps, buf, NULL));
}
}
return(retstatus);
}
runcom(cs) /* run a command locally and wait for it */
char *cs;
{
if (!silent) /* echo it */
printf("\t%s\n", cs);
if (noaction)
return(0);
fflush(stdout); /* flush output and pass to shell */
return(system(cs));
}
rmfile(fn) /* remove a file */
char *fn;
{
if (!silent)
#ifdef MSDOS
printf("\tdel %s\n", fn);
#else
printf("\trm -f %s\n", fn);
#endif
if (noaction)
return(0);
return(unlink(fn));
}
badvalue(vc) /* report bad variable value and exit */
int vc;
{
fprintf(stderr, "%s: bad value for variable '%s'\n",
progname, vnam(vc));
quit(1);
}
char *
dirfile(df, path) /* separate path into directory and file */
char *df;
register char *path;
{
register int i;
int psep;
for (i = 0, psep = -1; path[i]; i++)
if (path[i] == '/')
psep = i;
if (df != NULL)
if (psep == 0) {
df[0] = '/';
df[1] = '\0';
} else if (psep > 0) {
strncpy(df, path, psep);
df[psep] = '\0';
} else
df[0] = '\0';
return(path+psep+1);
}
int
getblur(double *bf) /* get # blur samples (and fraction) */
{
double blurf;
int nblur;
char *s;
if (!vdef(MBLUR)) {
if (bf != NULL)
*bf = 0.0;
return(0);
}
blurf = atof(vval(MBLUR));
if (blurf < 0.0)
blurf = 0.0;
if (bf != NULL)
*bf = blurf;
if (blurf <= FTINY)
return(0);
s = sskip(vval(MBLUR));
if (!*s)
return(DEF_NBLUR);
nblur = atoi(s);
if (nblur <= 0)
return(1);
return(nblur);
}