home *** CD-ROM | disk | FTP | other *** search
- /*
- * extrusion.c -- handle extrusion type objects
- *
- * (c) 1993, 1994 by 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.
- */
-
-
- /*
- * this turns out to be a routine which resembles the one presented in
- *
- * James T Kajiya, Raytracing procedurally defined objects
- *
- */
-
-
-
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
-
- extern struct methods my_methods;
-
- PRIVATE void
- free_extrusion(object *o)
- {
- free_object(o->data.extrusion->shape);
- free((void *) o->data.extrusion);
-
- o->type = NOSHAPE;
- }
-
- PRIVATE bool
- inside_extrusion(object *o, vector i)
- {
- struct extrusion_data *x;
- bool b;
- vector objectloc,
- projloc;
-
-
- objectloc = mvproduct(*o->inv_trans, i);
- setvector(projloc, objectloc.x, objectloc.y, 0);
- b = x->shape->methods->inside_method(x->shape, projloc);
- return (objectloc.z < x->height && objectloc.z > 0 && b) ^ o->inverted;
- }
-
- PRIVATE void
- print_extrusion(object *o)
- {
- #ifdef DEBUG
- struct extrusion_data *p = o->data.extrusion;
-
- printf("cap: lo %d, hi %d.", p->locap, p->hicap);
- printf("Height %lf\n", p->height);
- printf("Base object:\n");
- print_object(p->shape);
- #endif
- }
-
-
-
- PRIVATE bool
- all_extrusion_intersections(dqueue * globalq, object *o, struct ray *inputray, int flags, bool *isinside)
- {
- dqueue q_ent;
- struct extrusion_data *xtr;
- struct ray projray; /* ray projected onto XY plane */
- int rechit;
- double t;
- bool inside;
- struct ray r;
-
-
- my_methods.test++;
- rechit = 0;
- r = *inputray;
- q_ent.obj = o;
- xtr = o->data.extrusion;
-
- transform_ray(&r, o);
-
- /* do the projection */
- setvector(projray.pos, r.pos.x, r.pos.y, 0);
- setvector(projray.dir, r.dir.x, r.dir.y, 0);
-
-
- if (ABS(r.dir.x) < EPSILON && ABS(r.dir.y) < EPSILON) { /* vertical? */
- double t1,
- t2;
-
- /* inside ? */
- if (xtr->shape->methods->inside_method(xtr->shape, projray.pos)) {
-
- /* then check intersections with horiz. planes */
- if (xtr->locap)
- t1 = -r.pos.z / r.dir.z;
- else
- t1 = -INFTY;
-
- if (xtr->hicap)
- t2 = (xtr->height - r.pos.z) / r.dir.z;
- else
- t2 = -INFTY;
-
- if (t1 > tolerance)
- rechit++;
- if (t2 > tolerance)
- rechit++;
-
- inside = (*isinside) = o->inverted ^ (rechit % 2);
- q_ent.obj2 = NULL;
-
- if (t1 > tolerance) {
- q_ent.entering = (inside = !inside);
- q_ent.t = t1;
- add_to_queue(globalq, q_ent);
- }
- if (t2 > tolerance) {
- q_ent.entering = (inside = !inside);
- q_ent.t = t2;
- add_to_queue(globalq, q_ent);
- }
- } else {
- /*
- * vertical ray which is not inside. This will not hit the
- * shape
- */
- *isinside = !o->inverted;
- return 0;
- }
- } else {
- dqueue *q = get_new_queue(),
- *qp;
- bool gotbot,
- xinside;
- double zcoord,
- ztop,
- zbot;
-
-
- /* Get the intersections of the generating shape */
- xtr->shape->methods->all_intersections_method(q, xtr->shape, &projray, CHKALL | CHKINSIDE, &inside);
-
- if (r.dir.z >= 0) {
- zbot = 0.0;
- ztop = xtr->height;
- } else {
-
- /* convert the problem */
- zbot = -xtr->height;
- ztop = 0.0;
- r.dir.z = -r.dir.z;
- r.pos.z = -r.pos.z;
- }
-
- /* inside extrusion */
- *isinside = xinside = (r.pos.z >= zbot && r.pos.z <= ztop && inside) ^ o->inverted;
-
- gotbot = FALSE;
- for (qp = q; qp != NULL && qp->obj != NULL; qp = qp->next) {
- inside = !inside;
- zcoord = qp->t * r.dir.z + r.pos.z;
-
- /* check bottom intersection */
- if (zcoord >= zbot && !gotbot) {
-
- if (!inside && xtr->locap) {
- t = (zbot - r.pos.z) / r.dir.z;
- if (t > tolerance) {
- q_ent.t = t;
- rechit++;
- q_ent.entering = (xinside = !xinside);
- add_to_queue(globalq, q_ent);
- if (!(flags & CHKALL))
- break;
- }
- }
- gotbot = TRUE;
- }
- /* check top intersection */
- if (zcoord >= ztop) {
- if (xtr->hicap && !inside) {
- t = (ztop - r.pos.z) / r.dir.z;
- if (t > tolerance) {
- q_ent.t = t;
- q_ent.entering = (xinside = !xinside);
- rechit++;
- add_to_queue(globalq, q_ent);
- if (!(flags & CHKALL))
- break;
- }
- }
- break;
- }
- /*
- * we've passed the other tests. This is an intersection with
- * the standing part of the extrusion
- */
- if (zcoord >= zbot) {
- q_ent.t = qp->t;
- q_ent.obj2 = qp->obj;
- q_ent.entering = (xinside = !xinside);
-
- rechit++;
- add_to_queue(globalq, q_ent);
- if (!(flags & CHKALL))
- break;
- }
- }
- free_queue(q);
- }
- if (rechit)
- my_methods.hit++;
-
- return TRUE;
- }
-
- /*
- * returns the normal to the extrusion
- */
- PRIVATE vector
- extrusion_normal(struct intersect i, vector loc)
- {
- struct extrusion_data *xtr;
- struct intersect i2;
- vector objloc,
- n;
-
- xtr = i.q_ent.obj->data.extrusion;
- if (i.q_ent.obj->inv_trans)
- objloc = mvproduct(*i.q_ent.obj->inv_trans, loc);
- if (ISZERO(objloc.z)) {
- setvector(n, 0, 0, -1);
- } else if (ISZERO(objloc.z - xtr->height)) {
- setvector(n, 0, 0, 1);
- } else {
- i2 = i;
- i2.q_ent.obj = i.q_ent.obj2;
- objloc.z = 0;
- n = i.q_ent.obj2->methods->normal_method(i2, objloc);
- if (i.q_ent.obj2->inverted)
- vneg(n, n);
- }
- if (i.q_ent.obj->inv_trans)
- n = transform_normal(*i.q_ent.obj->inv_trans, n);
- norm(n, n);
-
- return n;
- }
-
- /* initialize a extrusion_data struct */
- PRIVATE void
- init_extrusion(struct extrusion_data *t)
- {
- t->shape = NULL;
- t->locap = t->hicap = TRUE;
- t->height = 0;
- }
-
- /* alloc and init extrusion */
- PRIVATE struct extrusion_data *
- get_new_extrusion(void)
- {
- struct extrusion_data *p;
- p = ALLOC(struct extrusion_data);
-
- CHECK_MEM(p, my_methods.name);
- init_extrusion(p);
- return p;
- }
-
- /* copy the shape data */
- PRIVATE void
- copy_extrusion(object *dst, object *src)
- {
- assert(dst != NULL && src != NULL);
-
- if (dst->type != EXTRUSION)
- dst->data.extrusion = get_new_extrusion();
- *dst->data.extrusion = *src->data.extrusion;
- dst->type = src->type;
-
- if (src->data.extrusion->shape != NULL)
- dst->data.extrusion->shape = get_new_object();
-
- copy_object(dst->data.extrusion->shape, src->data.extrusion->shape);
- }
-
-
- /*
- * The bounding volume of a cylinder is found. The algorithm operates in
- * three passes, one for each dimension. In each pass the extrema of the
- * bottom circle of the cylinder are found as values of the parameter t.
- * These values are then used to calculate the position of the two points
- * in the current dimension. Finally, these points from the bottom circle
- * and corresponding points from the top circle are considered while
- * computing the minimum and maximum values of the current dimension. The
- * canonical cylinder has a radius of 1.0 from the Y axis, and ranges from
- * Z=-1 to Z=1.
- */
-
- /* precondition: o is an extrusion of a sphere */
-
- PRIVATE void
- do_cylinder_bound(object *o)
- {
-
- double min[3],
- max[3]; /* returned minimum and maximum of extent */
- matrix ctm,
- tm; /* cumulative transformation matrix */
- int i;
- float t1,
- t2,
- p1,
- p2,
- tmp;
- struct extrusion_data *x;
- struct sphere_data *sph;
- vector tv;
-
- x = o->data.extrusion;
- sph = x->shape->data.sphere;
-
- setvector(tv, 0, 0, 1);
- translate_matrix(ctm, tv);
- setvector(tv, sph->radius, sph->radius, x->height / 2);
- scale_matrix(tm, tv);
- mmproduct(tm, tm, ctm);
-
- if (o->inv_trans) {
- matrix trans;
-
- invert_trans(trans, *o->inv_trans);
- mmproduct(tm, trans, tm);
- }
- transpose_matrix(ctm, tm);
-
- for (i = 0; i < 3; i++) {
-
- /*
- * calculate first extremum. second is +/- PI on other side of
- * circle
- */
- t1 = safe_arctangent(ctm[2][i], ctm[0][i]);
- if (t1 <= 0)
- t2 = t1 + M_PI;
- else
- t2 = t1 - M_PI;
-
- /* find and sort extrema locations in this dimension */
- p1 = ctm[0][i] * cos(t1) - ctm[1][i] + ctm[2][i] * sin(t1) + ctm[3][i];
- p2 = ctm[0][i] * cos(t2) - ctm[1][i] + ctm[2][i] * sin(t2) + ctm[3][i];
- if (p1 > p2) {
- tmp = p1;
- p1 = p2;
- p2 = tmp;
- }
- /*
- * add the difference between bottom and top circles to an
- * extremum
- */
- if (ctm[1][i] < 0) {
- min[i] = p1 + 2 * ctm[1][i];
- max[i] = p2;
- } else {
- min[i] = p1;
- max[i] = p2 + 2 * ctm[1][i];
- }
- }
- o->bmin.x = min[0];
- o->bmin.y = min[1];
- o->bmin.z = min[2];
- o->bmax.x = max[0];
- o->bmax.y = max[1];
- o->bmax.z = max[2];
- }
-
- PRIVATE void
- precompute_extrusion(object *o)
- {
- object *child;
- struct extrusion_data *x;
- vector p1,
- p2;
-
- my_methods.howmuch++;
- global_stats.nonprims++;
-
- x = o->data.extrusion;
- child = x->shape;
- precompute_object(child);
-
- if (child->type == SPHERE && isvnull(child->data.sphere->center) && child->inv_trans == NULL) {
- do_cylinder_bound(o);
- } else {
-
- p1 = child->bmin;
- p1.z = 0.0;
- p2 = child->bmax;
- p2.z = x->height;
-
- do_box_bound(&o->bmin, &o->bmax, *o->inv_trans, p1, p2);
- }
- }
-
-
-
- PRIVATE struct methods my_methods =
- {
- all_extrusion_intersections,
- extrusion_normal,
- copy_extrusion,
- inside_extrusion,
- generic_rotate_object,
- generic_translate_object,
- generic_scale_object,
- free_extrusion,
- print_extrusion,
- precompute_extrusion,
- "extrusion",
- };
-
-
-
- /* alloc/init */
- PUBLIC object *
- get_new_extrusion_object(void)
- {
- object *o;
-
- o = get_new_object();
-
- o->data.extrusion = get_new_extrusion();
- o->methods = &my_methods;
- o->type = EXTRUSION;
-
- return o;
- }
-
- /* create a cylinder running from <p1> to <p2> */
- PUBLIC object *
- make_cylinder_object(vector p1, vector p2, double rad)
- {
-
- vector dir;
- struct extrusion_data *x;
- object *o;
- double theta,
- phi;
- vector rot;
- matrix rotmat;
-
- o = get_new_extrusion_object();
- x = o->data.extrusion;
-
- vsub(dir, p2, p1);
- x->height = veclen(dir);
- x->shape = get_new_sphere_object();
- x->shape->data.sphere->radius = rad;
-
-
- /* express p2 - p1 in cylinder coordinates */
- theta = acos(dir.z / veclen(dir));
- phi = safe_arctangent(dir.y, dir.x);
-
- /* rotate accordingly */
- setvector(rot, 0, theta, phi);
- rotate_matrix(rotmat, rot);
- rotate_object(o, rotmat);
- translate_object(o, p1);
-
- return o;
- }
-
- /* eof */
-