#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'Doc/Guide/lights.tex' <<'END_OF_FILE' X\chapter{Light Sources} X XThe lighting in a scene is determined by the number, type, and nature Xof the light sources defined in the input file. Available light sources Xrange from simple directional sources to more realistic but computationally Xcostly quadrilateral area light sources. Typically, you will want to use Xpoint or directional light sources while developing images. When Xfinal renderings are made, these simple light sources may be replaced by Xthe more complex ones. X XNo matter what kind of light source you use, you will need to Xspecify its intensity. XIn this chapter, an {\em Intensity\/} is either a red-green-blue triple Xindicating the color of the light source, or a single value that is Xinterpreted as the intensity of a ``white'' light. XIn the current version of {\em rayshade}, the intensity of a light does Xnot decrease as Xone moves farther from it. X XIf you do not define a light source, \rayshade will create a directional Xlight source of intensity 1.0 defined by the vector (1., -1., 1.). XThis default light source is designed to work well when default Xviewing parameters and surface values are being used. X XYou may define any number of light sources, but keep in mind that Xit will require more time to render images that include many Xlight sources. It should also be noted that the light sources themselves Xwill not appear in the image, even if they are placed in frame. X X\section{Light Source Types} X XThe amount of ambient light present in a scene is controlled by a Xpseudo light source of type {\em ambient}. X X\begin{defkey}{light}{{\em Intensity\/} ambient} X Define the amount of ambient light present in the entire X scene. X\end{defkey} X XThere is only one ambient light source; its default intensity is X{1, 1, 1}. If more than one ambient light source is defined, Xonly the last instance is used. A surface's ambient color Xis multiplied by the intensity of the ambient source to give the Xtotal ambient light reflected from the surface. X XDirectional sources are described by a direction alone, and are useful Xfor modeling light sources that are effectively infinitely far away Xfrom the objects they illuminate. X X\begin{defkey}{light}{{\em Intensity\/} {\tt directional} \evec{direction}} X Define a light source with the given intensity that is X defined to be in the given direction from every point X it illuminates. The direction need not be normalized. X\end{defkey} X XPoint sources are defined as a single point in space. They produce Xshadows with sharp edges and are a good replacement for extended Xand other computationally expensive light source. X X\begin{defkey}{light}{{\em Intensity\/} {\tt point} \evec{pos}} X Place a point light source with the given intensity at the X given position. X\end{defkey} X XSpotlights are useful for creating dramatic localized lighting effects. XThey are defined by their position, the direction in which they Xare pointing, and the width of the beam of light they produce. X X\begin{defkey}{light}{{\em Intensity\/} {\tt spot} \evec{pos} \evec{to} X {$\alpha$} [ $\theta_{in}$ $\theta_{out}$ ]} X Place a spotlight at \evec{pos}, oriented as to be pointing at X \evec{to}. The intensity of the light falls off as X $(cosine \theta)^{\alpha}$, where $\theta$ is the angle between the X spotlight's main axis and the vector from the spotlight to the X point being illuminated. $\theta_{in}$ and X $\theta_{out}$ may be used to control the radius of the cone of light X produced by the spotlight. X\end{defkey} X$\theta_{in}$ is the the angle at which Xthe light source begins to be attenuated. At $\theta_{out}$, Xthe spotlight intensity is zero. XThis affords control Xover how ``fuzzy'' the edges of the spotlight are. If neither angle Xis given, they both are effectively set to 180 degrees. X XExtended sources are meant to model spherical light sources. Unlike Xpoint sources, extended sources actually possess a radius, and as such Xare capable or producing shadows with fuzzy edges ({\em penumbrae}). If Xyou do not specifically desire penumbrae in your image, use a point Xsource instead. X X\begin{defkey}{light}{{\em Intensity\/} {\tt extended} {\em radius} \evec{pos} } X Create an extended light source at the given position and with X the given {\em radius}. X\end{defkey} XThe shadows cast by Xextended sources are modeled by taking samples of the source at Xdifferent locations on its surface. When the source is partially Xhidden from a given point in space, that point is in partial shadow Xwith respect to the extended source, and the sampling process is Xusually able to determine this fact. X XQuadrilateral light sources are computationally more expensive than extended Xlight sources, but are more flexible and produce more realistic results. XThis is due to the fact that an area source is approximated by a Xnumber of point sources whose positions are jittered to reduce aliasing. XBecause each of these point sources has shading calculations performed Xindividually, area sources may be placed relatively close to the Xobjects it illuminates, and a reasonable image will result. X X\begin{defkey}{light}{{\em Intensity\/} {\tt area} \evec{p1} \evec{p2} {\em usamp} X \evec{p3} {\em vsamp}} X Create a quadrilateral area light source. X The $u$ axis X is defined by the vector from \evec{p1} to \evec{p2}. Along X this axis a total of {\em usamp} samples will be taken. X The $v$ axis of the light source is defined by the vector X from \evec{p1} to \evec{p3}. Along this axis a total of X {\em vsamp} samples will be taken. X\end{defkey} XThe values of {\em usamp} and {\em vsamp} are usually chosen to be Xproportional to the lengths of the $u$ and $v$ axes. Choosing a Xrelatively high number of samples will result in a good approximation Xto a ``real'' quadrilateral source. However, because complete Xlighting calculations are performed for each sample, Xthe computational cost is directly proportional to the product Xof {\em usamp} and {\em vsamp}. X X\section{Shadows} X XIn order to determine the color of a point on the surface Xof any object, it is necessary Xto determine if that point is in shadow with respect to each Xdefined light source. If the point is totally in shadow with respect to Xa light source, then the light source makes no contribution to the Xpoint's final color. X XThis shadowing determination is made by tracing rays from the point Xof intersection to each light source. These ``shadow feeler'' rays Xcan add substantially to the overall rendering time. This is especially Xtrue if extended or area light sources are used. If at any point you Xwish to disable shadow determination on a global scale, there is Xa command-line option ({\tt -n}) that allows you to do so. It is also Xpossible Xto disable the casting of shadows onto given objects through the use Xof the {\tt noshadow} keyword in surface descriptions. In addition, Xthe {\tt noshadow} keyword may be given following the definition Xof a light source, causing the light source to cast no shadows onto Xany surface. X XDetermining if a point is in shadow with respect to a light source Xis relatively simple if all the objects in a scene are opaque. In Xthis case, one simply traces a ray from the point to the light source. XIf the ray hits an object before it reaches the light source, then Xthe point is in shadow. X XShadow determination Xbecomes more complicated if there are one or more objects with Xnon-zero transparency between the point and the light source. XTransparent objects may not completely block the light from a source, Xbut merely attenuate it. In such cases, it is necessary to compute the Xamount of attenuation at each intersection and to continue Xthe shadow ray until it either reaches the light source or until Xthe light is completely attenuated. X XBy default, \rayshade computes shadow attenuation by assuming Xthat the index of refraction of the transparent object is the Xsame as that of the medium through which the ray is traveling. XTo disable Xpartial shadowing due to transparent objects, the {\em shadowtransp} Xkeyword should be given somewhere in the input file. X X\begin{defkey}{shadowtransp}{} X The intensity of light striking a point is {\em not} affected by X intervening transparent objects. X\end{defkey} XIf you enclose an object behind a transparent surface, and you wish Xthe inner object to be illuminated, you must not use the {\tt shadowtransp} Xkeyword or the {\tt -o} option. END_OF_FILE if test 8326 -ne `wc -c <'Doc/Guide/lights.tex'`; then echo shar: \"'Doc/Guide/lights.tex'\" unpacked with wrong size! fi # end of 'Doc/Guide/lights.tex' fi if test -f 'Doc/quickref.txt' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Doc/quickref.txt'\" else echo shar: Extracting \"'Doc/quickref.txt'\" \(9588 characters\) sed "s/^X//" >'Doc/quickref.txt' <<'END_OF_FILE' X Rayshade Quick Reference X X------------------------------------------------------------------------------- XKey: X[thing] Optional item Production XThing Number or String (thing) Default value(s) Xthing Keyword X------------------------------------------------------------------------------- X XReals and integers may be written in exponential notation, with or without a Xdecimal point. Reals are truncated to integers when need be. Numbers may also Xbe written as expressions surrounded by a matched pair of parentheses. XSubexpressions may be parenthesized to control order of evaluation. Variables Xmay be defined and used in parenthesized expressions. Predefined variables Xinclude time (current time) and frame (current frame number, 0 - frames-1), pi, Xdtor (pi/180), rotd (180/pi). Available operators are '+' (addition), X'-' (subtraction and negation), '*' (multiplication), '/' (division), X'%' (remainder), '^' (exponentiation). Functions include sin, cos, tan, asin, Xacos, atan, sqrt, hypot. X XStrings are written as non-quoted strings that may include include the Xspecial characters '/' ("slash"), '-' ("dash"), '_' ("underscore), and '.' X("period"), in addition to upper and lowercase letters and non-leading digits. X X------------------------------------------------------------------------------- XCommand-line options (override options set in input file): X X-A frame First frame to render X-a Toggle alpha channel -C cutoff Adaptive tree cutoff X-c Continued rendering -D depth Maximum ray tree depth. X-E eye_sep Eye separation -e Exponential RLE output X-F freq Report frequency -f Flip triangle normals X-G gamma Gamma exponent -g Use gaussian filter X-h Help -j Toggle jittered sampling X-l Render left eye view -m Produce sample map X-N frames Total frames to render -n No shadows X-O outfile Output file name -o Toggle opaque shadows X-P cpp-args Arguments for cpp -p Preview-quality X-q Run quietly -R xres yres Resolution X-r Right eye view -S samples Use Samples^2 samples X-s Toggle shadow caching -T r g b Contrast threshold X-V filename Verbose file output -v Verbose output X-W lx ly hx hy Render subwindow X------------------------------------------------------------------------------- X XFile: /* Input file consists of...*/ X [ ... ] X XItem: X X X X X X X XObjItem: /* Items used in object definition blocks */ X X X X X XViewing: X eyep Xpos Ypos Zpos /* Eye position (0 -10 0) */ X lookp Xpos Ypos Zpos /* Look position (0 0 0) */ X up Xup Yup Zup /* "up" vector (0 0 1) */ X fov Hfov [Vfov] /* Field of view in degrees (horiontal=45) */ X aperture Width /* Aperture width (0) */ X focaldist Distance /* focal distance (|eyep - lookp|) */ X shutter Speed /* Shutter speed (0 --> no blur) */ X framelength Length /* Length of a singelf frame (1) */ X screen Xsize Ysize /* Screen size */ X window Xmin Ymin Xmax Ymax /* Window (0 0 xsize-1 ysize-1) */ X eyesep Separation /* eye separation (0) */ X XSurfDef: /* Give a name to a set of surface attributes. */ X surface Name [ ...] X XSurface: /* Surface specification */ X /* Use gven attributes */ X Surfname [ ...] /* Use named surface w/ optional mods. */ X cursurf [ ...] /* Use cur. surface w/mods - see ApplySurf */ X XSurfSpec: /* Surface attribute specification */ X ambient R G B /* Ambient contribution */ X diffuse R G B /* Diffuse color */ X specular R G B /* Specular color */ X specpow Exponent /* Phong exponent */ X body R G B /* Body color */ X extinct Coef /* Extinction coefficient */ X transp Ktr /* Transparency */ X reflect Kr /* Reflectivity */ X index N /* Index of refraction */ X translu Ktl R G B Stpow /* Translucency, transmit diffuse, spec exp */ X noshadow /* No shadows cast on this surface */ X XEffect: /* Atmospheric Effects */ X mist R G B Rtrans Gtrans Btrans Zero Scale X fog R G B Rtrans Gtrans Btrans X XAtmosphere: /* Global atmosphere */ X atmosphere [Index] [...] /* Global index, effects */ X XApplySurf: X applysurf /* apply surf to all following objs w/o surface */ X XInstance: /* Instance of an object */ X [] [] X XObject: X Primitive /* Primitive object */ X Aggregate /* Named aggregate */ X XObjDef: /* define a named object */ X name Objname X XPrimitive: /* Primitive object */ X plane [] Xpos Ypos Zpos Xnorm Ynorm Znorm X disc [] Radius Xpos Ypos Zpos Xnorm Ynorm Znorm X sphere [] Radius Xpos Ypos Zpos X triangle [] Xv1 Yv1 Zv1 X Xv2 Yv2 Zv2 Xv3 Yv3 Zv3/* flat-shaded triangle */ X triangle [] Xv1 Yv1 Zv1 Xn1 Yn1 Zn1 X Xv2 Yv2 Zv2 Xn2 Yn2 Zn2 X Xv3 Yv3 Zv3 Xn3 Yn3 Zn3/* Phong-shaded triangle */ X polygon [] Xv1 Yv1 Zv1 X Xv2 Yv2 Zv2 Xv3 Yv3 Zv3 [Xv3 Yv4 Zv4 ...] X box [] Xlow Ylow Zlow X Xhi Yhi Zhi X cylinder [] Radius Xbase Ybase Zbase Xapex Yapex Zapex X cone [] Rbase Xbase Ybase Zbase Rapex Xapex Yapex Zapex X torus [] Rswept Rtube Xpos Ypos Zpos Xnorm Ynorm Znorm X blob [] Thresh Stren Rad Xpos Ypos Zpos X [Stren Rad X Y Z ...] X heightfield [] Filename X XAggregate: X Grid X List X Csg X XGrid: X grid X Y Z [ ...] end X XList: X list [ ...] end X XCsg: X union [ ...] end X intersect [ ...] end X difference [ ...] end X X /* CSG will only work properly when applied to closed objects, e.g.: X * sphere, box, torus, blob, closed Aggregate, other Csg object X */ X XTransforms: /* Transformations */ X translate Xtrans Ytrans Ztrans X scale Xscale Yscale Zscale X rotate Xaxis Yaxis Zaxis Degrees X transform A B C X D E F X G H I X [Xt Yt Zt] X XTextures: X texture [Transforms] [ [Transforms] ...] X XTexture: X checker X blotch Scale X bump Bumpscale X marble [Colormapname] X fbm Offset Scale H Lambda Octaves Thresh [Colormapname] X fbmbump Offset Scale H Lambda Octaves X wood X gloss Glossiness X cloud Offset Scale H Lambda Octaves Cthresh Lthresh Transcale X sky Scale H Lambda Octaves Cthresh Lthresh X stripe Width Bumpscale X image Imagefile [ [ ...]] X XImageTextOption: X component X range Lo Hi X smooth X textsurf X tile U V X X XSurfComp: X ambient X diffuse X reflect X transp X specular X specpow X XMapping: X map uv X map cylindrical [Xorigin Yorigin Zorigin Xup Yup Zup Xu Yu Zu] X map linear [Xorigin Yorigin Zorigin Xv Yv Zv Xu Yu Zu] X map spherical [Xorigin Yorigin Zorigin Xup Yup Zup Xu Yu Zu] X XLight: X light R G B [noshadow] X light Intensity [noshadow] X XLightType: X ambient X point Xpos Ypos Zpos X directional Xdir Ydir Zdir X extended Xpos Ypos Zpos Radius X spot Xpos Ypos Zpos Xat Yat Zat Coef Thetain Thetaout X area Xorigin Yorigin Zorigin Xu Yu Zu Usamples Xv Yv Zv Vsamples X XRenderOption: X samples Nsamp [jitter | nojitter] X /* Use Nsamp^2 pixel samples (3^2 jittered) */ X background R G B /* Background color (0 0 0) */ X outfile Filename /* Output file name (written to stdout) */ X frames Nframes /* Number of frames to render (1) */ X starttime Time /* Time corresponding to start of frame 0 */ X contrast R G B /* Maximum contrast w/o supersampling */ X maxdepth Depth /* Maximum ray tree depth (5) */ X cutoff Factor /* Minium spawned ray contribution (.001) */ X report [verbose] [quiet] [Freq] [Statfile] X /* Reporting mode (false false 10 stderr) */ X shadowtransp /* Toggle object opacity affects shadows */ X XDefinition: /* Variable definition */ X define Name Expr /* Assign value for Name */ END_OF_FILE if test 9588 -ne `wc -c <'Doc/quickref.txt'`; then echo shar: \"'Doc/quickref.txt'\" unpacked with wrong size! fi # end of 'Doc/quickref.txt' fi if test -f 'libray/libobj/geom.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libray/libobj/geom.c'\" else echo shar: Extracting \"'libray/libobj/geom.c'\" \(9132 characters\) sed "s/^X//" >'libray/libobj/geom.c' <<'END_OF_FILE' X/* X * object.c X * X * Copyright (C) 1989, 1991, Craig E. Kolb X * All rights reserved. X * X * This software may be freely copied, modified, and redistributed X * provided that this copyright notice is preserved on all copies. X * X * You may not distribute this software, in whole or in part, as part of X * any commercial product without the express consent of the authors. X * X * There is no warranty or other guarantee of fitness of this software X * for any purpose. It is provided solely "as is". X * X * $Id: geom.c,v 4.0 91/07/17 14:37:47 kolb Exp Locker: kolb $ X * X * $Log: geom.c,v $ X * Revision 4.0 91/07/17 14:37:47 kolb X * Initial version. X * X */ X#include "geom.h" X#include "list.h" X#include "libcommon/sampling.h" X Xstatic void GeomBounds(), GeomBoundsAnimated(); Xvoid GeomResolveAssoc(); /* probably static */ X XGeom * XGeomCreate(objptr, methods) XGeomRef objptr; XMethods *methods; X{ X Geom *obj; X X if (objptr == (GeomRef)NULL) X return (Geom *)NULL; X X obj = (Geom *)share_calloc(1, sizeof(Geom)); X obj->obj = objptr; X obj->methods = methods; X obj->animtrans = FALSE; X obj->trans = obj->transtail = (Trans *) NULL; X obj->frame = -1; /* impossible value */ X BoundsInit(obj->bounds); X#ifdef SHAREDMEM X /* X * If the counter is in shared memory, processes will X * be modifying it left-and-right. So, we cheat and X * make counter a pointer to a non-shared location and X * store the value there. X */ X new->counter = (unsigned long *)Malloc(sizeof(unsigned long)); X *new->counter = 0; X#endif X return obj; X} X X/* X * Return a copy of the given object. X * Note that surface, texturing, and transformation information X * is copied by reference. X */ XGeom * XGeomCopy(obj) XGeom *obj; X{ X Geom *new; X X new = GeomCreate(obj->obj, obj->methods); X /* Share texturing, name, #prims, surface info */ X new->name = obj->name; X new->texture = obj->texture; X new->surf = obj->surf; X new->prims = obj->prims; X new->trans = obj->trans; X new->animtrans = obj->animtrans; X new->transtail = obj->transtail; X /* copy bounds */ X BoundsCopy(obj->bounds, new->bounds); X return new; X} X X/* X * Report bounding box and number of primitives in object. X */ Xvoid XAggregatePrintInfo(obj, fp) XGeom *obj; XFILE *fp; X{ X if (fp) { X if (obj->name && obj->name[0]) X fprintf(fp,"%s \"%s\":\n", GeomName(obj), obj->name); X else X fprintf(fp,"%s:\n", GeomName(obj)); X if (!UNBOUNDED(obj)) X BoundsPrint(obj->bounds, fp); X fprintf(fp,"\t%lu primitive%c\n",obj->prims, X obj->prims == 1 ? ' ' : 's'); X } X} X X/* X * Convert the given object from a linked list of objects to X * the desired aggregate type. X */ Xint XAggregateConvert(obj, objlist) XGeom *obj, *objlist; X{ X if (!IsAggregate(obj)) { X RLerror(RL_ABORT, "A %s isn't an aggregate.\n", X GeomName(obj)); X return 0; X } X X return (*obj->methods->convert)(obj->obj, objlist); X} X X/* X * This should really be called X * GeomInitialize X * or something. X */ Xvoid XGeomComputeBounds(obj) XGeom *obj; X{ X if (obj->frame == Sampling.framenum) X return; X X if (!obj->animtrans) { X /* X * If it isn't animated, X * just compute bbox directly X */ X GeomBounds(obj, obj->bounds); X } else { X /* X * Animated things are gonna get a bbox X * which is large enough to enclose all X * the places where the object goes. X */ X GeomBoundsAnimated(obj); X } X /* X * Enlarge by EPSILON in each direction just to X * be on the safe side. X */ X obj->bounds[LOW][X] -= EPSILON; X obj->bounds[HIGH][X] += EPSILON; X obj->bounds[LOW][Y] -= EPSILON; X obj->bounds[HIGH][Y] += EPSILON; X obj->bounds[LOW][Z] -= EPSILON; X obj->bounds[HIGH][Z] += EPSILON; X /* X * Mark the fact that that the obj is initialized X * for this frame. X */ X obj->frame = Sampling.framenum; X obj->counter = 0; X} X Xstatic void XGeomBoundsAnimated(obj) XGeom *obj; X{ X int i, m; X Float newbounds[2][3]; X Float window, subwindow, jitter, subjitter; X X /* X * For each possible screen sample, X * choose TIME_SUB_SAMPLES times and recompute the X * bounds of obj at that time, X * expanding the computed bounding box appropriately. X */ X BoundsInit(obj->bounds); X jitter = Sampling.shutter / Sampling.totsamples; X subjitter = jitter / (Float)TIME_SUB_SAMPLES; X window = Sampling.starttime; X for (i = 0; i < Sampling.totsamples; i++, window += jitter) { X subwindow = window; X for (m = 0; m < TIME_SUB_SAMPLES; m++, subwindow += subjitter) { X /* X * Set the current time. X */ X TimeSet(subwindow + subjitter*nrand()); X /* X * Resolve the objects geometric associations X */ X GeomResolveAssoc(obj); X /* X * Compute bounds and expand current bounds. X */ X GeomBounds(obj, newbounds); X BoundsEnlarge(obj->bounds, newbounds); X } X } X /* X * Also sample at time extremes, as for many X * movements, extremes occur at beginning/end times. X */ X TimeSet(Sampling.starttime); X GeomResolveAssoc(obj); X GeomBounds(obj, newbounds); X BoundsEnlarge(obj->bounds, newbounds); X X TimeSet(Sampling.starttime + Sampling.shutter); X GeomResolveAssoc(obj); X GeomBounds(obj, newbounds); X BoundsEnlarge(obj->bounds, newbounds); X} X Xvoid XGeomResolveAssoc(obj) XGeom *obj; X{ X /* X * PrimResolveAssoc(obj); X */ X TransResolveAssoc(obj->trans); X} X X/* X * Set "bounds" of object to be the extent of the primitive. X */ Xstatic void XGeomBounds(obj, bounds) XGeom *obj; XFloat bounds[2][3]; X{ X Trans *trans; X X if (!obj || !obj->methods->bounds) X RLerror(RL_ABORT, "Can't compute bounds of \"%s\".\n", X GeomName(obj)); X (*obj->methods->bounds) (obj->obj, bounds); X if (obj->trans) { X for (trans = obj->trans; trans; trans = trans->next) X BoundsTransform(&trans->trans, bounds); X } X} X Xchar * XGeomName(obj) XGeom *obj; X{ X if (obj->methods->name) X return (*obj->methods->name)(); X X return "unknown"; X} X Xvoid XGeomStats(obj, tests, hits) XGeom *obj; Xunsigned long *tests, *hits; X{ X if (obj && obj->methods->stats) X (*obj->methods->stats)(tests, hits); X else { X *tests = *hits = 0; X } X} X X/* X * Push an object onto the head of the given stack, returning X * the new head. X */ XGeomList * XGeomStackPush(obj, list) XGeom *obj; XGeomList *list; X{ X GeomList *new; X /* X * Pretty simple. X * Make new element point to old head and return new head. X */ X new = (GeomList *)Malloc(sizeof(GeomList)); X new->obj = obj; X new->next = list; X return new; X} X X/* X * Pop the topmost object off of the given stack, returning the new head. X * The old head is freed, but the object it points to is not. X */ XGeomList * XGeomStackPop(list) XGeomList *list; X{ X GeomList *ltmp; X X ltmp = list->next; /* Save new head. */ X free((voidstar)list); /* Free old head. */ X return ltmp; /* Return new head. */ X} X XMethods * XMethodsCreate() X{ X return (Methods *)share_calloc(1, sizeof(Methods)); X} X X/* X * Call appropriate routine to compute UV and, if non-null, X * dpdu and dpdv at given point on the given primitive. The X * normal is used to facilitate computation of u, v, and the X * partial derivatives. X */ Xvoid XPrimUV(prim, pos, norm, uv, dpdu, dpdv) XGeom *prim; XVector *pos, *norm, *dpdu, *dpdv; XVec2d *uv; X{ X /* X * Call appropriate inverse mapping routine X */ X if (prim->methods->uv == NULL) { X uv->u = uv->v = 0.; X if (dpdu) { X dpdu->y = dpdu->z = 0.; X dpdu->x = 1.; X } X if (dpdv) { X dpdv->x = dpdv->z = 0.; X dpdv->y = 1.; X } X } else X (*prim->methods->uv)(prim->obj,pos,norm,uv,dpdu,dpdv); X} X Xint XPrimNormal(prim, pos, norm, gnorm) XGeom *prim; XVector *pos, *norm, *gnorm; X{ X /* X * Call appropriate normal routine X */ X return (*prim->methods->normal) (prim->obj, pos, norm, gnorm); X} X Xint XPrimEnter(obj, ray, mind, hitd) XGeom *obj; XRay *ray; XFloat mind, hitd; X{ X /* X * Call appropriate enter/leave routine X */ X if (obj->methods->enter == NULL) { X Vector pos, nrm, gnrm; X /* X * Sleazy method: Use hit point, find normal X * and take dot prod with ray X */ X VecAddScaled(ray->pos, hitd, ray->dir, &pos); X PrimNormal(obj, &pos, &nrm, &gnrm); X X return dotp(&ray->dir, &gnrm) < 0.0; X } X else X return (*obj->methods->enter) (obj->obj, ray, mind, hitd); X} X X/* X * Walk through a linked-list of objects. If the object is unbounded, X * unlink it it from the list and add it to the 'unbounded' list. X * If the object is bounded, enlarge the given bounding box if X * necessary. Return pointer to unbounded list. X */ XGeom * XGeomComputeAggregateBounds(bounded, unbounded, bounds) XGeom **bounded, *unbounded; XFloat bounds[2][3]; X{ X Geom *ltmp, *prev, *nextobj; X X BoundsInit(bounds); X X prev = (Geom *)0; X X for (ltmp = *bounded; ltmp; ltmp = nextobj) { X nextobj = ltmp->next; X GeomComputeBounds(ltmp); X if (UNBOUNDED(ltmp)) { X /* X * Geom is unbounded -- unlink it... X */ X if (prev) X prev->next = ltmp->next; X else X *bounded = ltmp->next; X /* X * And add it to unbounded object list. X */ X ltmp->next = unbounded; X unbounded = ltmp; X } else { X /* X * Geom is bounded. X */ X BoundsEnlarge(bounds, ltmp->bounds); X prev = ltmp; X } X } X return unbounded; X} X X/* X * Find 'highest' animated object on the hitlist. X */ Xint XFirstAnimatedGeom(hitlist) XHitList *hitlist; X{ X int i; X X for (i = hitlist->nodes -1; i; i--) X /* X * If object itself is animated, have X * to check other flag, too... X */ X if (hitlist->data[i].obj->animtrans) X return i; X return 0; X} END_OF_FILE if test 9132 -ne `wc -c <'libray/libobj/geom.c'`; then echo shar: \"'libray/libobj/geom.c'\" unpacked with wrong size! fi # end of 'libray/libobj/geom.c' fi if test -f 'libray/libobj/torus.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libray/libobj/torus.c'\" else echo shar: Extracting \"'libray/libobj/torus.c'\" \(8916 characters\) sed "s/^X//" >'libray/libobj/torus.c' <<'END_OF_FILE' X/* X * torus.c X * X * Copyright (C) 1990, 1991, Mark Podlipec, Craig E. Kolb X * All rights reserved. X * X * This software may be freely copied, modified, and redistributed X * provided that this copyright notice is preserved on all copies. X * X * You may not distribute this software, in whole or in part, as part of X * any commercial product without the express consent of the authors. X * X * There is no warranty or other guarantee of fitness of this software X * for any purpose. It is provided solely "as is". X * X * $Id: torus.c,v 4.0 91/07/17 14:39:28 kolb Exp Locker: kolb $ X * X * $Log: torus.c,v $ X * Revision 4.0 91/07/17 14:39:28 kolb X * Initial version. X * X */ X#include "geom.h" X#include "torus.h" X Xstatic Methods *iTorusMethods = NULL; Xstatic char torusName[] = "torus"; Xunsigned long TorusTests, TorusHits; X X/* X * Create & return reference to a torus. X */ XTorus * XTorusCreate(a, b, pos, norm) XFloat a, b; XVector *pos, *norm; X{ X Torus *torus; X Vector tmpnrm; X X if ((a < EPSILON) || (b < EPSILON)) { X RLerror(RL_WARN, "Degenerate torus.\n"); X return (Torus *)NULL; X } X X tmpnrm = *norm; X if (VecNormalize(&tmpnrm) == 0.) { X RLerror(RL_WARN, "Degenerate torus normal.\n"); X return (Torus *)NULL; X } X X torus = (Torus *)share_malloc(sizeof(Torus)); X X /* X * torus->aa holds the square of the swept radius. X * torus->bb holds the square of the tube radius. X */ X torus->a = a; X torus->b = b; X torus->aa = a*a; X torus->bb = b*b; X CoordSysTransform(pos, &tmpnrm, 1., 1., &torus->trans); X X return torus; X} X X/* X * Ray/torus intersection test. X */ Xint XTorusIntersect(torus, inray, mindist, maxdist) XTorus *torus; XRay *inray; XFloat mindist, *maxdist; X{ X Vector pos,ray; X double c[5],s[4], dist, nmin; X Float distfactor; X register int num,i; X X TorusTests++; X X /* Transform ray into toroid space */ X { X Ray tmpray; X tmpray = *inray; X distfactor = RayTransform(&tmpray, &torus->trans.itrans); X ray = tmpray.dir; X pos = tmpray.pos; X nmin = mindist * distfactor; X } X X /* X * Original Equations for Toroid with position of (0,0,0) and axis (0,0,1) X * X * Equation for two circles of radius b centered at (-a,0,0) and (a,0,0) X * X * ((R-a)^2 + z*2 - b*b) * ((R+a)^2 + z*z - b*b) = 0 X * X * a is swept radius X * b is tube radius X * X * subsitute R*R = x*x + y*y to rotate about z-axis X * X * and substitute the parametric ray equations: X * X * x = x0 + t * x1; X * y = y0 + t * y1; X * z = z0 + t * z1; X * X * to get a Quartic in t. X * X * c4*t^4 + c3*t^3 + c2*t^2 + c1*t + c0 = 0 X * X * where the coefficients are: X * X * c4 = (x1s + y1s + z1s) * (x1s + y1s + z1s); X * c3 = 4.0 * (tx + ty + tz) * (x1s + y1s + z1s); X * c2 = 2.0 * (x1s + y1s + z1s) * (x0s + y0s + z0s - as - bs) X * + 4.0 * (tx + ty + tz) * (tx + ty + tz) X * + 4.0 * as * z1s; X * c1 = 4.0 * (tx + ty + tz) * (x0s + y0s + z0s - as - bs) X * + 8.0 * as * tz; X * c0 = (x0s + y0s + z0s - as - bs) * (x0s + y0s + z0s - as - bs) X * + 4.0 * as * (z0s - bs); X * X * as is swept radius squared X * bs is tube radius squared X * (x0,y0,z0) is origin of ray to be tested X * (x1,y1,z1) is direction vector of ray to be tested X * tx is x0 * x1 X * ty is y0 * y1 X * tz is z0 * z1 X * X * Since the direction vector (x1,y1,z1) is normalized: X * (x1s + y1s + z1s) = 1.0 X * X * Also let g2s = (x1 * x0) + (y1 * y0) + (z1 * z0) X * and let g0s = (x0 * x0) * (y0 * y0) + (z0 * z0) - as - bs X * since these terms are used fairly often X */ X { X register Float g0s,g2s; X register Float as,bs; X register Float z0s,z1s,tz; X X as = torus->aa; X bs = torus->bb; X z0s = pos.z * pos.z; X z1s = ray.z * ray.z; X tz = pos.z * ray.z; X g0s = pos.x * pos.x + pos.y * pos.y + z0s - as - bs; X g2s = pos.x * ray.x + pos.y * ray.y + tz; X X c[4] = 1.0; X c[3] = 4.0 * g2s; X c[2] = 2.0 * (g0s + 2.0 * g2s * g2s + 2.0 * as * z1s); X c[1] = 4.0 * (g2s*g0s + 2.0*as*tz); X c[0] = g0s * g0s + 4.0 * as * (z0s - bs); X } X X /* use GraphGem's Solve Quartic to find roots */ X num = SolveQuartic(c,s); X X /* no roots - return 0. */ X if (num==0) return FALSE; X X /* of roots return the smallest root > EPSILON */ X dist = 0.0; X for(i=0;i nmin) { X /* first valid root */ X if (dist == 0.0) dist = s[i]; X /* else update only if it's closer to ray origin */ X else if (s[i] < dist) dist = s[i]; X } X } X dist /= distfactor; X if (dist > mindist && dist < *maxdist) { X *maxdist = dist; X TorusHits++; X return TRUE; X } X return FALSE; X} X X/* X * Compute the normal to a torus at a given location on its surface X */ Xint XTorusNormal(torus, rawpos, nrm, gnrm) XTorus *torus; XVector *rawpos, *nrm, *gnrm; X{ X Vector pos; X register Float dist,posx,posy,xm,ym; X X /* Transform intersection point to torus space. */ X pos = *rawpos; X PointTransform(&pos, &torus->trans.itrans); X X/* X * The code for the toroid is simpified by always having the axis X * be the z-axis and then transforming information to and from X * toroid space. X * X * Flatten toroid by ignoring z. Now imagine a knife cutting from X * center of toroid to the ray intersection point(x,y). The point X * on the tube axis(a circle about the origin with radius 'a') X * where the knife cuts is (xm,ym,zm=0). Unflattening the toroid, X * the normal at the point [x,y,z] is (x-xm,y-ym,z). Of course, we X * must transform the normal back into world coordinates. X * Instead of messing with tan-1,sin and cos, we can find (xm,ym) X * by using the proportions: X * X * xm x ym y X * ---- = ---- and ---- = ---- X * a dist a dist X * X * a is the swept radius X * [x,y,z] is the point on the toroids surface X * dist is the distance from the z-axis (x*x + y*y). X * [xm,ym,zm=0] is the point on the tube's axis X * X */ X X /* find distance from axis */ X posx = pos.x; X posy = pos.y; X dist = sqrt(posx * posx + posy * posy); X X if (dist > EPSILON) X { X xm = torus->a * posx / dist; X ym = torus->a * posy / dist; X } X else /* ERROR - dist should not be < EPSILON (should never happen)*/ X { X xm = 0.0; X ym = 0.0; X } X X /* normal is vector from [xm,ym,zm] to [x,y,z] */ X nrm->x = posx - xm; X nrm->y = posy - ym; X nrm->z = pos.z; /* note by default zm is 0 */ X X /* Transform normal back to world space. */ X NormalTransform(nrm, &torus->trans.itrans); X *gnrm = *nrm; X return FALSE; X} X Xvoid XTorusUV(torus, pos, norm, uv, dpdu, dpdv) XTorus *torus; XVector *pos, *norm, *dpdu, *dpdv; XVec2d *uv; X{ X Vector npos; X Float costheta, sintheta, rad, cosphi; X X npos = *pos; X PointTransform(&npos, &torus->trans.itrans); X /* X * u = theta / 2PI X */ X rad = sqrt(npos.x*npos.x + npos.y*npos.y); X costheta = npos.x / rad; X sintheta = npos.y / rad; X if (costheta > 1.) /* roundoff */ X uv->u = 0.; X else if (costheta < -1.) X uv->u = 0.5; X else X uv->u = acos(costheta) / TWOPI; X if (sintheta < 0.) X uv->u = 1. - uv->u; X if (dpdu) { X dpdu->x = -npos.y; X dpdu->y = npos.x; X dpdu->z = 0.; X VecTransform(dpdu, &torus->trans.trans); X (void)VecNormalize(dpdu); X } X /* X * sinphi = npos.z / tor->b; X * cosphi = rad - tor->a; X * cosphi is negated in order to make texture 'seam' X * occur on the interior of the torus. X */ X cosphi = -(rad - torus->a) / torus->b; X if (cosphi > 1.) X uv->v = 0.; X else if (cosphi < -1.) X uv->v = 0.5; X else X uv->v = acos(cosphi) / TWOPI; X if (npos.z > 0.) /* if sinphi > 0... */ X uv->v = 1. - uv->v; X /* X * dpdv = norm X dpdu X */ X if (dpdv) { X VecCross(norm, dpdu, dpdv); X VecTransform(dpdv, &torus->trans.trans); X (void)VecNormalize(dpdv); X } X} X X/* X * Return the extent of a torus. X */ Xvoid XTorusBounds(torus, bounds) XTorus *torus; XFloat bounds[2][3]; X{ X bounds[LOW][X] = bounds[LOW][Y] = -(torus->a+torus->b); X bounds[HIGH][X] = bounds[HIGH][Y] = torus->a+torus->b; X bounds[LOW][Z] = -torus->b; X bounds[HIGH][Z] = torus->b; X /* X * Transform bounding box to world space. X */ X BoundsTransform(&torus->trans.trans, bounds); X} X Xchar * XTorusName() X{ X return torusName; X} X Xvoid XTorusStats(tests, hits) Xunsigned long *tests, *hits; X{ X *tests = TorusTests; X *hits = TorusHits; X} X XMethods * XTorusMethods() X{ X if (iTorusMethods == NULL) { X iTorusMethods = MethodsCreate(); X iTorusMethods->create = (GeomCreateFunc *)TorusCreate; X iTorusMethods->methods = TorusMethods; X iTorusMethods->name = TorusName; X iTorusMethods->intersect = TorusIntersect; X iTorusMethods->bounds = TorusBounds; X iTorusMethods->normal = TorusNormal; X iTorusMethods->uv = TorusUV; X iTorusMethods->stats = TorusStats; X iTorusMethods->checkbounds = TRUE; X iTorusMethods->closed = TRUE; X } X return iTorusMethods; X} X Xvoid XTorusMethodRegister(meth) XUserMethodType meth; X{ X if (iTorusMethods) X iTorusMethods->user = meth; X} END_OF_FILE if test 8916 -ne `wc -c <'libray/libobj/torus.c'`; then echo shar: \"'libray/libobj/torus.c'\" unpacked with wrong size! fi # end of 'libray/libobj/torus.c' fi if test -f 'libshade/options.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'libshade/options.c'\" else echo shar: Extracting \"'libshade/options.c'\" \(8699 characters\) sed "s/^X//" >'libshade/options.c' <<'END_OF_FILE' X/* X * options.c X * X * Copyright (C) 1989, 1991, Craig E. Kolb X * All rights reserved. X * X * This software may be freely copied, modified, and redistributed X * provided that this copyright notice is preserved on all copies. X * X * You may not distribute this software, in whole or in part, as part of X * any commercial product without the express consent of the authors. X * X * There is no warranty or other guarantee of fitness of this software X * for any purpose. It is provided solely "as is". X * X * $Id: options.c,v 4.0 91/07/17 14:46:47 kolb Exp Locker: kolb $ X * X * $Log: options.c,v $ X * Revision 4.0 91/07/17 14:46:47 kolb X * Initial version. X * X */ X#include "rayshade.h" X#include "options.h" X#include "stats.h" X#include "viewing.h" X XRSOptions Options; X Xstatic void usage(); X Xvoid XRSOptionsSet(argc, argv) Xint argc; Xchar **argv; X{ X extern void OpenStatsFile(); X X Options.progname = strsave(argv[0]); X Options.inputname = (char *)NULL; X X while (--argc) { X argv++; X if (argv[0][0] != '-') { X /* X * Must be the input file name. X * If already given, complain and then exit. X */ X if (Options.inputname != (char *)NULL) { X usage(); X exit(1); X } X Options.inputname = strsave(argv[0]); X continue; X } X /* else */ X switch(argv[0][1]) { X case 'A': X /* X * First frame number X */ X Options.startframe = atoi(argv[1]); X argv++; argc--; X break; X#ifdef URT X case 'a': X Options.alpha = !Options.alpha; X break; X#endif X case 'C': X Options.cutoff.r = atof(argv[1]); X Options.cutoff.g = atof(argv[2]); X Options.cutoff.b = atof(argv[3]); X Options.cutoff_set = TRUE; X argv += 3; argc -= 3; X break; X#ifdef URT X case 'c': X Options.appending = TRUE; X break; X#endif X case 'D': X Options.maxdepth = atoi(argv[1]); X Options.maxdepth_set = TRUE; X argv++; argc--; X break; X case 'E': X Options.eyesep = atof(argv[1]); X Options.eyesep_set = TRUE; X argc--; argv++; X break; X#ifdef URT X case 'e': X Options.exp_output = TRUE; X break; X#endif X case 'F': X Options.report_freq = atoi(argv[1]); X if (Options.report_freq < 1) X Options.report_freq = 1; X Options.freq_set = TRUE; X argv++; argc--; X break; X case 'f': X Options.flipnorm = !Options.flipnorm; X break; X case 'G': X Options.gamma = atof(argv[1]); X argv++; argc--; X break; X case 'g': X Options.gaussian = !Options.gaussian; X break; X case 'h': X usage(); X exit(0); X break; X case 'j': X Options.jitter = !Options.jitter; X Options.jitter_set = TRUE; X break; X case 'l': X Options.stereo = LEFT; X break; X#ifdef URT X case 'm': X Options.samplemap = !Options.samplemap; X break; X#endif X case 'N': X Options.totalframes = atof(argv[1]); X Options.totalframes_set = TRUE; X argv++; argc--; X break; X case 'n': X Options.no_shadows = !Options.no_shadows; X break; X case 'O': X Options.imgname = strsave(argv[1]); X argv++; X argc--; X break; X case 'o': X Options.shadowtransp = !Options.shadowtransp; X break; X case 'P': X Options.cppargs = argv[1]; X argv++; argc--; X break; X case 'p': X /* X * Preview-quality rendering X * no shadows X * max depth of 0 X * 1 jittered sample/pixel X */ X Options.no_shadows = TRUE; X Options.maxdepth = 0; X Options.maxdepth_set = TRUE; X Options.jitter = TRUE; X Options.jitter_set = TRUE; X Options.samples = 1; X Options.samples_set = TRUE; X break; X case 'q': X Options.quiet = TRUE; X break; X case 'R': X Screen.xres = atoi(argv[1]); X Screen.yres = atoi(argv[2]); X Options.resolution_set = TRUE; X argv += 2; X argc -= 2; X break; X case 'r': X Options.stereo = RIGHT; X break; X case 'S': X Options.samples = atoi(argv[1]); X if (Options.samples < 1) X Options.samples = 1; X Options.samples_set = TRUE; X argv++; argc--; X break; X case 's': X Options.cache = !Options.cache; X break; X case 'T': X Options.contrast.r = atof(argv[1]); X Options.contrast.g = atof(argv[2]); X Options.contrast.b = atof(argv[3]); X Options.contrast_set = TRUE; X argv += 3; X argc -= 3; X break; X case 'v': X Options.verbose = TRUE; X break; X case 'V': X Options.verbose = TRUE; X if (argv[1][0] == '-') { X /* User probably blew it, and X * it's difficult to remove a file X * that begins with '-'... X */ X usage(); X exit(2); X } X Options.statsname = strsave(argv[1]); X OpenStatsFile(); X argv++; argc--; X break; X#ifdef URT X case 'W': X Options.xmin = atof(argv[1]); X Options.xmax = atof(argv[2]); X Options.ymin = atof(argv[3]); X Options.ymax = atof(argv[4]); X Options.window_set = TRUE; X argv += 4; argc -= 4; X break; X#endif X#ifdef LINDA X case 'X': X Options.workers = atoi(argv[1]); X if (Options.workers<0 || Options.workers>17) { X RLerror(RL_PANIC, "%d workers?\n", X Options.workers); X } X argv++; argc--; X break; X case 'w': X Options.verbose_worker = X !Options.verbose_worker; X break; X#endif X default: X RLerror(RL_PANIC,"Bad argument: %s\n",argv[0]); X } X } X} X Xvoid XRSOptionsList() X{ X if (Options.totalframes > 1) { X fprintf(Stats.fstats,"Rendering frames %d through %d.\n", X Options.startframe, Options.endframe); X } else { X fprintf(Stats.fstats,"Rendering frame %d.\n", Options.startframe); X } X X fprintf(Stats.fstats,"Screen resolution: %d x %d\n", X Screen.xres,Screen.yres); X fprintf(Stats.fstats,"Image window: (%d - %d), (%d - %d).\n", X Screen.minx, Screen.maxx, Screen.miny, Screen.maxy); X X if (Options.jitter) X fprintf(Stats.fstats,"Using jittered sampling, "); X fprintf(Stats.fstats,"Max sampling rate %d %s/pixel.\n", X Options.samples*Options.samples, X Options.samples == 1 ? "sample" : "samples"); X X fprintf(Stats.fstats, X "Maximum contrast: %g red, %g green, %g blue.\n", X Options.contrast.r, Options.contrast.g, X Options.contrast.b); X fprintf(Stats.fstats,"Maximum ray depth: %d. Cutoff thresh: %g %g %g.\n", X Options.maxdepth, X Options.cutoff.r, Options.cutoff.g, Options.cutoff.b); X if (Options.stereo == LEFT) X fprintf(Stats.fstats,"Rendering image for left eye.\n"); X else if (Options.stereo == RIGHT) X fprintf(Stats.fstats,"Rendering image for right eye.\n"); X if (Options.no_shadows) { X fprintf(Stats.fstats,"No shadows are rendered.\n"); X } else if (Options.shadowtransp) { X fprintf(Stats.fstats, X "Object opacity affects depth of shadowing.\n"); X } X if (!Options.cache) X fprintf(Stats.fstats,"Shadow caching is disabled.\n"); X if (Options.totalframes != 1) X fprintf(Stats.fstats,"Rendering %d frames.\n", X Options.totalframes); X} X Xstatic void Xusage() X{ X fprintf(stderr,"usage: %s [options] [filename]\n", Options.progname); X fprintf(stderr,"Where options include:\n"); X fprintf(stderr,"\t-A frame\t(Begin with given frame #.)\n"); X#ifdef URT X fprintf(stderr,"\t-a \t\t(Toggle writing of alpha channel.)\n"); X#endif X fprintf(stderr,"\t-C thresh\t(Set adaptive ray tree cutoff value.)\n"); X#ifdef URT X fprintf(stderr,"\t-c \t\t(Continue interrupted rendering.)\n"); X#endif X fprintf(stderr,"\t-D depth\t(Set maximum ray tree depth.)\n"); X fprintf(stderr,"\t-E eye_sep\t(Set eye separation in stereo pairs.)\n"); X#ifdef URT X fprintf(stderr,"\t-e \t\t(Write exponential RLE file.)\n"); X#endif X fprintf(stderr,"\t-F freq\t\t(Set frequency of status report.)\n"); X fprintf(stderr,"\t-G gamma\t(Use given gamma correction exponent.)\n"); X fprintf(stderr,"\t-h \t\t(Print this message.)\n"); X fprintf(stderr,"\t-l \t\t(Render image for left eye view.)\n"); X fprintf(stderr,"\t-N number\t(Render given number of frames.)\n"); X fprintf(stderr,"\t-n \t\t(Do not render shadows.)\n"); X fprintf(stderr,"\t-O outfile \t(Set output file name.)\n"); X fprintf(stderr,"\t-o \t\t(Toggle opacity effect on shadowing.)\n"); X fprintf(stderr,"\t-P cpp-args\t(Options to pass to C pre-processor.\n"); X fprintf(stderr,"\t-p \t\t(Preview-quality rendering.)\n"); X fprintf(stderr,"\t-q \t\t(Run quietly.)\n"); X fprintf(stderr,"\t-R xres yres\t(Render at given resolution.)\n"); X fprintf(stderr,"\t-r \t\t(Render image for right eye view.)\n"); X fprintf(stderr,"\t-S samples\t(Max density of samples^2 samples.)\n"); X fprintf(stderr,"\t-s \t\t(Don't cache shadowing information.)\n"); X fprintf(stderr,"\t-T r g b\t(Set contrast threshold (0. - 1.).)\n"); X fprintf(stderr,"\t-V filename \t(Write verbose output to filename.)\n"); X fprintf(stderr,"\t-v \t\t(Verbose output.)\n"); X#ifdef URT X fprintf(stderr,"\t-W x y x y \t(Render subwindow.)\n"); X#endif X#ifdef LINDA X fprintf(stderr,"\t-X workers\t(Number of workers.)\n"); X fprintf(stderr,"\t-w \t\t(Verbose worker output.)\n"); X#endif X} END_OF_FILE if test 8699 -ne `wc -c <'libshade/options.c'`; then echo shar: \"'libshade/options.c'\" unpacked with wrong size! fi # end of 'libshade/options.c' fi echo shar: End of archive 12 \(of 19\). cp /dev/null ark12isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 19 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0