home *** CD-ROM | disk | FTP | other *** search
- /*
- * triangle.c -- handle smooth triangles.
- *
- * (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 void
- free_triangle(object *o)
- {
- free((void *) o->data.triangle);
- if (o->data.triangle->normals != NULL)
- free(o->data.triangle->normals);
- o->type = NOSHAPE;
- }
-
- PRIVATE bool
- inside_triangle(object *o, vector i)
- {
- assert(FALSE);
- }
-
- /* scale it. */
- PRIVATE void
- scale_triangle(object *o, vector s)
- {
- struct triangle_data *t = o->data.triangle;
- vector invs;
- double len;
- int i;
-
- setvector(invs, 1 / s.x, 1 / s.y, 1 / s.z);
- for (i = 0; i < 3; i++) {
- vcproduct(t->vertices[i], t->vertices[i], s);
- if (t->normals != NULL) {
-
- /* preserve length */
- len = veclen(t->normals[i]);
- vcproduct(t->normals[i], t->normals[i], invs);
- norm(t->normals[i], t->normals[i]);
- svproduct(t->normals[i], len, t->normals[i]);
- }
- }
- }
-
- /* rotation */
- PRIVATE void
- rotate_triangle(object *o, matrix rotmat)
- {
- struct triangle_data *t = o->data.triangle;
- int i;
-
-
- for (i = 0; i < 3; i++) {
- t->vertices[i] = mvproduct(rotmat, t->vertices[i]);
- if (t->normals != NULL)
- t->normals[i] = mvproduct(rotmat, t->normals[i]);
- }
- }
-
- /* and translation */
- PRIVATE void
- translate_triangle(object *o, vector tr)
- {
- int i;
- struct triangle_data *t = o->data.triangle;
-
- for (i = 0; i < 3; i++) {
- vadd(t->vertices[i], tr, t->vertices[i]);
- }
- }
-
- PRIVATE void
- print_triangle(object *o)
- {
- #ifdef DEBUG
- int i;
- struct triangle_data *p = o->data.triangle;
-
- for (i = 0; i < 3; i++) {
- print_v("vertex", p->vertices[i]);
- if (p->normals != NULL)
- print_v("normal", p->normals[i]);
- }
- printf("projecting along %c\n", p->project_to);
- print_v("linfunc1", p->linfunc1);
- print_v("linfunc2", p->linfunc2);
- print_v("normal", p->n);
- #endif
- }
-
- PRIVATE bool
- all_triangle_intersections(dqueue * q, object *o, struct ray *r, int flags, bool *isinside)
- {
- dqueue q_ent;
- vector x;
- double coef1,
- coef2;
-
- struct triangle_data *s = o->data.triangle;
-
- q_ent.obj = o;
-
-
- my_methods.test++;
-
-
- { /* do plane intersection */
- double d;
-
- d = vdot(r->dir, s->n);
- if (ISZERO(d))
- return 0;
-
- q_ent.t = (s->mov - vdot(r->pos, s->n)) / d;
- }
-
-
- /* try to get out lazily */
- if (q_ent.t < tolerance || q_ent.t > r->maxt)
- return FALSE;
-
- /*
- * write x-vertex2 as coef1 * (vertex0-vertex1) + coef2 *
- * (vertex0-vertex2)
- */
-
- /*
- * do the intersection. What really happens is the projected version
- * of:
- */
- #ifdef undefined
-
- vadd(x, x, r->pos);
- svproduct(x, q_ent.t, r->dir);
- vsub(x, x, s->vertices[2]);
-
- coef1 = vdot(x, s->linfunc1);
-
- if (coef1 < 0)
- return FALSE;
-
- coef2 = vdot(x, s->linfunc2);
- if (coef2 < 0)
- return FALSE;
- #endif
-
-
- switch (s->project_to) {
- case 'x':
- x.x = 0.0;
- x.y = r->pos.y + q_ent.t * r->dir.y;
- x.z = r->pos.z + q_ent.t * r->dir.z;
-
- /* bounding box test. Inspired on GGems */
- if ((x.y < o->bmin.y) || (x.y > o->bmax.y) || (x.z < o->bmin.z) || (x.z > o->bmax.z))
- return FALSE;
-
- x.y = x.y - s->vertices[2].y;
- x.z = x.z - s->vertices[2].z;
- coef1 = x.y * s->linfunc1.y + x.z * s->linfunc1.z;
- if (coef1 < 0)
- return FALSE;
-
- coef2 = x.y * s->linfunc2.y + x.z * s->linfunc2.z;
-
- if (coef2 < 0)
- return FALSE;
- break;
- case 'y':
-
- x.x = r->pos.x + q_ent.t * r->dir.x;
- x.y = 0.0;
- x.z = r->pos.z + q_ent.t * r->dir.z;
-
- /* bounding box test. Inspired on GGems */
- if ((x.x < o->bmin.x) || (x.x > o->bmax.x) || (x.z < o->bmin.z) || (x.z > o->bmax.z))
- return FALSE;
- x.x = x.x - s->vertices[2].x;
-
- x.z = x.z - s->vertices[2].z;
- coef1 = x.x * s->linfunc1.x + x.z * s->linfunc1.z;
- if (coef1 < 0)
- return FALSE;
-
- coef2 = x.x * s->linfunc2.x + x.z * s->linfunc2.z;
-
- if (coef2 < 0)
- return FALSE;
- break;
-
- case 'z':
- x.x = r->pos.x + q_ent.t * r->dir.x;
- x.y = r->pos.y + q_ent.t * r->dir.y;
- x.z = 0.0;
-
- /* bounding box test. Inspired on GGems */
- if ((x.x <
- o->bmin.x) || (x.x > o->bmax.x) || (x.y < o->bmin.y) || (x.y >
- o->bmax.y))
- return FALSE;
-
- x.x = x.x - s->vertices[2].x;
- x.y = x.y - s->vertices[2].y;
-
- coef1 = x.y * s->linfunc1.y + x.x * s->linfunc1.x;
- if (coef1 < 0)
- return FALSE;
-
- coef2 = x.y * s->linfunc2.y + x.x * s->linfunc2.x;
-
- if (coef2 < 0)
- return FALSE;
- break;
- default:
- assert(FALSE);
- }
-
- #ifdef DEBUG /* should be removed */
- if (debug_options & DEBUGRUNTIME) {
- vector t1,
- t2,
- t3;
-
- svproduct(x, q_ent.t, r->dir);
- vadd(x, x, r->pos);
-
- svproduct(t1, coef1, s->vertices[0]);
- svproduct(t2, coef2, s->vertices[1]);
- svproduct(t3, 1 - coef1 - coef2, s->vertices[2]);
- vadd(t1, t1, t2);
- vadd(t1, t1, t3);
- vsub(t1, t1, x);
- if (!ISZERO(veclen(t1)))
- assert(FALSE);
- }
- #endif
-
- if (coef1 + coef2 > 1)
- return FALSE;
- else {
- add_to_queue(q, q_ent);
- my_methods.hit++;
- return TRUE;
- }
- }
-
- /*
- * returns the normal to the triangle
- */
- PRIVATE vector
- triangle_normal(struct intersect i, vector loc)
- {
- struct triangle_data *s = i.q_ent.obj->data.triangle;
-
- if (s->normals != NULL) {
- double coef1,
- coef2,
- coef3;
- vector normal,
- normaltemp;
-
-
- vsub(loc, loc, s->vertices[2]);
- switch (s->project_to) {
- case 'x':
- loc.x = 0;
- break;
- case 'y':
- loc.y = 0;
- break;
- case 'z':
- loc.z = 0;
- break;
- default:
- assert(FALSE);
- }
-
-
-
- /* decompose into base formed by two edges */
- coef1 = vdot(loc, s->linfunc1);
- coef2 = vdot(loc, s->linfunc2);
- coef3 = 1 - coef1 - coef2;
- svproduct(normal, coef1, s->normals[0]);
- svproduct(normaltemp, coef2, s->normals[1]);
- vadd(normal, normaltemp, normal);
- svproduct(normaltemp, coef3, s->normals[2]);
- vadd(normal, normaltemp, normal);
- norm(normal, normal);
- return normal;
- } else {
- return s->n;
- }
- }
-
- /* initialize a triangle_data struct */
- PRIVATE void
- init_triangle(struct triangle_data *t)
- {
- setvector(t->vertices[0], 0, 0, 0);
- setvector(t->vertices[1], 0, 1, 0);
- setvector(t->vertices[2], 1, 0, 0);
- setvector(t->linfunc1, 0, 0, 0);
- setvector(t->linfunc2, 0, 0, 0);
- t->project_to = ' ';
- }
-
- PRIVATE void
- init_smooth_triangle(struct triangle_data *t)
- {
- setvector(t->vertices[0], 0, 0, 0);
- setvector(t->vertices[1], 0, 1, 0);
- setvector(t->vertices[2], 1, 0, 0);
- setvector(t->normals[0], 0, 0, 0);
- setvector(t->normals[1], 0, 1, 0);
- setvector(t->normals[2], 1, 0, 0);
- setvector(t->linfunc1, 0, 0, 0);
- setvector(t->linfunc2, 0, 0, 0);
- t->project_to = ' ';
- }
-
- PRIVATE struct triangle_data *
- get_new_triangle(void)
- {
- struct triangle_data *p;
- p = ALLOC(struct triangle_data);
-
- CHECK_MEM(p, my_methods.name);
- init_triangle(p);
- return p;
- }
-
- /* copy the shape data */
- PRIVATE void
- copy_triangle(object *dst, object *src)
- {
- assert(dst != NULL && src != NULL);
-
- if (dst->type != TRIANGLE)
- dst->data.triangle = get_new_triangle();
- *dst->data.triangle = *src->data.triangle;
- dst->type = src->type;
- }
-
- PRIVATE void
- precompute_triangle(object *o)
- {
- vector bas1,
- bas2;
- struct triangle_data *s = o->data.triangle;
-
- vector orth,
- bas1_norm;
- double alpha,
- normlen;
- int i;
-
-
- vsub(bas1, s->vertices[0], s->vertices[2]);
- vsub(bas2, s->vertices[1], s->vertices[2]);
-
- vcross(s->n, bas1, bas2);
- normlen = veclen(s->n);
- if (normlen < EPSILON)
- warning("degenerate triangle");
- else
- svproduct(s->n, 1 / normlen, s->n);
-
- s->mov = vdot(s->n, s->vertices[2]);
- /* project along which axis ? */
-
- {
- double max;
-
- if (ABS(s->n.x) > ABS(s->n.y)) {
- max = ABS(s->n.x);
- s->project_to = 'x';
- } else {
- max = ABS(s->n.y);
- s->project_to = 'y';
- }
- if (ABS(s->n.z) > max) {
- s->project_to = 'z';
- }
- }
-
- /*
- * we want to express x in
- *
- * a_1 x_1 + a_2 x_2 + a_3 x_3,
- *
- * with a_1 + a_2+ a_3 = 1 this is equivalent to (x - x_3) = a_1 ( x_1 -
- * x_3) + a_2 ( x_2 -x_3) So we have to express x-x_3 in the basis {
- * (x_1-x_3), (x_2-x_3) }
- *
- * The theory of finite dimensional vectorspaces states, that the
- * coefficients a_1 and a_2 can be found by taking an improduct of
- * suitable linfunc1 and linfunc2 with x - x_3. These linfunc[12] are
- * computed here:
- *
- * since this is a 2D problem, we might as well project the intersection
- * and the vertices onto one of the coordinate planes.
- *
- * first we compute orth, a vector orthogonal to bas1 = x1 - x3
- */
-
-
- switch (s->project_to) {
- case 'x':
- bas1.x = bas2.x = 0;
- break;
- case 'y':
- bas1.y = bas2.y = 0;
- break;
- case 'z':
- bas1.z = bas2.z = 0;
- break;
- default:
- assert(FALSE);
- }
-
- alpha = -vdot(bas1, bas2) / vdot(bas1, bas1);
- svproduct(orth, alpha, bas1);
- vadd(orth, orth, bas2);
-
- svproduct(s->linfunc2, 1 / vdot(orth, orth), orth);
- svproduct(s->linfunc1, alpha, s->linfunc2);
- svproduct(bas1_norm, 1 / vdot(bas1, bas1), bas1);
- vadd(s->linfunc1, bas1_norm, s->linfunc1);
-
-
- for (i = 0; i < 3; i++)
- update_min_and_max(&o->bmin, &o->bmax, s->vertices[i]);
-
- my_methods.howmuch++;
- global_stats.prims++;
- }
-
- PRIVATE struct methods my_methods =
- {
- all_triangle_intersections,
- triangle_normal,
- copy_triangle,
- inside_triangle,
- rotate_triangle,
- translate_triangle,
- scale_triangle,
- free_triangle,
- print_triangle,
- precompute_triangle,
- "triangle",
- };
-
-
- /* alloc/init */
- PUBLIC object *
- get_new_triangle_object(void)
- {
- object *o;
-
- o = get_new_object();
-
- o->data.triangle = ALLOC(struct triangle_data);
-
- CHECK_MEM(o->data.triangle, my_methods.name);
-
- init_triangle(o->data.triangle);
- o->methods = &my_methods;
- o->type = TRIANGLE;
-
- return o;
- }
-
- /* alloc/init */
- PUBLIC object *
- get_new_smooth_triangle_object(void)
- {
- object *o;
- struct triangle_data *p;
-
- o = get_new_object();
-
- p = ALLOC(struct triangle_data);
-
- CHECK_MEM(p, my_methods.name);
- p->normals = malloc(3 * sizeof(vector));
-
- CHECK_MEM(p->normals, "triangle normals");
- o->data.triangle = p;
-
- o->methods = &my_methods;
- o->type = TRIANGLE;
- init_smooth_triangle(o->data.triangle);
-
- return o;
- }
-
-
-
- /* eof */
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /* not really.... But who actually reads this crap? :) */
-