home *** CD-ROM | disk | FTP | other *** search
- /*
- * superq.c -- this should implement superquadrics
- *
- * (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.
- */
-
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
-
-
- #define SGRAD(g,X,n) \
- (g.x = (n).x* SGN(X.x)* pow(ABS(X.x),(n).x),\
- g.y = (n).y* SGN(X.y)* pow(ABS(X.y),(n).y),\
- g.z = (n).z* SGN(X.z)* pow(ABS(X.z),(n).z))
- #define SNORM(X,n) (pow(ABS(X.x),(n).x)+pow(ABS(X.y),(n).y) + pow(ABS(X.z),(n).z))
-
-
- #define MAXIT 200
-
- extern struct methods my_methods;
-
-
- PRIVATE void
- free_superq(object *o)
- {
- free((void *) o->data.superq);
- o->type = NOSHAPE;
- }
-
- PRIVATE bool
- inside_superq(object *o, vector loc)
- {
- struct superq_data *s;
-
- s = o->data.superq;
- if (SNORM(loc, (s->powvect)) < 1)
- return !o->inverted;
- else
- return o->inverted;
- }
-
- PRIVATE void
- print_superq(object *o)
- {
- #ifdef DEBUG
- struct superq_data *p = o->data.superq;
-
- print_v("exponents", p->powvect);
- #endif
- }
-
-
- PRIVATE int intersect_superq(struct superq_data *t, struct ray r, double *rhits, bool chkall);
-
- PRIVATE bool
- all_superq_intersections(dqueue * q, object *o, struct ray *r, int flags,
- bool *isinside)
- {
- int n;
- double inter[2];
- dqueue i;
- struct ray localray;
-
- localray = *r;
- transform_ray(&localray, o);
-
-
- n = intersect_superq(o->data.superq, localray, inter, flags & (CHKALL | CHKINSIDE));
- *isinside = (n % 2) ^ o->inverted;
-
- if (!n)
- return FALSE;
- else {
- i.t = inter[0];
- i.obj = o;
- i.entering = !*isinside;
- add_to_queue(q, i);
-
- if (n > 1 && flags & CHKALL) {
- i.t = inter[1];
- i.obj = o;
- i.entering = *isinside;
- add_to_queue(q, i);
- }
- return TRUE;
- }
- }
-
-
-
- PRIVATE vector maxv =
- {1 + BOUNDFUDGE, 1 + BOUNDFUDGE, 1 + BOUNDFUDGE},
- minv =
- {-1 - BOUNDFUDGE, -1 - BOUNDFUDGE, -1 - BOUNDFUDGE};
-
- /*
- * Intersect a ray with a superquadric.
- */
- PRIVATE int
- intersect_superq(struct superq_data *s, struct ray r, double *rhits, bool chkall)
- {
- int n;
- vector X,
- gradpow,
- lo_grad,
- hi_grad;
- bool inside;
- double bhits[2],
- mid,
- lo_t,
- hi_t;
-
- my_methods.test++;
-
-
- /* intersect bounding box */
- n = intersect_rawbox(minv, maxv, &r, bhits);
- if (!n)
- return 0;
-
- /* determine interval for ray parameter t */
- if (n == 1) {
- lo_t = 2 * tolerance;
- hi_t = bhits[0];
- } else {
- lo_t = bhits[0];
- hi_t = bhits[1];
- }
-
- /* are we inside? */
- svproduct(X, lo_t, r.dir);
- vadd(X, r.pos, X);
-
- if (n == 1)
- inside = (SNORM(X, s->powvect) < 1);
- else
- inside = FALSE;
-
- setvector(gradpow, s->powvect.x - 1, s->powvect.y - 1, s->powvect.z - 1);
-
-
- SGRAD(lo_grad, X, gradpow);
-
-
- if (!inside && vdot(lo_grad, r.dir) > 0) /* the shape is behind us. */
- return 0;
-
- if (inside) {
-
- /*
- * use binary chop to get to surface. We know for sure that hi_t
- * (the box hit) is outside. so an intersection must be between
- * lo_t and hi_t
- */
-
- while (ABS(lo_t - hi_t) > ALG_TOLERANCE) {
- mid = (lo_t + hi_t) / 2;
- svproduct(X, mid, r.dir);
- vadd(X, r.pos, X);
- if (SNORM(X, s->powvect) < 1)
- hi_t = mid;
- else
- lo_t = mid;
- }
-
- rhits[0] = lo_t;
- my_methods.hit++;
- return 1;
-
- } else {
- /* not inside */
- double inside_t,
- savehi,
- lo_dot,
- hi_dot,
- middot;
- vector midgrad;
- int its;
-
- lo_dot = vdot(lo_grad, r.dir);
-
- svproduct(X, hi_t, r.dir);
- vadd(X, r.pos, X);
- SGRAD(hi_grad, X, gradpow);
- hi_dot = vdot(hi_grad, r.dir);
-
- inside = FALSE;
-
-
- /*
- * first we try to find the point with (superq_normal, r.dir) == 0
- * At such a point, the distance to superquadric will be minimal.
- */
- its = MAXIT;
- while (ABS(lo_t - hi_t) > ALG_TOLERANCE && its--) {
-
- mid = (lo_t + hi_t) / 2;
- svproduct(X, mid, r.dir);
- vadd(X, r.pos, X);
-
- SGRAD(midgrad, X, gradpow);
-
- if (vdot(midgrad, X) < 1) {
- inside = TRUE;
- break;
- }
- middot = vdot(midgrad, r.dir);
- if (middot < 0) {
- lo_t = mid;
- lo_grad = midgrad;
- lo_dot = middot;
- } else {
- hi_t = mid;
- hi_grad = midgrad;
- hi_dot = middot;
- }
- }
- if (!its && debug_options & DEBUGRUNTIME)
- warning("intersect_superq(): can't converge");
-
-
- /* minimum distance , and not inside. Exit. */
- if (!inside)
- return 0;
-
- /*
- * we now know that
- *
- * X = pos + mid * dir
- *
- *
- * is inside the superquadric, and pos+lo*dir and pos+hi*dir outside
- * . Now all we have to do is find the intersection inbetween.
- */
-
- my_methods.hit++;
-
- /* use binary chop to get to surface. */
- inside_t = mid;
- savehi = hi_t;
- hi_t = mid;
-
- its = MAXIT;
-
- while (ABS(lo_t - hi_t) > ALG_TOLERANCE && its--) {
-
- mid = (lo_t + hi_t) / 2;
- svproduct(X, mid, r.dir);
- vadd(X, r.pos, X);
- if (SNORM(X, s->powvect) < 1)
- hi_t = mid;
- else
- lo_t = mid;
- }
- if (!its && debug_options & DEBUGRUNTIME)
- warning("intersect_superq(): can't converge");
-
- rhits[0] = lo_t;
- if (!chkall)
- return 1;
-
-
- lo_t = inside_t;
- hi_t = savehi;
- its = MAXIT;
-
- while (ABS(lo_t - hi_t) > ALG_TOLERANCE && its--) {
- mid = (lo_t + hi_t) / 2;
- svproduct(X, mid, r.dir);
- vadd(X, r.pos, X);
- if (SNORM(X, s->powvect) < 1)
- hi_t = mid;
- else
- lo_t = mid;
- }
- if (!its)
- warning("intersect_superq(): can't converge");
- rhits[1] = hi_t;
-
- return 2;
- }
-
- }
-
- /*
- * returns the normal to the superq
- */
- PRIVATE vector
- superq_normal(struct intersect i, vector loc)
- {
- struct superq_data *s;
- vector n,
- gradpow;
- object *o = i.q_ent.obj;
-
- s = i.q_ent.obj->data.superq;
-
- setvector(gradpow, s->powvect.x - 1, s->powvect.y - 1, s->powvect.z - 1);
-
- if (o->inv_trans)
- loc = mvproduct(*o->inv_trans, loc);
-
- SGRAD(n, loc, gradpow);
- if (o->inv_trans)
- n = transform_normal(*o->inv_trans, n);
- norm(n, n);
-
- return n;
- }
-
- /* initialize a superq_data struct */
- PRIVATE void
- init_superq(struct superq_data *t)
- {
- setvector(t->powvect, 2, 2, 2);
- }
-
- /* alloc and init superq */
- PRIVATE struct superq_data *
- get_new_superq(void)
- {
- struct superq_data *s;
- s = ALLOC(struct superq_data);
-
- CHECK_MEM(s, my_methods.name);
- init_superq(s);
- return s;
- }
-
- /* copy the shape data */
- PRIVATE void
- copy_superq(object *dst, object *src)
- {
- assert(dst != NULL && src != NULL);
-
- if (dst->type != SUPERQ)
- dst->data.superq = get_new_superq();
- *dst->data.superq = *src->data.superq;
- dst->type = src->type;
- }
-
- PRIVATE void
- precompute_superq(object *o)
- {
- matrix m;
-
- my_methods.howmuch++;
- if (o->inv_trans)
- copy_matrix(m, *o->inv_trans);
- else
- unit_matrix(m);
- do_box_bound(&o->bmin, &o->bmax, m, minv, maxv);
- global_stats.prims++;
- }
-
- PRIVATE struct methods my_methods =
- {
- all_superq_intersections,
- superq_normal,
- copy_superq,
- inside_superq,
- generic_rotate_object,
- generic_translate_object,
- generic_scale_object,
- free_superq,
- print_superq,
- precompute_superq,
- "superquadric"
- };
-
-
- /* alloc/init */
- PUBLIC object *
- get_new_superq_object(void)
- {
- object *o;
-
- o = get_new_object();
-
- o->data.superq = get_new_superq();
- o->methods = &my_methods;
- o->type = SUPERQ;
-
- return o;
- }
-
- /* eof */
-