home *** CD-ROM | disk | FTP | other *** search
- /*
- * quadric.c -- standard quadratic shaperoutines.
- *
- * (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"
-
- extern struct methods my_methods;
-
-
- PRIVATE bool inside_quadric(object *o, vector x);
- PUBLIC struct quadric_data *get_new_quadric(void);
- PRIVATE int intersect_quadric(struct quadric_data *qp, struct ray *r, double *roots);
-
- PRIVATE bool
- all_quadric_intersections(dqueue * q, object *o, struct ray *r,
- int flags, bool *isinside)
- {
- int no_int;
- dqueue q_ent;
- double roots[2];
- vector sample;
-
- no_int = intersect_quadric(o->data.quadric, r, roots);
- if (flags & CHKINSIDE)
- if (o->data.quadric->isclosed) {
- *isinside = (no_int % 2) ^ o->inverted;
- } else {
- svproduct(sample, 2 * tolerance, r->dir);
- vadd(sample, sample, r->pos);
- *isinside = inside_quadric(o, sample);
- }
- if (!no_int)
- return FALSE;
-
- q_ent.t = roots[0];
- q_ent.obj = o;
- q_ent.entering = !(*isinside);
- add_to_queue(q, q_ent);
-
- if (no_int > 1 && flags & CHKALL) {
- q_ent.t = roots[1];
- q_ent.obj = o;
- q_ent.entering = *isinside;
- add_to_queue(q, q_ent);
- }
- return TRUE;
- }
-
-
- /* intersect ray with quadratic form. */
- PRIVATE int
- intersect_quadric(struct quadric_data *qp, struct ray *r, double *roots)
- {
- double a1,
- a2,
- a3,
- b1,
- b2,
- b3,
- xx,
- yy,
- zz,
- xy,
- yz,
- xz,
- x0,
- y0,
- z0;
- spoly quad_poly;
- int sols;
-
- my_methods.test++;
-
- a1 = r->dir.x;
- a2 = r->dir.y;
- a3 = r->dir.z;
- b1 = r->pos.x;
- b2 = r->pos.y;
- b3 = r->pos.z;
-
- xx = qp->xx;
- yy = qp->yy;
- zz = qp->zz;
- xy = qp->xy;
- yz = qp->yz;
- xz = qp->xz;
- x0 = qp->x0;
- y0 = qp->y0;
- z0 = qp->z0;
-
- /* coefficients of t^2, t^1 and t^0 */
- quad_poly.coef[2] = xx * sqr(a1) + yy * sqr(a2) + zz * sqr(a3)
- + xy * a1 * a2 + yz * a2 * a3 + xz * a3 * a1;
-
- quad_poly.coef[1] = 2 * (xx * a1 * b1 + yy * a2 * b2 + zz * a3 * b3)
- + xy * (a1 * b2 + b1 * a2)
- + yz * (a2 * b3 + a3 * b2)
- + xz * (a1 * b3 + a3 * b1)
- + x0 * a1 + y0 * a2 + z0 * a3;
-
- quad_poly.coef[0] = xx * sqr(b1) + yy * sqr(b2) + zz * sqr(b3)
- + xy * b1 * b2 + yz * b2 * b3 + xz * b1 * b3 +
- x0 * b1 + y0 * b2 + z0 * b3 + qp->a;
-
-
- quad_poly.ord = 2;
- sols = solve_rt_poly(&quad_poly, roots, TRUE); /* solve polynomial. */
-
- if (sols)
- my_methods.hit++;
-
- return sols;
- }
-
-
- /* normal to a quadric. It's just the gradient at the desired point */
- PRIVATE vector
- quadric_normal(struct intersect i, vector x)
- {
- vector n;
- object *o = i.q_ent.obj;
- struct quadric_data *qp;
-
- qp = o->data.quadric;
-
-
- /* calculate gradient: it's svproduct((d/dx, d/dy, d/dz) , function) */
- n.x = 2 * qp->xx * x.x + qp->xy * x.y + qp->xz * x.z + qp->x0;
- n.y = 2 * qp->yy * x.y + qp->yz * x.z + qp->xy * x.x + qp->y0;
- n.z = 2 * qp->zz * x.z + qp->xz * x.x + qp->yz * x.y + qp->z0;
-
- if (quickveclen(n) < EPSILON) { /* null vector */
- return n; /* just return, don't normalize */
- }
- norm(n, n);
- return n;
- }
-
- PRIVATE bool
- inside_quadric(object *o, vector x)
- {
- struct quadric_data *q = o->data.quadric;
-
- if (q->xx * sqr(x.x) + q->yy * sqr(x.y) + q->zz * sqr(x.z)
- + q->xy * x.x * x.y + q->yz * x.z * x.y + q->xz * x.x * x.z
- + q->x0 * x.x + q->y0 * x.y + q->z0 * x.z + q->a < 0)
- return !o->inverted;
- else
- return o->inverted;
- }
-
- PRIVATE void
- translate_quadric(object *o, vector t)
- {
- struct quadric_data quad,
- *q;
-
- q = o->data.quadric;
-
- quad = *q;
-
- /*
- * substitute x:=x-t.x, y:=y-t.y, z:=z-t.z in the quadratic equation.
- * Is there a smarter way of doing this?
- */
-
- q->a += -(quad.x0 * t.x + quad.y0 * t.y + quad.z0 * t.z) +
- quad.xy * t.x * t.y + quad.yz * t.y * t.z + quad.xz * t.z * t.x +
- quad.xx * sqr(t.x) + quad.yy * sqr(t.y) + quad.zz * sqr(t.z);
-
- q->x0 += -2 * t.x * quad.xx + -t.y * quad.xy - t.z * quad.xz;
- q->y0 += -2 * t.y * quad.yy + -t.z * quad.yz - t.x * quad.xy;
- q->z0 += -2 * t.z * quad.zz + -t.x * quad.xz - t.y * quad.yz;
- }
-
-
- /*
- * rotate quadric: a quadric is determined by it's equation Q: (Ax,x) +
- * (b,x) + c = 0. (A matrix, b vector, c constant) so if x \in RQ (R is a
- * rotation, then R^{-1}x \in Q, so (AR^-1x, x) + (b, R^-1x) + c = 0, or
- * (RAR^Tx,x) + (Rx,b) +c = 0.
- */
-
- PRIVATE void
- rotate_quadric(object *o, matrix rotmat)
- {
- matrix quad,
- prod,
- transm;
- vector lin;
- struct quadric_data *q;
-
- q = o->data.quadric;
-
- if (o == NULL || q == NULL)
- assert(FALSE);
-
-
- null_matrix(quad);
-
- quad[0][0] = q->xx;
- quad[1][1] = q->yy;
- quad[2][2] = q->zz;
- quad[0][1] = quad[1][0] = q->xy / 2.0;
- quad[1][2] = quad[2][1] = q->yz / 2.0;
- quad[0][2] = quad[2][0] = q->xz / 2.0;
-
- transpose_matrix(transm, rotmat);
-
- /* calc AR^T, store result in prod, quad is no longer needed */
- mmproduct(prod, quad, transm);
-
- /* calc RAR^T, store result in quad */
- mmproduct(quad, rotmat, prod);
-
- q->xx = quad[0][0];
- q->yy = quad[1][1];
- q->zz = quad[2][2];
-
- q->xy = quad[0][1] + quad[1][0];
- q->yz = quad[1][2] + quad[2][1];
- q->xz = quad[0][2] + quad[2][0];
-
- lin.x = q->x0;
- lin.y = q->y0;
- lin.z = q->z0;
- rotate_vector(&lin, rotmat);
- q->x0 = lin.x;
- q->y0 = lin.y;
- q->z0 = lin.z;
- }
-
- PRIVATE void
- scale_quadric(object *o, vector s)
- {
- double a,
- b,
- c;
- struct quadric_data *q;
-
- q = o->data.quadric;
- if (q == NULL)
- assert(FALSE);
-
- a = 1 / s.x;
- b = 1 / s.y;
- c = 1 / s.z;
-
- q->xx *= sqr(a);
- q->yy *= sqr(b);
- q->zz *= sqr(c);
-
- q->xy *= a * b;
- q->yz *= b * c;
- q->xz *= c * a;
-
- q->x0 *= a;
- q->y0 *= b;
- q->z0 *= c;
- }
-
-
- PRIVATE void
- copy_quadric(object *dst, object *src)
- {
- if (src == NULL || dst == NULL)
- assert(FALSE);
- if (dst->type != QUADRIC)
- dst->data.quadric = get_new_quadric();
- *dst->data.quadric = *src->data.quadric;
- dst->type = src->type;
- }
-
-
- PRIVATE void
- print_quadric(object *o)
- {
- #ifdef DEBUG
- struct quadric_data *q;
-
- q = o->data.quadric;
-
- printf("%5lfx^2 + %5lfy^2 + %5lfz^2 + %5lfxy\n"
- "\t+ %5lfyz +%5lfxz %5lfx\n"
- "\t+ %5lfy + %5lfz + %5lf = 0\n",
- q->xx, q->yy, q->zz,
- q->xy, q->yz, q->xz,
- q->x0, q->y0, q->z0,
- q->a);
-
- #endif
- }
-
- PRIVATE void
- free_quadric(object *o)
- {
- free((void *) o->data.quadric);
- o->type = NOSHAPE;
- }
-
- PUBLIC void
- init_quadric(struct quadric_data *p)
- {
- p->xx = p->yy = p->zz = p->xy = p->yz = p->xz = p->x0 = p->y0 = p->z0 = p->a = 0.0;
- p->isclosed = FALSE;
- }
-
- PUBLIC struct quadric_data *
- get_new_quadric(void)
- {
- struct quadric_data *p;
- p = ALLOC(struct quadric_data);
-
- CHECK_MEM(p, my_methods.name);
-
- init_quadric(p);
-
- return p;
- }
-
- PRIVATE void
- precompute_quadric(object *o)
- {
- my_methods.howmuch++;
- global_stats.prims++;
- }
-
- PRIVATE struct methods my_methods =
- {
- all_quadric_intersections,
- quadric_normal,
- copy_quadric,
- inside_quadric,
- rotate_quadric,
- translate_quadric,
- scale_quadric,
- free_quadric,
- print_quadric,
-
- precompute_quadric,
- "quadric",
- };
-
- PUBLIC object *
- get_new_quadric_object(void)
- {
- object *o;
-
- o = get_new_object();
- o->data.quadric = get_new_quadric();
- o->methods = &my_methods;
- o->type = QUADRIC;
-
- return o;
- }
-