home *** CD-ROM | disk | FTP | other *** search
- /*
- * polygon.c -- handle polygon 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.
- */
-
- #include "ray.h"
- #include "proto.h"
- #include "extern.h"
-
- extern struct methods my_methods;
-
- /* zapp */
- PRIVATE void
- free_polygon(object *o)
- {
- free(o->data.polygon->vertices);
- if (o->data.polygon->clips != NULL)
- free(o->data.polygon->clips);
- free((void *) o->data.polygon);
- o->type = NOSHAPE;
- }
-
- /* opps.. Somebody made a mistake. */
- PRIVATE bool
- inside_polygon(object *o, vector i)
- {
- assert(FALSE);
- }
-
- /* scale it. */
- PRIVATE void
- scale_polygon(object *o, vector s)
- {
- struct polygon_data *p = o->data.polygon;
- int i;
- vector invs;
-
- invs.x = 1 / s.x;
- invs.y = 1 / s.y;
- invs.z = 1 / s.z;
-
- for (i = 0; i < p->no_vertices; i++) {
- vcproduct(p->vertices[i], p->vertices[i], s);
- }
- }
-
- /* rotation */
- PRIVATE void
- rotate_polygon(object *o, matrix rotmat)
- {
-
- struct polygon_data *p = o->data.polygon;
- int i;
-
-
- for (i = 0; i < p->no_vertices; i++) {
- p->vertices[i] = mvproduct(rotmat, p->vertices[i]);
- }
- }
-
- /* and translation */
- PRIVATE void
- translate_polygon(object *o, vector t)
- {
- struct polygon_data *p = o->data.polygon;
- int i;
-
- for (i = 0; i < p->no_vertices; i++) {
- vadd(p->vertices[i], p->vertices[i], t);
- }
- }
-
- /* print the data. */
- PRIVATE void
- print_polygon(object *o)
- {
- #ifdef DEBUG
- int i;
-
- struct polygon_data *p = o->data.polygon;
-
-
- /*
- * print_v("normal", p->normal); printf(", %f }\n", p->mov);
- */
- printf("projecting along %c%s\n", p->project_to, (p->convex) ? ", convex" : "");
-
- printf("%d vertices {\n", p->no_vertices);
-
-
- for (i = 0; i < p->no_vertices; i++) {
- print_v("vertex", p->vertices[i]);
- }
- printf("}\n");
- #endif
- }
-
-
- PRIVATE bool
- point_in_concave_polygon(double t, object *o, struct ray *r)
- {
- struct polygon_data *pol = o->data.polygon;
- double m,
- b;
- vector x;
- double *points,
- V[3];
- int i,
- j,
- crosses = 0;
- int qi,
- qj;
- int ri,
- rj;
- int u,
- v;
-
- switch (pol->project_to) {
- case 'x':
- x.x = 0.0;
- x.y = r->pos.y + t * r->dir.y;
- x.z = r->pos.z + t * r->dir.z;
- u = 1;
- v = 2;
- /* 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;
-
- break;
-
- case 'y':
- x.x = r->pos.x + t * r->dir.x;
- x.y = 0.0;
- x.z = r->pos.z + t * r->dir.z;
- u = 0;
- v = 2;
-
- /* 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;
- break;
- case 'z':
- x.x = r->pos.x + t * r->dir.x;
- x.y = r->pos.y + t * r->dir.y;
- x.z = 0.0;
- u = 0;
- v = 1;
-
- /* 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;
-
- break;
- }
- V[0] = x.x;
- V[1] = x.y;
- V[2] = x.z;
-
-
-
- /***** THIS IS DIRTY ******/
- /***** THIS IS NOT PORTABLE *****/
- /* but it works :-) */
- points = (void *) pol->vertices;
-
- for (i = 0; i < pol->no_vertices; i++) {
-
- j = (i + 1) % pol->no_vertices;
-
- qi = 0;
- qj = 0;
- ri = 0;
- rj = 0;
-
- if (*(points + 3 * i + v) == *(points + 3 * j + v))
- continue; /* ignore horizontal lines */
-
- /*
- * If we are both above, or both below the intersection point, go
- * onto the next one.
- */
-
- if (*(points + 3 * i + v) < V[v])
- qi = 1;
- if (*(points + 3 * j + v) < V[v])
- qj = 1;
- if (qi == qj)
- continue;
-
- /*
- * We know one end point was above, and one was below. If theyare
- * both to the left, then we crossed the line to negative
- * infinity, and we continue.
- */
- if (*(points + 3 * i + u) < V[u])
- ri = 1;
- if (*(points + 3 * j + u) < V[u])
- rj = 1;
-
- if (ri & rj) {
- crosses++;
- continue;
- }
- /*
- * Otherwise, if we are both to the right, we can continue without
- * a cross.
- */
-
- if ((ri | rj) == 0)
- continue;
-
-
- /*
- * more difficult acceptance... We have a line segment which
- * occurs with endpoints in diagonally opposite quadrants. We
- * must solve for the intersection, ie, where v = 0.
- */
-
- m = (*(points + 3 * j + v) - *(points + 3 * i + v)) /
- (*(points + 3 * j + u) - *(points + 3 * i + u));
-
- b = (*(points + 3 * j + v) - V[v]) -
- m * (*(points + 3 * j + u) - V[u]);
-
- if ((-b / m) < EPSILON)
- crosses++;
- }
- return (crosses & 1);
- }
-
-
- PRIVATE bool
- point_in_convex_polygon(double t, object *o, struct ray *r)
- {
-
- int i;
- struct polygon_data *pol = o->data.polygon;
- vector a,
- x;
-
-
- #ifdef undefined
- svproduct(x, t, r->dir);
- vadd(x, x, r->pos);
-
- if ((x.x < o->bmin.x) || (x.y < o->bmin.y) || (x.z < o->bmin.z) ||
- (x.x > o->bmax.x) || (x.y > o->bmax.y) || (x.z > o->bmax.z))
- return FALSE;
- #endif
-
- /* clip plane to polygon. */
- switch (pol->project_to) {
- case 'x':
-
- x.x = 0.0;
- x.y = r->pos.y + t * r->dir.y;
- x.z = r->pos.z + 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;
-
-
-
- for (i = 0; i < pol->no_vertices; i++) {
- a.y = x.y - pol->vertices[i].y;
- a.z = x.z - pol->vertices[i].z;
-
- if (a.y * pol->clips[i].y + a.z * pol->clips[i].z < 0)
- return FALSE;
- }
- break;
-
- case 'y':
- x.x = r->pos.x + t * r->dir.x;
- x.y = 0.0;
- x.z = r->pos.z + 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;
- for (i = 0; i < pol->no_vertices; i++) {
- a.x = x.x - pol->vertices[i].x;
- a.z = x.z - pol->vertices[i].z;
-
- if (a.x * pol->clips[i].x + a.z * pol->clips[i].z < 0)
- return FALSE;
- }
- break;
- case 'z':
- x.x = r->pos.x + t * r->dir.x;
- x.y = r->pos.y + 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;
-
- for (i = 0; i < pol->no_vertices; i++) {
- a.y = x.y - pol->vertices[i].y;
- a.x = x.x - pol->vertices[i].x;
- if (a.y * pol->clips[i].y + a.x * pol->clips[i].x < 0)
- return FALSE;
- }
- break;
- }
- return TRUE;
- }
-
-
- /* stuff all intersections down a queue */
- PRIVATE bool
- all_polygon_intersections(dqueue * q, object *o, struct ray *r, int flags, bool *isinside)
- {
- double d;
-
- bool b;
- dqueue inters;
- struct polygon_data *pol = o->data.polygon;
-
- my_methods.test++;
-
- d = vdot(r->dir, pol->normal);
- if (ISZERO(d))
- return 0;
-
- inters.t = (pol->mov - vdot(r->pos, pol->normal)) / d;
- if (inters.t < tolerance || inters.t > r->maxt)
- return FALSE;
-
-
- if (pol->convex)
- b = point_in_convex_polygon(inters.t, o, r);
- else
- b = point_in_concave_polygon(inters.t, o, r);
-
- if (b) {
- my_methods.hit++;
-
- inters.obj = o;
- add_to_queue(q, inters);
- return TRUE;
- } else
- return FALSE;
- }
-
- /* precompute the data. */
- PRIVATE void
- precompute_polygon(object *o)
- {
- struct polygon_data *p = o->data.polygon;
- vector *v,
- proj,
- a,
- b,
- chknormal,
- last;
- int i;
-
- v = p->vertices;
-
- if (p->no_vertices < 3)
- errormsg("polygon must have at least 3 vertices. ");
-
- vsub(a, v[1], v[0]);
- vsub(b, v[2], v[1]);
-
- vcross(p->normal, a, b);
- norm(p->normal, p->normal);
- p->mov = vdot(v[0], p->normal);
-
-
- /* project along which axis ? */
- {
- double max;
-
- if (ABS(p->normal.x) > ABS(p->normal.y)) {
- max = ABS(p->normal.x);
- p->project_to = 'x';
- setvector(proj, SGN(p->normal.z), 0, 0);
- } else {
- max = ABS(p->normal.y);
- p->project_to = 'y';
- setvector(proj, 0, SGN(p->normal.y), 0);
- }
- if (ABS(p->normal.z) > max) {
- setvector(proj, 0, 0, SGN(p->normal.z));
- p->project_to = 'z';
- }
- }
-
- switch (p->project_to) {
- case 'x':
- b.x = a.x = 0;
- break;
- case 'y':
- b.y = a.y = 0;
- break;
- case 'z':
- b.z = a.z = 0;
- break;
- }
- vcross(proj, a, b);
-
- p->clips = malloc(p->no_vertices * sizeof(vector));
-
- CHECK_MEM(p->clips, "polygon clips");
-
- last = a;
- for (i = 0; i < p->no_vertices - 1; i++) {
- vsub(a, v[i + 1], v[i]);
- switch (p->project_to) {
- case 'x':
- last.x = a.x = 0;
- break;
- case 'y':
- last.y = a.y = 0;
- break;
- case 'z':
- last.z = a.z = 0;
- break;
- }
-
- vcross(p->clips[i], proj, a);
-
- /* check convexity. */
- vcross(chknormal, last, a);
- last = a;
- if (vdot(chknormal, p->normal) < 0)
- p->convex = FALSE;
-
- if (isvnull(p->clips[i]))
- warning("degenerate polygon");
-
- update_min_and_max(&o->bmin, &o->bmax, v[i]);
- }
-
- update_min_and_max(&o->bmin, &o->bmax, v[i]);
-
- vsub(a, v[0], v[i]);
- switch (p->project_to) {
- case 'x':
- a.x = 0;
- break;
- case 'y':
- a.y = 0;
- break;
- case 'z':
- a.z = 0;
- break;
- }
- vcross(p->clips[i], proj, a);
-
- vcross(chknormal, last, a);
- if (vdot(chknormal, p->normal) < 0)
- p->convex = FALSE;
-
- if (!p->convex) {
- free(o->data.polygon->clips);
- o->data.polygon->clips = NULL;
- }
- my_methods.howmuch++;
- global_stats.prims++;
- }
-
-
-
- /*
- * returns the normal to the polygon
- */
- PRIVATE vector
- polygon_normal(struct intersect i, vector x)
- {
- return i.q_ent.obj->data.polygon->normal;
- }
-
- /* initialize a polygon_data struct */
- PUBLIC void
- init_polygon(struct polygon_data *t)
- {
- setvector(t->normal, 0, 0, 0);
- t->no_vertices = 0;
- t->mov = 0;
- t->clips = NULL;
- t->vertices = NULL;
- t->convex = TRUE;
- t->project_to = ' ';
- }
-
- /* alloc and init polygon */
- PUBLIC struct polygon_data *
- get_new_polygon(void)
- {
- struct polygon_data *p;
-
- p = ALLOC(struct polygon_data);
-
- CHECK_MEM(p, my_methods.name);
- init_polygon(p);
-
- return p;
- }
-
- /* add vertex to o, doesn't do precomputation. */
- PUBLIC void
- add_vertex_to_polygon(object *o, vector vert)
- {
- struct polygon_data *p;
-
- p = o->data.polygon;
-
- p->no_vertices++;
- p->vertices = realloc(p->vertices, p->no_vertices * sizeof(vector));
-
-
- if (p->vertices == NULL)
- alloc_err("polygon vertex");
- p->vertices[p->no_vertices - 1] = vert;
- }
-
- /* copy the shape data */
- PRIVATE void
- copy_polygon(object *dst, object *src)
- {
- struct polygon_data *p;
-
- assert(dst != NULL && src != NULL);
-
- if (dst->type != POLYGON) {
- dst->data.polygon = get_new_polygon();
- }
- p = dst->data.polygon;
- *p = *src->data.polygon;
- p->vertices = malloc(p->no_vertices * sizeof(vector));
-
- if (p->vertices == NULL)
- alloc_err("polygon vertices");
-
- memcpy(p->vertices, src->data.polygon->vertices, p->no_vertices * sizeof(vector));
-
- dst->type = src->type;
- }
-
- PRIVATE struct methods my_methods =
- {
- all_polygon_intersections,
- polygon_normal,
- copy_polygon,
- inside_polygon,
- rotate_polygon,
- translate_polygon,
- scale_polygon,
- free_polygon,
- print_polygon,
- precompute_polygon,
- "polygon",
- };
-
-
- /* alloc/init */
- PUBLIC object *
- get_new_polygon_object(void)
- {
- object *o;
-
- o = get_new_object();
-
- o->data.polygon = get_new_polygon();
- o->methods = &my_methods;
- o->type = POLYGON;
-
- return o;
- }
-
- /* eof */
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- /* not really.... But who actually reads this crap? :) */
-