home *** CD-ROM | disk | FTP | other *** search
- /*
- * box.c -- standard routines for boxes.
- *
- * (c) 1993, 1994, Han-Wen Nienhuys <hanwen@stack.urc.tue.nl>
- *
- * 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"
-
- extern struct methods my_methods;
-
-
- PRIVATE bool
- all_box_intersections(dqueue * q, object *o, struct ray *r,
- int flags, bool *isinside)
- {
- int no_int;
- dqueue q_ent;
- double d[2];
- struct box_data *b = o->data.box;
-
-
-
- if (o->inv_trans) {
- struct ray localray;
-
- localray = *r;
- transform_ray(&localray, o);
- no_int = intersect_rawbox(b->p1, b->p2, &localray, d);
- } else {
- no_int = intersect_rawbox(b->p1, b->p2, r, d);
- }
-
- *isinside = (no_int % 2) ^ o->inverted;
-
- if (!no_int)
- return FALSE;
-
- q_ent.t = d[0];
- q_ent.obj = o;
- q_ent.entering = !(*isinside);
- add_to_queue(q, q_ent);
-
- if (no_int > 1 && flags & CHKALL) {
- q_ent.t = d[1];
- q_ent.obj = o;
- q_ent.entering = (*isinside);
- add_to_queue(q, q_ent);
- }
- return TRUE;
- }
-
- PRIVATE void
- precompute_box(object *o)
- {
- struct box_data *b = o->data.box;
- vector p1,
- p2;
- matrix m;
-
-
- /* make sure the points are in the right order */
- p1.x = MIN(b->p1.x, b->p2.x);
- p2.x = MAX(b->p1.x, b->p2.x);
- p1.y = MIN(b->p1.y, b->p2.y);
- p2.y = MAX(b->p1.y, b->p2.y);
- p1.z = MIN(b->p1.z, b->p2.z);
- p2.z = MAX(b->p1.z, b->p2.z);
-
- b->p1 = p1;
- b->p2 = p2;
- if (o->inv_trans)
- copy_matrix(m, *o->inv_trans);
- else
- unit_matrix(m);
- do_box_bound(&o->bmin, &o->bmax, m, p1, p2);
- my_methods.howmuch++;
- global_stats.prims++;
- }
-
- PUBLIC void
- do_box_bound(vector *bmin, vector *bmax, matrix inv_trans, vector p1, vector p2)
- {
- matrix trans;
- vector point;
-
- invert_trans(trans, inv_trans);
-
- p1 = mvproduct(trans, p1);
- p2 = mvproduct(trans, p2);
-
- setvector(point, p1.x, p1.y, p1.z);
- update_min_and_max(bmin, bmax, point);
- setvector(point, p2.x, p1.y, p1.z);
- update_min_and_max(bmin, bmax, point);
- setvector(point, p1.x, p2.y, p1.z);
- update_min_and_max(bmin, bmax, point);
- setvector(point, p1.x, p1.y, p2.z);
- update_min_and_max(bmin, bmax, point);
- setvector(point, p2.x, p2.y, p1.z);
- update_min_and_max(bmin, bmax, point);
- setvector(point, p1.x, p2.y, p2.z);
- update_min_and_max(bmin, bmax, point);
- setvector(point, p2.x, p1.y, p2.z);
- update_min_and_max(bmin, bmax, point);
- setvector(point, p2.x, p2.y, p2.z);
- }
-
- /*
- * intersect a ray with a box which has unit_matrix for inv_trans. It
- * could be used for automatic bounding boxes in the future
- *
- * after having looked to the PoV sources again, I have found that they
- * essentially use the same routine. The difference is: their routines
- * are very optimized, and mine are not. The ABS() calls take up too much
- * time. (I think)
- */
-
- PUBLIC int
- intersect_rawbox(vector p1, vector p2, struct ray *r, double *params)
- {
- double t1,
- t2,
- tmin,
- tmax,
- tmp;
-
- tmin = -INFTY;
- tmax = INFTY;
-
- my_methods.test++;
-
- /* intersect YZ-planes */
- if (!ISZERO(r->dir.x)) { /* it's not parallel to YZ plane */
- /*
- * calculate two plane intersection, optimised version We are
- * still using the formula (x,n) = mov, but since n is (1,0,0),
- * calculations can be greatly simplified.
- */
-
- t1 = (p1.x - r->pos.x) / r->dir.x;
- t2 = (p2.x - r->pos.x) / r->dir.x;
- if (t1 > t2) { /* swap t1 and t2 if t1 is not the
- * minimum. */
- tmp = t1;
- t1 = t2;
- t2 = tmp;
- }
- /* intersect previous domain with current */
- tmin = MAX(tmin, t1);
- tmax = MIN(tmax, t2);
-
- if (tmax < tolerance)
- return 0;
- } else if (p1.x > r->pos.x || r->pos.x > p2.x)
- return 0;
-
- /* y part */
- if (!ISZERO(r->dir.y)) {
- t1 = (p1.y - r->pos.y) / r->dir.y;
- t2 = (p2.y - r->pos.y) / r->dir.y;
-
- if (t1 > t2) { /* swap? */
- tmp = t1;
- t1 = t2;
- t2 = tmp;
- }
- /*
- * [t1, t2] is the domain for the intersection parameter t, ie if
- * x = p +t*d is inside the box, then t in [t1,t2] holds. but t
- * also has to be in [tmin,tmax], so intersect these two sets.
- */
-
- tmin = MAX(tmin, t1);
- tmax = MIN(tmax, t2);
- if (tmax < tolerance || /* the intersection will be behind us. */
- tmax < tmin) /* the domain is empty */
- return 0;
- } else if (p1.y > r->pos.y || r->pos.y > p2.y)
- return 0;
-
- /* z part. It works in the same way as the X and Y parts do. */
- if (!ISZERO(r->dir.z)) {
- t1 = (p1.z - r->pos.z) / r->dir.z;
- t2 = (p2.z - r->pos.z) / r->dir.z;
-
- if (t1 > t2) { /* swap */
- tmp = t1;
- t1 = t2;
- t2 = tmp;
- }
- tmin = MAX(tmin, t1);
- tmax = MIN(tmax, t2);
-
- if (tmax < tolerance || tmax < tmin)
- return 0;
- } else if (p1.z > r->pos.z || r->pos.z > p2.z)
- return 0;
-
-
- /*
- * OK. We've checked wether the ray r(t) is inside the box: the
- * solutions set for t is not empty. both tmin and tmax are
- * intersection points of the ray with the box boundary (the 6
- * rectangles) Now choose the least positive of the two intersection
- * points, and return it.
- */
-
-
- my_methods.hit++;
- if (tmin < tolerance) {
- *params = tmax;
- return 1;
- } else {
- *params++ = tmin;
- *params = tmax;
- return 2;
- }
- }
-
- PRIVATE bool
- inside_box(object *o, vector org)
- {
- struct box_data *b = o->data.box;
-
- if (o->inv_trans)
- org = mvproduct(*o->inv_trans, org);
-
- /* no fudges here! */
- if (org.x < b->p2.x && org.y < b->p2.y && org.z < b->p2.z &&
- org.x > b->p1.x && org.y > b->p1.y && org.z > b->p1.z)
- return !o->inverted;
- else
- return o->inverted;
- }
-
- PRIVATE vector
- box_normal(struct intersect i, vector x)
- {
- vector n;
- object *o = i.q_ent.obj;
- struct box_data *b;
-
- b = o->data.box;
-
- setvector(n, 0, 0, 0);
-
- if (o->inv_trans)
- x = mvproduct(*o->inv_trans, x);
-
- if (ISZERO(x.x - b->p1.x) || ISZERO(x.x - b->p2.x)) {
- n.x = 1.0;
- } else if (ISZERO(x.y - b->p1.y) || ISZERO(x.y - b->p2.y)) {
- n.y = 1.0;
- } else if (ISZERO(x.z - b->p1.z) || ISZERO(x.z - b->p2.z)) {
- n.z = 1.0;
- } else
- assert(FALSE);
-
- if (o->inv_trans)
- n = transform_normal(*o->inv_trans, n);
-
- norm(n, n);
-
- return n;
- }
-
- PRIVATE void
- init_box(struct box_data *b)
- {
- setvector(b->p2, 1, 1, 1);
- setvector(b->p2, -1, -1, -1);
- }
-
- PRIVATE struct box_data *
- get_new_box(void)
- {
- struct box_data *b;
-
- b = ALLOC(struct box_data);
-
- CHECK_MEM(b, my_methods.name);
- init_box(b);
-
- return b;
- }
-
- PRIVATE void
- free_box(object *o)
- {
- struct box_data *p = o->data.box;
-
- free((void *) p);
-
- o->type = NOSHAPE;
- }
-
- PRIVATE void
- scale_box(object *o, vector s)
- {
- struct box_data *b = o->data.box;
-
- if (o->inv_trans) {
- generic_scale_object(o, s);
- } else {
- vcproduct(b->p1, s, b->p1);
- vcproduct(b->p2, s, b->p2);
- }
- }
-
- PRIVATE void
- rotate_box(object *o, matrix rotmat)
- {
- generic_rotate_object(o, rotmat);
- }
-
- PRIVATE void
- copy_box(object *dst, object *src)
- {
- struct box_data *d;
-
- assert(dst != NULL && src != NULL);
-
- if (dst->type != BOX)
- d = dst->data.box = get_new_box();
-
- *d = *src->data.box;
- dst->type = src->type;
- }
-
- PRIVATE void
- translate_box(object *o, vector t)
- {
- struct box_data *b = o->data.box;
-
- if (o->inv_trans) {
- generic_translate_object(o, t);
- } else {
- vadd(b->p1, b->p1, t);
- vadd(b->p2, b->p2, t);
- }
- }
-
- PRIVATE void
- print_box(object *o)
- {
- #ifdef DEBUG
- struct box_data *b = o->data.box;
-
- print_v("p1", b->p1);
- print_v("p2", b->p2);
- #endif
- }
-
- PRIVATE struct methods my_methods =
- {
- all_box_intersections,
- box_normal,
- copy_box,
- inside_box,
- rotate_box,
- translate_box,
- scale_box,
- free_box,
- print_box,
- precompute_box,
- "box",
- };
-
-
- PUBLIC object *
- get_new_box_object(void)
- {
- object *o;
-
- o = get_new_object();
- o->data.box = get_new_box();
- o->methods = &my_methods;
- o->type = BOX;
-
- return o;
- }
-