home *** CD-ROM | disk | FTP | other *** search
- /*
- * torus.c -- this implements toruses.
- *
- * (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 <math.h>
- #include <stdio.h>
-
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
-
-
- extern struct methods my_methods;
-
- /* give back memory */
- PRIVATE void
- free_torus(object *o)
- {
- struct torus_data *t = o->data.torus;
-
- free((void *) t);
- o->type = NOSHAPE;
- }
-
- /*
- * inside_xxx, this should be pretty optimized. It is used in every CSG
- * test. (it could be better.. )
- *
- * a torus eqn looks like factor^2 - stuff = 0.
- */
-
- PRIVATE bool
- inside_torus(object *o, vector i)
- {
- struct torus_data *t = o->data.torus;
- double factor,
- stuff;
- vector loc;
-
- if (o->inv_trans)
- loc = mvproduct(*o->inv_trans, i);
-
- factor = vdot(loc, loc) + sqr(t->Rad) - sqr(t->rad);
- loc.y = 0;
- stuff = 4 * sqr(t->Rad) * vdot(loc, loc);
-
- if (sqr(factor) - stuff < 0)
- return !o->inverted;
- else
- return o->inverted;
- }
-
- PRIVATE void
- print_torus(object *o)
- {
- #ifdef DEBUG
- struct torus_data *p = o->data.torus;
-
- printf("Major radius %lf, Minor radius %lf\n", p->Rad, p->rad);
- #endif
- }
-
-
- /*
- * the bounding was inspired on Graphics Gems II, elliptic torus solver.
- */
- PRIVATE int
- intersect_torus(struct torus_data *tor, struct ray r, double *rhits, bool chkall)
- {
- int nhits;
- double yin,
- yout,
- brad;
- spoly tosolve;
- double sphere_hits[2];
- vector tmpvector;
-
-
- my_methods.test++;
-
- /* Transform the intersection ray */
- brad = tor->Rad + tor->rad + BOUNDFUDGE;
- brad = sqr(brad);
-
- setvector(tmpvector, 0, 0, 0);
-
- /*
- * Compute the intersection of the ray with a real minimum bounding
- * sphere.
- */
- nhits = intersect_sphere(tmpvector, brad, &r, sphere_hits);
-
- if (nhits == 0)
- return 0;
-
- if (nhits == 2) {
- if (sphere_hits[0] > r.maxt) /* something has been found
- * closer. Early abort. */
- return 0;
-
- /*
- * Bound the torus by two parallel planes
- */
- yin = r.pos.y + sphere_hits[0] * r.dir.y;
- yout = r.pos.y + sphere_hits[1] * r.dir.y;
-
- if ((yin > tor->rad && yout > tor->rad) || (yin < -tor->rad && yout < -tor->rad))
- return 0;
-
- /*
- * translate the ray along it's direction. This should keep
- * numerical inaccuracies a bit under control.
- */
-
- /* does it, actually? */
- }
- /* let the calculations begin! */
- {
- double R,
- mr,
- quad1,
- lin1,
- cons1,
- quad0,
- lin0,
- cons0;
- vector pos,
- dir;
-
- /*
- * svproduct(pos, baset, r.dir); vadd(pos, r.pos, pos);
- */
- pos = r.pos;
-
- dir = r.dir;
-
- mr = sqr(tor->rad);
- R = sqr(tor->Rad);
-
- /*
- * the equation is:
- *
- * (z^2 + x^2 + y^2 + R - mr)^2 = 4R(x^2 + z^2)
- *
- * note that R = Rad^2, and mr = rad^2
- *
- * Calculate what happens if the ray is subst in (z^2 + x^2 + y^2 + R
- * - r), which is vdot(X,X) + R-r.
- */
-
- cons0 = vdot(pos, pos) + R - mr;
- lin0 = 2 * vdot(pos, dir);
- quad0 = vdot(dir, dir);
-
- /*
- * now subst in right part:
- *
- * 4R(x^2+y^2), which is 4R*vdot(X,X) with X.y = 0
- */
-
- pos.y = dir.y = 0;
- cons1 = R * 4 * vdot(pos, pos);
- lin1 = R * 8 * vdot(pos, dir);
- quad1 = R * 4 * vdot(dir, dir);
-
- /*
- * now work out
- *
- * (cons0 + t * lin0 + t^2 quad0) ^2 - (cons1 + t * lin1 + t^2 quad1)
- */
-
- tosolve.coef[0] = sqr(cons0) - cons1;
- tosolve.coef[1] = 2 * cons0 * lin0 - lin1;
- tosolve.coef[2] = sqr(lin0) + 2 * cons0 * quad0 - quad1;
- tosolve.coef[3] = 2 * lin0 * quad0;
- tosolve.coef[4] = sqr(quad0);
- tosolve.ord = 4;
- }
-
- /* and solve it. */
- if (tor->use_sturm)
- nhits = sturm_solve_rt_poly(&tosolve, rhits, chkall);
- else
- nhits = solve_rt_poly(&tosolve, rhits, chkall);
-
- /*
- * for (i = 0; i < nhits; i++) rhits[i] += ;
- */
-
- if (nhits) {
- my_methods.hit++;
- }
- return nhits;
- }
-
-
- PRIVATE bool
- all_torus_intersections(dqueue * q, object *o, struct ray *r,
- int flags, bool *isinside)
- {
- int n,
- j;
- double inter[4];
- dqueue i;
- bool loc_ins;
- struct ray localray;
-
- localray = *r;
- transform_ray(&localray, o);
-
- n = intersect_torus(o->data.torus, localray, inter, flags & CHKALL);
- loc_ins = *isinside = (n % 2) ^ o->inverted;
-
- if (!n)
- return FALSE;
-
-
- if (flags & CHKALL && n > 1)/* check everything? */
- for (j = 0; j < n; j++) {
- i.t = inter[j];
- i.obj = o;
- i.entering = (loc_ins = !loc_ins);
- add_to_queue(q, i);
- } else {
- /* record only one hit. */
-
- i.t = inter[0];
- i.obj = o;
- i.entering = (loc_ins = !loc_ins);
- add_to_queue(q, i);
- }
-
- return TRUE;
- }
-
- /*
- * returns the normal to the torus
- *
- * The normal has the same direction as a vector from the loc's projection on
- * the torus axis to the intersection point itself. Of course you could
- * also take the gradient of the quartic formula, and then normalize it,
- * but it's a load of work. This is simpler!
- */
- PRIVATE vector
- torus_normal(struct intersect i, vector loc)
- {
- struct torus_data *t;
- vector normal,
- heart_proj;
- object *o = i.q_ent.obj;
-
-
- t = o->data.torus;
-
- if (o->inv_trans)
- loc = mvproduct(*o->inv_trans, loc);
-
- heart_proj = loc;
- heart_proj.y = 0;
- norm(heart_proj, heart_proj);
- svproduct(heart_proj, t->Rad, heart_proj);
-
- /* heart_proj now is the projection of loc on the tube axis. */
- vsub(normal, loc, heart_proj);
-
- if (o->inv_trans)
- normal = transform_normal(*o->inv_trans, normal);
-
- if (quickveclen(normal) < EPSILON && (debug_options & DEBUGRUNTIME))
- warning("Weird torus!");
- else
- norm(normal, normal);
-
- return normal;
- }
-
-
- /* initialize a torus_data struct */
- PUBLIC void
- init_torus(struct torus_data *t)
- {
- t->Rad = 1.0;
- t->rad = 0.5;
- t->use_sturm = FALSE;
- }
-
- /* alloc and init torus */
- PUBLIC struct torus_data *
- get_new_torus(void)
- {
- struct torus_data *p;
- p = ALLOC(struct torus_data);
-
- CHECK_MEM(p, my_methods.name);
- init_torus(p);
- return p;
- }
-
- /* copy the shape data */
- PRIVATE void
- copy_torus(object *dst, object *src)
- {
- struct torus_data *s,
- *d;
-
- assert(dst != NULL && src != NULL);
-
- if (dst->type != TORUS)
- dst->data.torus = get_new_torus();
- *(d = dst->data.torus) = *(s = src->data.torus);
- dst->type = src->type;
- }
-
-
- /* bounding volume calculation from Gems III */
- /*
- * I did a little testing, it seems to work
- *
- *
- * The bounding volume of a torus is found. The algorithm performs three
- * passes, one for each dimension. In each pass the extrema of the
- * surface are found as values of the parameters u and v. These values
- * are then used to calculate the position of the two points in the
- * current dimension. Finally, these points are sorted to find the minimum
- * and maximum extents. The torus is defined as the rotation of a circle
- * that is perpendicular to the XZ plane. This circle has radius q, and
- * its center lies in the XZ plane and is rotated about the Y axis in a
- *
- * circle of radius r. The value of q must be less than that of r.
- */
-
- PRIVATE void
- precompute_torus(object *o)
- {
- struct torus_data *t = o->data.torus;
- double min[3],
- max[3]; /* returned minimum and maximum of extent */
- matrix ctm,
- trans; /* cumulative transformation matrix */
- double r; /* major radius of torus */
- double q; /* minor radius of torus */
- int i;
- double u1,
- u2,
- v1,
- v2,
- tmp,
- denominator;
-
-
-
- /* do bounding */
- q = t->rad;
- r = t->Rad;
- if (o->inv_trans) {
- invert_trans(trans, *o->inv_trans);
-
- /* Gems do everything transposed. */
- transpose_matrix(ctm, trans);
- } else
- unit_matrix(ctm);
-
- for (i = 0; i < 3; i++) {
-
- /* calculate first extremum. assure that -PI/2 <= v1 <= PI/2 */
- u1 = safe_arctangent(ctm[2][i], ctm[0][i]);
- denominator = ctm[0][i] * cos(u1) + ctm[2][i] * sin(u1);
- v1 = safe_arctangent(ctm[1][i], denominator);
-
- /* second extremum is +/- PI from u1, negative of v1 */
- if (u1 <= 0)
- u2 = u1 + M_PI;
- else
- u2 = u1 - M_PI;
- v2 = -v1;
-
- /* find and sort extrema locations in this dimension */
- min[i] =
- ctm[0][i] * cos(u1) * (r + q * cos(v1)) +
- ctm[1][i] * sin(v1) * q +
- ctm[2][i] * sin(u1) * (r + q * cos(v1)) +
- ctm[3][i];
- max[i] =
- ctm[0][i] * cos(u2) * (r + q * cos(v2)) +
- ctm[1][i] * sin(v2) * q +
- ctm[2][i] * sin(u2) * (r + q * cos(v2)) +
- ctm[3][i];
- if (min[i] > max[i]) {
- tmp = max[i];
- max[i] = min[i];
- min[i] = tmp;
- }
- }
-
- 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];
-
- my_methods.howmuch++;
- global_stats.prims++;
- }
-
- PRIVATE struct methods my_methods =
- {
- all_torus_intersections,
- torus_normal,
- copy_torus,
- inside_torus,
- generic_rotate_object,
- generic_translate_object,
- generic_scale_object,
- free_torus,
- print_torus,
- precompute_torus,
- "torus",
- };
-
-
- /* alloc/init */
- PUBLIC object *
- get_new_torus_object(void)
- {
- object *o;
-
- o = get_new_object();
-
- o->data.torus = get_new_torus();
- o->methods = &my_methods;
- o->type = TORUS;
-
- return o;
- }
-
- /* eof */
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /* not really.... But who actually reads this crap? :) */
-