home *** CD-ROM | disk | FTP | other *** search
- /*
- * trace.c -- Important tracing stuff is done here, this is the actual
- * ray-tracing part
- *
- * (c) 1993, 1994 Han-Wen Nienhuys <hanwen@stack.urc.tue.nl>
- *
- * originally by George Kyriazis, 1988. Thoroughly rehacked by Han-Wen
- * Nienhuys.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation;
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
-
-
- #ifdef GNUCC
- #error bo
- /* Gcc needs this for SEEK_EOF */
- #include <unistd.h>
- #endif
-
- PRIVATE void line_stat_update(void);
- PRIVATE void print_endstats(void);
- PRIVATE void write_color(color col);
- PRIVATE void open_TGA(char *fn);
- PRIVATE void open_continueTGA(char *fn);
- PRIVATE void write_line(char *fn);
-
- PRIVATE int cur_x,
- cur_y; /* current x,y of pixels we're tracing */
- PRIVATE char *line_buffer, /* buffer to hold data of the line we're
- * working on */
- *line_bufp; /* current buffer position */
-
- PRIVATE bool trace_start;
-
-
- /*
- * sample a ray inside specified solid angle
- */
- PUBLIC struct ray
- sample_ray(struct ray r, double theta)
- {
- vector tmp,
- hor,
- ver,
- dev;
- double randfact1,
- randfact2;
-
- if (theta < EPSILON) /* don't go thru difficult stuff if you
- * don't have to */
- return r;
-
- setvector(tmp, 1, 0, 0);
- vcross(hor, tmp, r.dir);
-
- if (quickveclen(tmp) < EPSILON) { /* tmp x r.dir is nullvector */
- setvector(tmp, 0, 1, 0);/* then this won't give nullvector */
- vcross(hor, tmp, r.dir);
- }
- /* hor is orthogonal to r.dir */
- norm(hor, hor);
- vcross(ver, r.dir, hor);
-
- /* pick a point in disc radius 1 */
- do {
- randfact1 = Gauss_rand(); /* maybe use a homogenous rand */
- randfact2 = Gauss_rand();
- }
- while (sqr(randfact1) + sqr(randfact2) > 1);
-
- randfact1 *= theta;
- randfact2 *= theta;
-
- svproduct(hor, randfact1, hor);
- svproduct(ver, randfact2, ver);
-
- vadd(dev, hor, ver);
- vadd(r.dir, dev, r.dir);
- norm(r.dir, r.dir);
-
- return r;
- }
-
-
- /*
- * trace a single straight ray
- */
- PUBLIC color
- trace(struct ray r, int n, double importance)
- { /* n = recursion level */
- struct intersect inter;
- color col;
-
- if (n > line_stats.maxdepth)
- line_stats.maxdepth = n;
-
-
- /* check for intersection with the object */
- inter = intersect(r);
-
-
- /* if no intersection, return some background color */
- if (inter.q_ent.obj == NULL)
- return bgcolor(&r);
-
- /* else calculate the shading function there */
- col = shade(inter, r, n, importance);
- return col;
- }
-
- /***************************************************************************
- * RAYTRACE(): entry point of trace.c. raytrace the whole scene *
- ***************************************************************************/
-
- PUBLIC void
- raytrace(char *fname)
- {
- double xr,
- yr,
- xstep,
- ystep;
- color
- pixcol, /* color gathered sofar for a pixel */
- color;
- struct ray eyeray;
- double
- factor,
- hor_coef,
- ver_coef;
-
-
- int i;
- double pix_width,
- pix_height;
- vector camhor,
- camver,
- hor,
- ver;
-
- pix_width = 2 * thecamera->fov / xres; /* pixel width in radians */
- pix_height = 2 * thecamera->fov / xres; /* pixel height in radians */
-
-
- /* decide whether to continue or restart */
- if (continue_flag) {
- open_continueTGA(fname);
- } else {
- cur_y = 0;
- open_TGA(fname);
- }
-
- /* init the camera */
- vcross(camhor, thecamera->sky, thecamera->eye_dir); /* the x screen vector */
- if (isvnull(camhor))
- errormsg("direction vector can't be sky vector.");
- norm(camhor, camhor);
-
- vcross(camver, thecamera->eye_dir, camhor); /* the y screen vector */
- norm(camver, camver);
-
- eyeray.pos = thecamera->eye;/* eye is the beginning of the ray */
- eyeray.type = R_EYE; /* this is an eye ray */
-
- xstep = 2.0 / xres;
- ystep = 2.0 / yres;
-
- yr = 1.0 - (double) cur_y *ystep; /* where should we begin? */
-
- factor = 1 / (double) tries;
-
- trace_start = TRUE;
-
- for (; cur_y < yres; cur_y++) {
- vector screen; /* position on imaginary screen in space */
-
- xr = -1.0;
-
- for (cur_x = 0; cur_x < xres; cur_x++) {
- hor_coef = xr * thecamera->fov * thecamera->aspect;
- ver_coef = yr * thecamera->fov;
-
- setcolor(pixcol, 0, 0, 0); /* init the color */
-
- /*
- * the time blur has to be done in linear time and not
- * randomly
- */
- i = tries;
- while (i--) {
- check_abort(); /* somebody pressed a key? */
-
- /*
- * pick a Time inside time interval randomization is used
- * so we won't get the strobo effect
- */
- Time = (time2 + time1) / 2 +
- (time2 - time1) * (i + rand1()) / tries;
-
- /* ray direction calculations */
- svproduct(hor, hor_coef +
- 0.5 * pix_width * Gauss_rand() * Aalias_width,
- camhor);
- svproduct(ver, ver_coef +
- 0.5 * pix_height * Gauss_rand() * Aalias_width, camver);
-
- vadd(screen, hor, ver);
- vadd(eyeray.dir, thecamera->eye_dir, screen);
-
- norm(eyeray.dir, eyeray.dir);
-
- /* !!!! this is the important step! */
- color = trace(eyeray, 0, 1.0);
-
- /* sum all the intensities together */
- pixcol.r += color.r * factor;
- pixcol.g += color.g * factor;
- pixcol.b += color.b * factor;
- }
-
- /* normalise color */
- pixcol = normcol(pixcol);
- write_color(pixcol);/* output one pixel */
- xr += xstep; /* next */
- }
-
- write_line(fname); /* write image line to output */
- yr -= ystep; /* next line */
- line_stat_update(); /* and print stats */
- }
-
- print_endstats(); /* global statistics. */
- }
-
- /* print statistics after abortion */
- PUBLIC void
- abort_trace(int blubber)
- { /* needed for keeping signal () from
- * complaining */
- if (trace_start) {
- if (!silent_mode) {
- printf("\nStopped at pixel %d\n", cur_x);
- line_stat_update();
- }
- print_endstats();
-
- } else {
- printf("(interrupt)\n");
- }
-
- exit(0);
- }
-
-
- /***************************************************************************
- * statistics *
- ***************************************************************************/
-
- /* statistics for a line */
- PRIVATE void
- line_stat_update(void)
- {
- if (cur_x == 0)
- return;
- if (!silent_mode)
- printf("line %3d. rays shot: %ld, hits: %ld, rel: %.2lf, ray/pxl: %.2lf\n"
- " eye: %ld shad: %ld, refl: %ld, refr %ld, depth %d/%d\n",
- cur_y, line_stats.raytest, line_stats.rayintersections, (double) line_stats.rayintersections / line_stats.raytest,
- (double) line_stats.raytest / xres, line_stats.eyerays, line_stats.shadowtest, line_stats.reflected,
- line_stats.refracted, line_stats.maxdepth, max_trace_level);
-
- /* update statistics */
- global_stats.raytest += line_stats.raytest;
- global_stats.rayintersections += line_stats.rayintersections;
- global_stats.shadowtest += line_stats.shadowtest;
- global_stats.reflected += line_stats.reflected;
- global_stats.refracted += line_stats.refracted;
- global_stats.eyerays += line_stats.eyerays;
-
- line_stats.raytest = 0;
- line_stats.rayintersections = 0;
- line_stats.shadowtest = 0;
- line_stats.reflected = 0;
- line_stats.refracted = 0;
- line_stats.maxdepth = 0;
- line_stats.eyerays = 0;
-
- }
-
-
- #define FMT0 "%-23s\t%12s\t%12s\t%13s\n"
- #define FMT1 "%-23s\t%12ld\t%12ld\t%12.2lf%%\n"
- #define FMT2 "%-17s%6ld\t%12ld\t%12ld\t%12.2lf%%\n"
-
-
- PRIVATE void
- print_tot_stat(char *s, long total, long succ)
- {
- double rel;
-
- if (total > 0)
- rel = 100.0 * succ / total;
- else
- rel = 0.0;
-
- printf(FMT1, s, total, succ, rel);
- }
-
- PRIVATE void
- print_shap_stat(char *s, long no, long total, long succ)
- {
- double rel;
-
- if (!no)
- return;
- if (total > 0)
- rel = 100.0 * succ / total;
- else
- rel = 0.0;
-
- printf(FMT2, s, no, total, succ, rel);
- }
-
-
-
- /* print statistics after finishing */
- PRIVATE void
- print_endstats(void)
- {
- long sec;
-
- printf(FMT0, "", "tested", "succeeded", "relative");
-
- print_tot_stat("Rays: ", global_stats.raytest, global_stats.rayintersections);
- print_tot_stat("Shadow cache: ", global_stats.cachetest, global_stats.cachehit);
-
- print_shap_stat("Objects: ", global_stats.objects, global_stats.objtest, global_stats.objhit);
- print_all_shape_stats(FMT2, thescene);
-
- print_shap_stat("Bounds: ", global_stats.bounds, global_stats.boundtest, global_stats.boundhit);
- print_shap_stat("Clips: ", global_stats.clips, global_stats.cliptest, global_stats.cliphit);
-
-
- printf("Total reflected rays traced: %ld\n", global_stats.reflected);
- printf("Total refracted rays traced: %ld\n", global_stats.refracted);
- printf("Total eye rays traced: %ld\n", global_stats.eyerays);
-
- if (first_light != NULL)
- printf("light_sources: %ld\n", first_light->methods->howmuch);
- printf("cache misses: %ld\n", global_stats.cachemiss);
-
- if (test_stat)
- printf("\n\nTEST STATISTICS: %ld\n", test_stat);
-
- sec = timer_stop();
- printf("Time for trace: %d:%02d:%02d (%ld seconds)", (int) sec / 36000,
- (int) (sec / 600) % 60, (int) (sec % 600) / 10, (long) sec / 10);
- if (global_stats.raytest > 0)
- printf(" Time per ray: %.3fms\n",
- 100.0 * (double) sec / global_stats.raytest);
- else
- printf("\n");
-
- }
-
- /***************************************************************************
- * FILES *
- ***************************************************************************/
-
- PRIVATE char *
- alloc_buf(int xres)
- {
- void *p;
- p = malloc(xres * 3 * sizeof(char));
-
- if (p == NULL)
- alloc_err("line buffer");
-
- return (char *) p;
- }
-
-
- /* calc the integer value to be put to the file */
- /* and store it. NB targa is in bgr form */
- PRIVATE void
- write_color(color col)
- {
- *line_bufp++ = col.b * 255; /* TGA is BGR */
- *line_bufp++ = col.g * 255;
- *line_bufp++ = col.r * 255;
- }
-
- /* dump line_buffer to output, and flush buffer (close file) */
- PRIVATE void
- write_line(char *fn)
- {
- int i;
- char *cp;
- FILE *f;
-
- if ((f = fopen(fn, "ab")) == NULL) {
- errormsg("can\'t open %s", fn);
- }
- /* reset buffer */
- cp = line_bufp = line_buffer;
- for (i = 0; i < 3 * xres; i++)
- putc(*cp++, f);
-
- /* now you can press ctl-alt-del */
- fclose(f);
- }
-
- /* open a file, write TGA header */
- PUBLIC void
- open_TGA(char *fn)
- {
- FILE *f;
- int header[18];
- int i;
-
-
- f = fopen(fn, "wb");
- if (f == NULL)
- errormsg("can\'t open %s", fn);
-
- for (i = 0; i < 18; i++)
- header[i] = 0;
-
- printf("xres %d x yres %d\n", xres, yres);
-
- /* put ID and screen res in header */
- header[2] = 0x02;
- header[12] = xres & 0x00ff;
- header[13] = xres / 256;
- header[14] = yres & 0x00ff;
- header[15] = yres / 256;
- header[16] = 24;
- header[17] = 32;
-
- /* dump header */
- for (i = 0; i < 18; i++)
- putc(header[i], f);
-
- line_bufp = line_buffer = alloc_buf(xres);
-
-
- fclose(f);
-
-
- }
-
-
- /* open a file for appending */
- PRIVATE void
- open_continueTGA(char *fn)
- {
- FILE *f;
- int header[18];
- long len;
- int i;
-
- f = fopen(fn, "r+");
- if (f == NULL) {
- printf("error opening continue file, creating a new file\n");
- open_TGA(fn);
- return;
- }
- /* read TGA header */
- for (i = 0; i < 18; i++)
- header[i] = getc(f);
-
- /* check file id */
- if (header[2] != 0x02 || header[16] != 24 || header[17] != 32)
- errormsg("Not a valid TGA!");
-
-
- /* calc resolution */
- xres = header[12] + header[13] * 256;
- yres = header[14] + header[15] * 256;
-
- /* find out where to start. This could be more robust */
- fseek(f, 0, SEEK_END);
- len = ftell(f) - 18;
- if (len % xres * 3)
- errormsg("TGA doesn't end at a scanline");
- cur_y = len / (xres * 3);
-
- printf("xres %d x yres %d, starting at line %d\n", xres, yres, cur_y);
-
- /* set up buffer */
- line_bufp = line_buffer = alloc_buf(xres);
- fclose(f);
- }
-