#ifndef lint
static const char RCSid[] = "$Id: ranimove.c,v 3.1 2003/02/22 02:07:30 greg Exp $";
#endif
/*
* Radiance object animation program
*
* Main program and control file handling.
*
* See ranimove.h and the ranimove(1) man page for 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 "ranimove.h"
#include
#include
int NVARS = NV_INIT; /* total number of variables */
VARIABLE vv[] = VV_INIT; /* variable-value pairs */
extern int nowarn; /* don't report warnings? */
int silent = 0; /* run silently? */
int quickstart = 0; /* time initial frame as well? */
int nprocs = 1; /* number of rendering processes */
int rtperfrm = 60; /* seconds to spend per frame */
double ndthresh = 2.; /* noticeable difference threshold */
int ndtset = 0; /* user threshold -- stop when reached? */
int fbeg = 1; /* starting frame */
int fend = 0; /* ending frame */
int fcur; /* current frame being rendered */
char lorendoptf[32]; /* low quality options file */
RAYPARAMS lorendparams; /* low quality rendering parameters */
char hirendoptf[32]; /* high quality options file */
RAYPARAMS hirendparams; /* high quality rendering parameters */
RAYPARAMS *curparams; /* current parameter settings */
int twolevels; /* low and high quality differ */
double mblur; /* vflt(MBLUR) */
double rate; /* vflt(RATE) */
char objtmpf[32]; /* object temporary file */
struct ObjMove *obj_move; /* object movements */
int haveprio = 0; /* high-level saliency specified */
int gargc; /* global argc for printargs */
char **gargv; /* global argv for printargs */
int
main(argc, argv)
int argc;
char *argv[];
{
int explicate = 0;
char *cfname;
int i;
progname = argv[0]; /* get arguments */
gargc = argc;
gargv = argv;
for (i = 1; i < argc && argv[i][0] == '-'; i++)
switch (argv[i][1]) {
case 't': /* seconds per frame */
rtperfrm = atoi(argv[++i]);
break;
case 'd': /* noticeable difference */
ndthresh = atof(argv[++i]);
ndtset = 1;
break;
case 'e': /* print variables */
explicate++;
break;
case 's': /* silent running */
silent++;
break;
case 'q': /* start quickly */
quickstart++;
break;
case 'w': /* turn off warnings */
nowarn++;
break;
case 'f': /* frame range */
switch (sscanf(argv[++i], "%d,%d", &fbeg, &fend)) {
case 2:
if ((fbeg <= 0 | fend < fbeg))
goto userr;
break;
case 1:
if (fbeg <= 0)
goto userr;
fend = 0;
break;
default:
goto userr;
}
break;
case 'n': /* number of processes */
nprocs = atoi(argv[++i]);
break;
default:
goto userr;
}
if (rtperfrm <= 0) {
if (!ndtset)
error(USER, "specify -d jnd with -t 0");
rtperfrm = 7*24*3600;
}
if (i != argc-1)
goto userr;
cfname = argv[i];
/* load variables */
loadvars(cfname);
/* check variables */
checkvalues();
/* load RIF if any */
if (vdef(RIF))
getradfile(vval(RIF));
/* set defaults */
setdefaults();
/* print variables */
if (explicate)
printvars(stdout);
/* run animation */
if (nprocs > 0)
animate();
/* all done */
if (lorendoptf[0])
unlink(lorendoptf);
if (hirendoptf[0])
unlink(hirendoptf);
if (objtmpf[0])
unlink(objtmpf);
return(0);
userr:
fprintf(stderr,
"Usage: %s [-n nprocs][-f beg,end][-t sec][-d jnd][-s][-w][-e] anim_file\n",
progname);
quit(1);
}
void
eputs(s) /* put string to stderr */
register char *s;
{
static int midline = 0;
if (!*s)
return;
if (!midline++) {
fputs(progname, stderr);
fputs(": ", stderr);
}
fputs(s, stderr);
if (s[strlen(s)-1] == '\n') {
fflush(stderr);
midline = 0;
}
}
void
setdefaults() /* set default values */
{
int nviews;
int decades;
char buf[256];
int i;
if (!vdef(OCTREEF)) {
sprintf(errmsg, "%s or %s must be defined",
vnam(OCTREEF), vnam(RIF));
error(USER, errmsg);
}
if (!vdef(VIEWFILE)) {
sprintf(errmsg, "%s must be defined", vnam(VIEWFILE));
error(USER, errmsg);
}
nviews = countviews();
if (!nviews)
error(USER, "no views in view file");
if (!vdef(END)) {
if (nviews == 1) {
sprintf(errmsg, "%s must be defined for single view",
vnam(END));
error(USER, errmsg);
}
sprintf(buf, "%d", nviews);
vval(END) = savqstr(buf);
vdef(END)++;
}
if (!fend)
fend = vint(END);
if (fbeg > fend)
error(USER, "begin after end");
if (!vdef(BASENAME)) {
decades = (int)log10((double)vint(END)) + 1;
if (decades < 3) decades = 3;
sprintf(buf, "frame%%0%dd", decades);
vval(BASENAME) = savqstr(buf);
vdef(BASENAME)++;
}
if (!vdef(RATE)) {
vval(RATE) = "8";
vdef(RATE)++;
}
rate = vflt(RATE);
if (!vdef(RESOLUTION)) {
vval(RESOLUTION) = "640";
vdef(RESOLUTION)++;
}
if (!vdef(MBLUR)) {
vval(MBLUR) = "0";
vdef(MBLUR)++;
}
mblur = vflt(MBLUR);
if (mblur > 1.)
mblur = 1.;
/* set up objects */
if (vdef(MOVE)) {
obj_move = (struct ObjMove *)malloc(
sizeof(struct ObjMove)*vdef(MOVE) );
if (obj_move == NULL)
error(SYSTEM, "out of memory in setdefaults");
for (i = 0; i < vdef(MOVE); i++)
setmove(&obj_move[i], nvalue(MOVE, i));
}
/* set up high quality options */
setrendparams(hirendoptf, vval(HIGHQ));
ray_save(&hirendparams);
/* set up low quality options */
setrendparams(lorendoptf, vval(LOWQ));
ray_save(&lorendparams);
curparams = &lorendparams;
twolevels = bcmp(&lorendparams, &hirendparams, sizeof(RAYPARAMS));
}
void
setmove(om, ms) /* assign a move object from spec. */
struct ObjMove *om;
char *ms;
{
char parname[128];
char *cp;
ms = nextword(parname, sizeof(parname), ms);
if (!parname[0])
goto badspec;
for (cp = parname; *cp; cp++)
if (isspace(*cp))
*cp = '_';
for (om->parent = (om - obj_move); om->parent--; )
if (!strcmp(parname, obj_move[om->parent].name))
break;
if (om->parent < 0 &&
strcmp(parname, ".") && strcmp(parname, VOIDID)) {
sprintf(errmsg, "undefined parent object '%s'", parname);
error(USER, errmsg);
}
ms = nextword(om->name, sizeof(om->name), ms);
if (!om->name[0])
goto badspec;
for (cp = om->name; *cp; cp++)
if (isspace(*cp))
*cp = '_';
ms = nextword(om->xf_file, sizeof(om->xf_file), ms);
if (!strcmp(om->xf_file, "."))
om->xf_file[0] = '\0';
if (!om->xf_file[0]) {
om->xfs[0] = '\0';
} else if (om->xf_file[0] == '-') {
strcpy(om->xfs, om->xf_file);
om->xf_file[0] = '\0';
}
ms = nextword(om->spec, sizeof(om->spec), ms);
if (om->spec[0]) {
if (!strcmp(om->spec, ".") || !strcmp(om->spec, VOIDID))
om->spec[0] = '\0';
ms = nextword(om->prio_file, sizeof(om->prio_file), ms);
} else
om->prio_file[0] = '\0';
if (om->prio_file[0]) {
if (isflt(om->prio_file)) {
om->prio = atof(om->prio_file);
om->prio_file[0] = '\0';
haveprio |= (om->prio < 0.95 | om->prio > 1.05);
} else
haveprio = 1;
} else
om->prio = 1.;
om->cfm = -1;
return;
badspec:
error(USER, "bad object specification");
}
void
setrendparams(optf, qval) /* set global rendering parameters */
char *optf;
char *qval;
{
char *argv[1024];
char **av = argv;
int ac, i, rval;
av[ac=0] = NULL;
/* load options from file, first */
if (optf != NULL && *optf) {
ac = wordfile(av, optf);
if (ac < 0) {
sprintf(errmsg, "cannot load options file \"%s\"",
optf);
error(SYSTEM, errmsg);
}
}
/* then from options string */
if (qval != NULL && qval[0] == '-')
ac += wordstring(av+ac, qval);
/* start with default parameters */
ray_defaults(NULL);
/* set what we have */
for (i = 0; i < ac; i++) {
while ((rval = expandarg(&ac, &av, i)) > 0)
;
if (rval < 0) {
sprintf(errmsg, "cannot expand '%s'", av[i]);
error(SYSTEM, errmsg);
}
if (!strcmp(av[i], "-w")) {
nowarn++;
continue;
}
rval = getrenderopt(ac-i, av+i);
if (rval >= 0) {
i += rval;
continue;
}
sprintf(errmsg, "bad render option at '%s'", av[i]);
error(USER, errmsg);
}
}
void
getradfile(rfargs) /* run rad and get needed variables */
char *rfargs;
{
static short mvar[] = {OCONV,OCTREEF,RESOLUTION,EXPOSURE,-1};
char combuf[256];
register int i;
register char *cp;
char *pippt;
/* create rad command */
strcpy(lorendoptf, "ranim0.opt");
sprintf(combuf,
"rad -v 0 -s -e -w %s %s oconv=-f OPTFILE=%s | egrep '^[ \t]*(NOMATCH",
rfargs,
(vdef(LOWQ) && vval(LOWQ)[0]!='-') ? vval(LOWQ) : "",
lorendoptf);
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 {
strcpy(cp, ")[ \t]*=' > ranimove.var");
cp += 11; /* point to file name */
}
system(combuf); /* ignore exit code */
if (pippt == NULL) { /* load variables and remove file */
loadvars(cp);
unlink(cp);
}
if (!vdef(HIGHQ) || vval(HIGHQ)[0]=='-') {
strcpy(hirendoptf, lorendoptf);
return;
}
/* get high quality options */
strcpy(hirendoptf, "ranim1.opt");
sprintf(combuf, "rad -v 0 -s -w %s %s OPTFILE=%s",
rfargs, vval(HIGHQ), hirendoptf);
system(combuf);
}
void
animate() /* run through animation */
{
int rpass;
if (fbeg > 1) /* synchronize transforms */
getoctspec(fbeg-1);
for (fcur = fbeg; fcur <= fend; fcur++) {
if (!silent)
printf("Frame %d:\n", fcur);
/* base rendering */
init_frame();
/* refinement */
for (rpass = 0; refine_frame(rpass); rpass++)
;
/* final filter pass */
filter_frame();
/* output frame */
send_frame();
}
/* free resources */
free_frame();
if (nprocs > 1)
ray_pdone(1);
else
ray_done(1);
getview(0);
getexp(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(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 int curfrm = 0;
register char *cp;
if (n == 0) { /* signal to close file */
if (expfp != NULL) {
fclose(expfp);
expfp = NULL;
}
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) {
sprintf(errmsg, "cannot open exposure file \"%s\"",
vval(EXPOSURE));
error(SYSTEM, errmsg);
}
curfrm = 0;
}
if (curfrm > n) { /* rewind if necessary */
rewind(expfp);
curfrm = 0;
}
while (n > curfrm) { /* read to line */
if (fgets(expval, sizeof(expval), expfp) == NULL) {
sprintf(errmsg, "%s: too few exposures",
vval(EXPOSURE));
error(USER, errmsg);
}
if (strlen(expval) == sizeof(expval)-1)
goto formerr;
curfrm++;
}
cp = fskip(expval); /* check format */
if (cp != NULL)
while (isspace(*cp))
*cp++ = '\0';
if (cp == NULL || *cp)
goto formerr;
return(expval); /* return value */
formerr:
sprintf(errmsg, "%s: exposure format error on line %d",
vval(EXPOSURE), curfrm);
error(USER, errmsg);
}
double
expspec_val(s) /* get exposure value from spec. */
char *s;
{
double expval;
if (s == NULL || !*s)
return(1.0);
expval = atof(s);
if ((s[0] == '+' | s[0] == '-'))
return(pow(2.0, expval));
return(expval);
}
char *
getoctspec(n) /* get octree for the given frame */
int n;
{
static char combuf[1024];
int cfm = 0;
int uses_inline;
FILE *fp;
int i;
/* is octree static? */
if (!vdef(MOVE))
return(vval(OCTREEF));
/* done already */
if (n == cfm)
return(combuf);
/* else create object file */
strcpy(objtmpf, "movinobj.rad");
fp = fopen(objtmpf, "w");
if (fp == NULL) {
sprintf(errmsg, "cannot write to moving objects file '%s'",
objtmpf);
error(SYSTEM, errmsg);
}
uses_inline = 0;
for (i = 0; i < vdef(MOVE); i++) {
int inlc = (obj_move[i].spec[0] == '!');
if (!obj_move[i].spec[0])
continue;
if (inlc)
fprintf(fp, "%s %d \\\n\t| xform",
obj_move[i].spec, n);
else
fputs("!xform", fp);
fprintf(fp, " -n %s", getobjname(&obj_move[i]));
fputs(getxf(&obj_move[i], n), fp);
if (!inlc)
fprintf(fp, " %s\n", obj_move[i].spec);
else
fputc('\n', fp);
uses_inline |= inlc;
}
if (fclose(fp) == EOF)
error(SYSTEM, "error writing moving objects file");
if (uses_inline)
sprintf(combuf, "!oconv %s -f -i '%s' %s",
vdef(OCONV) ? vval(OCONV) : "",
vval(OCTREEF), objtmpf);
else
sprintf(combuf, "!xform -f %s | oconv -f -i '%s' -",
objtmpf, vval(OCTREEF));
return(combuf);
}
char *
getobjname(om) /* get fully qualified object name */
register struct ObjMove *om;
{
static char objName[512];
register char *cp = objName;
strcpy(cp, om->name);
while (om->parent >= 0) {
while (*cp) cp++;
*cp++ = '@';
om = &obj_move[om->parent];
strcpy(cp, om->name);
}
return(objName);
}
char *
getxf(om, n) /* get total transform for object */
register struct ObjMove *om;
int n;
{
static char xfsbuf[4096];
char *xfp;
int framestep = 0;
XF oxf;
FILE *fp;
char abuf[512];
char *av[64];
int ac;
int i;
register char *cp;
/* get parent transform, first */
if (om->parent >= 0)
xfp = getxf(&obj_move[om->parent], n);
else
*(xfp = xfsbuf + sizeof(xfsbuf)-1) = '\0';
/* get transform spec. & priority */
if (om->cfm != n) {
if (om->xf_file[0]) {
fp = fopen(om->xf_file, "r");
if (fp == NULL) {
sprintf(errmsg,
"cannot open transform file '%s'",
om->xf_file);
error(SYSTEM, errmsg);
}
for (i = 0; i < n; i++)
if (fgetline(om->xfs, sizeof(om->xfs), fp)
== NULL) {
sprintf(errmsg,
"too few transforms in file '%s'",
om->xf_file);
error(USER, errmsg);
}
fclose(fp);
}
strcpy(cp=abuf, om->xfs);
ac = 0;
for ( ; ; ) {
while (isspace(*cp))
*cp++ = '\0';
if (!*cp)
break;
av[ac++] = cp;
while (*++cp && !isspace(*cp))
;
}
av[ac] = NULL;
if (xf(&oxf, ac, av) != ac ||
fabs(oxf.sca) <= FTINY) {
sprintf(errmsg, "bad transform args: %s",
om->xfs);
error(USER, errmsg);
}
copymat4(om->xfm, oxf.xfm);
if (om->prio_file[0]) {
fp = fopen(om->prio_file, "r");
if (fp == NULL) {
sprintf(errmsg,
"cannot open priority file '%s'",
om->prio_file);
error(SYSTEM, errmsg);
}
for (i = 0; i < n; i++)
if (fgets(abuf, sizeof(abuf), fp)
== NULL) {
sprintf(errmsg,
"too few priorities in file '%s'",
om->prio_file);
error(USER, errmsg);
}
fclose(fp);
cp = fskip(abuf);
if (cp != NULL)
while (isspace(*cp))
*cp++ = '\0';
if (cp == NULL || *cp) {
sprintf(errmsg,
"priority '%s' in file '%s' not a number",
abuf, om->prio_file);
error(USER, errmsg);
}
om->prio = atof(abuf);
}
framestep = (n == om->cfm + 1);
om->cfm = n;
}
/* prepend to parent transform */
if (om->xfs[0]) {
i = strlen(om->xfs);
if (xfp - i <= xfsbuf)
error(INTERNAL, "transform too long in getxf");
cp = om->xfs + i;
while (i--)
*--xfp = *--cp;
*--xfp = ' ';
}
if (framestep)
copymat4(oxf.xfm, om->cxfm);
if (om->parent >= 0) {
multmat4(om->cxfm, om->xfm, obj_move[om->parent].cxfm);
om->cprio = obj_move[om->parent].cprio * om->prio;
} else {
copymat4(om->cxfm, om->xfm);
om->cprio = om->prio;
}
/* XXX bxfm relies on call order */
if (framestep)
if (invmat4(om->bxfm, om->cxfm))
multmat4(om->bxfm, om->bxfm, oxf.xfm);
else
setident4(om->bxfm);
/* all done */
return(xfp);
}
int
getmove(obj) /* find matching move object */
OBJECT obj;
{
static int lasti;
static OBJECT lasto = OVOID;
char *onm, *objnm;
int len, len2;
register int i;
if (obj == OVOID)
return(-1);
if (obj == lasto)
return(lasti);
/* look for matching object */
onm = objptr(obj)->oname;
for (i = vdef(MOVE); i--; ) {
objnm = obj_move[i].name;
len = strlen(objnm);
if (!strncmp(onm, objnm, len)) {
if ((obj_move[i].parent < 0 & onm[len] == '.'))
break;
objnm = getobjname(&obj_move[i]) + len;
len2 = strlen(objnm);
if (!strncmp(onm+len, objnm, len2) && onm[len+len2] == '.')
break;
}
}
lasto = obj; /* cache what we found */
return(lasti = i);
}
double
obj_prio(obj) /* return priority for object */
OBJECT obj;
{
int moi;
if (obj == OVOID || (moi = getmove(obj)) < 0)
return(1.0);
return(obj_move[moi].cprio);
}
double
getTime() /* get current time (CPU or real) */
{
struct timeval time_now;
/* return CPU time if one process */
if (nprocs == 1)
return((double)clock()*(1.0/(double)CLOCKS_PER_SEC));
/* otherwise, return wall time */
gettimeofday(&time_now, NULL);
return((double)time_now.tv_sec + 1e-6*(double)time_now.tv_usec);
}