Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
gle/ex_angle.c
/* |
* MODULE NAME: ex_angle.c |
* |
* FUNCTION: |
* This module contains code that draws extrusions with angled |
* joins ("angle join style"). It also inserts colors and normals |
* where necessary, if appropriate. |
* |
* HISTORY: |
* written by Linas Vepstas August/September 1991 |
* split into multiple compile units, Linas, October 1991 |
* added normal vectors Linas, October 1991 |
* "code complete" (that is, I'm done), Linas Vepstas, October 1991 |
* work around OpenGL's lack of support for concave polys, June 1994 |
*/ |
#ifdef __APPLE__ |
#include "gle_osx.h" |
#endif |
#include <stdlib.h> |
#include <math.h> |
#include <string.h> /* for the memcpy() subroutine */ |
#include <tube.h> |
#include "port.h" |
#include "gutil.h" |
#include "vvector.h" |
#include "tube_gc.h" |
#include "extrude.h" |
#include "intersect.h" |
#include "segment.h" |
/* ============================================================ */ |
/* |
* Algorithmic trivia: |
* |
* There is a slight bit of trivia which the super-duper exacto coder |
* needs to know about the code in this module. It is this: |
* |
* This module attempts to correctly treat contour normal vectors |
* by applying the inverse transpose of the 2D contour affine |
* transformation to the 2D contour normals. This is perfectly correct, |
* when applied to the "raw" join style. However, if the affine transform |
* has a strong rotational component, AND the join style is angle or |
* cut, then the normal vectors would continue to rotate as the |
* intersect point is extrapolated. |
* |
* The extrapolation of the inverse-transpose matrix to the intersection |
* point is not done. This would appear to be overkill for most |
* situations. The viewer might possibly detect an artifact of the |
* failure to do this correction IF all three of the following criteria |
* were met: |
* 1) The affine xform has a strong rotational component, |
* 2) The angle between two succesive segments is sharp (greater than 15 or |
* 30 degrees). |
* 3) The join style is angle or cut. |
* |
* However, I beleive that it is highly unlikely that the viewer will |
* detect any artifacts. The reason I beleive this is that a strong |
* rotational component will twist a segment so strongly that the more |
* visible artifact will be that a segment is composed of triangle strips. |
* As the user attempts to minimize the tesselation artifacts by shortening |
* segments, then the rotational component will decrease in proportion, |
* and the lighting artifact will fall away. |
* |
* To summarize, there is a slight inexactness in this code. The author |
* of the code beleives that this inexactness results in miniscule |
* errors in every situation. |
* |
* Linas Vepstas March 1993 |
*/ |
/* ============================================================ */ |
void draw_angle_style_front_cap (int ncp, /* number of contour points */ |
gleDouble bi[3], /* biscetor */ |
gleDouble point_array[][3]) /* polyline */ |
{ |
int j; |
#ifdef OPENGL_10 |
GLUtriangulatorObj *tobj; |
#endif /* OPENGL_10 */ |
if (bi[2] < 0.0) { |
VEC_SCALE (bi, -1.0, bi); |
} |
#ifdef GL_32 |
/* old-style gl handles concave polygons no problem, so the code is |
* simple. New-style gl is a lot more tricky. */ |
/* draw the end cap */ |
BGNPOLYGON (); |
N3F (bi); |
for (j=0; j<ncp; j++) { |
V3F (point_array[j], j, FRONT_CAP); |
} |
ENDPOLYGON (); |
#endif /* GL_32 */ |
#ifdef OPENGL_10 |
N3F(bi); |
tobj = gluNewTess (); |
gluTessCallback (tobj, GLU_BEGIN, glBegin); |
gluTessCallback (tobj, GLU_VERTEX, glVertex3dv); |
gluTessCallback (tobj, GLU_END, glEnd); |
gluBeginPolygon (tobj); |
for (j=0; j<ncp; j++) { |
gluTessVertex (tobj, point_array[j], point_array[j]); |
} |
gluEndPolygon (tobj); |
gluDeleteTess (tobj); |
#endif /* OPENGL_10 */ |
} |
/* ============================================================ */ |
void draw_angle_style_back_cap (int ncp, /* number of contour points */ |
gleDouble bi[3], /* biscetor */ |
gleDouble point_array[][3]) /* polyline */ |
{ |
int j; |
#ifdef OPENGL_10 |
GLUtriangulatorObj *tobj; |
#endif /* OPENGL_10 */ |
if (bi[2] > 0.0) { |
VEC_SCALE (bi, -1.0, bi); |
} |
#ifdef GL_32 |
/* old-style gl handles concave polygons no problem, so the code is |
* simple. New-style gl is a lot more tricky. */ |
/* draw the end cap */ |
BGNPOLYGON (); |
N3F (bi); |
for (j=ncp-1; j>=0; j--) { |
V3F (point_array[j], j, BACK_CAP); |
} |
ENDPOLYGON (); |
#endif /* GL_32 */ |
#ifdef OPENGL_10 |
N3F (bi); |
tobj = gluNewTess (); |
gluTessCallback (tobj, GLU_BEGIN, glBegin); |
gluTessCallback (tobj, GLU_VERTEX, glVertex3dv); |
gluTessCallback (tobj, GLU_END, glEnd); |
gluBeginPolygon (tobj); |
for (j=ncp-1; j>=0; j--) { |
gluTessVertex (tobj, point_array[j], point_array[j]); |
} |
gluEndPolygon (tobj); |
gluDeleteTess (tobj); |
#endif /* OPENGL_10 */ |
} |
/* ============================================================ */ |
void extrusion_angle_join (int ncp, /* number of contour points */ |
gleDouble contour[][2], /* 2D contour */ |
gleDouble cont_normal[][2], /* 2D normal vecs */ |
gleDouble up[3], /* up vector for contour */ |
int npoints, /* numpoints in poly-line */ |
gleDouble point_array[][3], /* polyline */ |
float color_array[][3], /* color of polyline */ |
gleDouble xform_array[][2][3]) /* 2D contour xforms */ |
{ |
int i, j; |
int inext, inextnext; |
gleDouble m[4][4]; |
gleDouble len; |
gleDouble len_seg; |
gleDouble diff[3]; |
gleDouble bi_0[3], bi_1[3]; /* bisecting plane */ |
gleDouble bisector_0[3], bisector_1[3]; /* bisecting plane */ |
gleDouble end_point_0[3], end_point_1[3]; |
gleDouble origin[3], neg_z[3]; |
gleDouble yup[3]; /* alternate up vector */ |
gleDouble *front_loop, *back_loop; /* contours in 3D */ |
char * mem_anchor; |
double *norm_loop; |
double *front_norm, *back_norm, *tmp; /* contour normals in 3D */ |
int first_time; |
/* By definition, the contour passed in has its up vector pointing in |
* the y direction */ |
if (up == NULL) { |
yup[0] = 0.0; |
yup[1] = 1.0; |
yup[2] = 0.0; |
} else { |
VEC_COPY(yup, up); |
} |
/* ========== "up" vector sanity check ========== */ |
(void) up_sanity_check (yup, npoints, point_array); |
/* the origin is at the origin */ |
origin [0] = 0.0; |
origin [1] = 0.0; |
origin [2] = 0.0; |
/* and neg_z is at neg z */ |
neg_z[0] = 0.0; |
neg_z[1] = 0.0; |
neg_z[2] = 1.0; |
/* ignore all segments of zero length */ |
i = 1; |
inext = i; |
FIND_NON_DEGENERATE_POINT (inext, npoints, len, diff, point_array); |
len_seg = len; /* store for later use */ |
/* get the bisecting plane */ |
bisecting_plane (bi_0, point_array[0], |
point_array[1], |
point_array[inext]); |
/* reflect the up vector in the bisecting plane */ |
VEC_REFLECT (yup, yup, bi_0); |
/* malloc the storage we'll need for relaying changed contours to the |
* drawing routines. */ |
mem_anchor = malloc (2 * 3 * ncp * sizeof(double) |
+ 2 * 3 * ncp * sizeof(gleDouble)); |
front_loop = (gleDouble *) mem_anchor; |
back_loop = front_loop + 3 * ncp; |
front_norm = (double *) (back_loop + 3 * ncp); |
back_norm = front_norm + 3 * ncp; |
norm_loop = front_norm; |
/* may as well get the normals set up now */ |
if (cont_normal != NULL) { |
if (xform_array == NULL) { |
for (j=0; j<ncp; j++) { |
norm_loop[3*j] = cont_normal[j][0]; |
norm_loop[3*j+1] = cont_normal[j][1]; |
norm_loop[3*j+2] = 0.0; |
} |
} else { |
for (j=0; j<ncp; j++) { |
NORM_XFORM_2X2 ( (&front_norm[3*j]), |
xform_array[inext-1], |
cont_normal [j]); |
front_norm[3*j+2] = 0.0; |
back_norm[3*j+2] = 0.0; |
} |
} |
} |
first_time = TRUE; |
/* draw tubing, not doing the first segment */ |
while (inext<npoints-1) { |
inextnext = inext; |
/* ignore all segments of zero length */ |
FIND_NON_DEGENERATE_POINT (inextnext, npoints, len, diff, point_array); |
/* get the next bisecting plane */ |
bisecting_plane (bi_1, point_array[i], |
point_array[inext], |
point_array[inextnext]); |
/* rotate so that z-axis points down v2-v1 axis, |
* and so that origen is at v1 */ |
uviewpoint (m, point_array[i], point_array[inext], yup); |
PUSHMATRIX (); |
MULTMATRIX (m); |
/* rotate the bisecting planes into the local coordinate system */ |
MAT_DOT_VEC_3X3 (bisector_0, m, bi_0); |
MAT_DOT_VEC_3X3 (bisector_1, m, bi_1); |
neg_z[2] = -len_seg; |
/* draw the tube */ |
/* --------- START OF TMESH GENERATION -------------- */ |
for (j=0; j<ncp; j++) { |
/* if there are normals, and there are either affine xforms, OR |
* path-edge normals need to be drawn, then compute local |
* coordinate system normals. |
*/ |
if (cont_normal != NULL) { |
/* set up the back normals. (The front normals we inherit |
* from previous pass through the loop) */ |
if (xform_array != NULL) { |
/* do up the normal vectors with the inverse transpose */ |
NORM_XFORM_2X2 ( (&back_norm[3*j]), |
xform_array[inext], |
cont_normal [j]); |
} |
/* Note that if the xform array is NULL, then normals are |
* constant, and are set up outside of the loop. |
*/ |
/* |
* if there are normal vectors, and the style calls for it, |
* then we want to project the normal vectors into the |
* bisecting plane. (This style is needed to make toroids, etc. |
* look good: Without this, segmentation artifacts show up |
* under lighting. |
*/ |
if (__TUBE_DRAW_PATH_EDGE_NORMALS) { |
/* Hmm, if no affine xforms, then we haven't yet set |
* back vector. So do it. */ |
if (xform_array == NULL) { |
back_norm[3*j] = cont_normal[j][0]; |
back_norm[3*j+1] = cont_normal[j][1]; |
} |
/* now, start with a fresh normal (z component equal to |
* zero), project onto bisecting plane (by computing |
* perpendicular componenet to bisect vector, and renormalize |
* (since projected vector is not of unit length */ |
front_norm[3*j+2] = 0.0; |
VEC_PERP ((&front_norm[3*j]), (&front_norm[3*j]), bisector_0); |
VEC_NORMALIZE ((&front_norm[3*j])); |
back_norm[3*j+2] = 0.0; |
VEC_PERP ((&back_norm[3*j]), (&back_norm[3*j]), bisector_1); |
VEC_NORMALIZE ((&back_norm[3*j])); |
} |
} |
/* Next, we want to define segements. We find the endpoints of |
* the segments by intersecting the contour with the bisecting |
* plane. If there is no local affine transform, this is easy. |
* |
* If there is an affine tranform, then we want to remove the |
* torsional component, so that the intersection points won't |
* get twisted out of shape. We do this by applying the |
* local affine transform to the entire coordinate system. |
*/ |
if (xform_array == NULL) { |
end_point_0 [0] = contour[j][0]; |
end_point_0 [1] = contour[j][1]; |
end_point_1 [0] = contour[j][0]; |
end_point_1 [1] = contour[j][1]; |
} else { |
/* transform the contour points with the local xform */ |
MAT_DOT_VEC_2X3 (end_point_0, |
xform_array[inext-1], contour[j]); |
MAT_DOT_VEC_2X3 (end_point_1, |
xform_array[inext-1], contour[j]); |
} |
end_point_0 [2] = 0.0; |
end_point_1 [2] = - len_seg; |
/* The two end-points define a line. Intersect this line |
* against the clipping plane defined by the PREVIOUS |
* tube segment. */ |
INNERSECT ((&front_loop[3*j]), /* intersection point (returned) */ |
origin, /* point on intersecting plane */ |
bisector_0, /* normal vector to plane */ |
end_point_0, /* point on line */ |
end_point_1); /* another point on the line */ |
/* The two end-points define a line. Intersect this line |
* against the clipping plane defined by the NEXT |
* tube segment. */ |
/* if there's an affine coordinate change, be sure to use it */ |
if (xform_array != NULL) { |
/* transform the contour points with the local xform */ |
MAT_DOT_VEC_2X3 (end_point_0, |
xform_array[inext], contour[j]); |
MAT_DOT_VEC_2X3 (end_point_1, |
xform_array[inext], contour[j]); |
} |
INNERSECT ((&back_loop[3*j]), /* intersection point (returned) */ |
neg_z, /* point on intersecting plane */ |
bisector_1, /* normal vector to plane */ |
end_point_0, /* point on line */ |
end_point_1); /* another point on the line */ |
} |
/* --------- END OF TMESH GENERATION -------------- */ |
/* v^v^v^v^v^v^v^v^v BEGIN END CAPS v^v^v^v^v^v^v^v^v^v^v^v */ |
/* if end caps are required, draw them. But don't draw any |
* but the very first and last caps */ |
if (__TUBE_DRAW_CAP) { |
if (first_time) { |
if (color_array != NULL) C3F (color_array[inext-1]); |
first_time = FALSE; |
draw_angle_style_front_cap (ncp, bisector_0, (gleVector *) front_loop); |
} |
if (inext == npoints-2) { |
if (color_array != NULL) C3F (color_array[inext]); |
draw_angle_style_back_cap (ncp, bisector_1, (gleVector *) back_loop); |
} |
} |
/* v^v^v^v^v^v^v^v^v END END CAPS v^v^v^v^v^v^v^v^v^v^v^v */ |
/* |||||||||||||||||| START SEGMENT DRAW |||||||||||||||||||| */ |
/* There are six different cases we can have for presence and/or |
* absecnce of colors and normals, and for interpretation of |
* normals. The blechy set of nested if statements below |
* branch to each of the six cases */ |
if ((xform_array == NULL) && (!__TUBE_DRAW_PATH_EDGE_NORMALS)) { |
if (color_array == NULL) { |
if (cont_normal == NULL) { |
draw_segment_plain (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, inext, len_seg); |
} else |
if (__TUBE_DRAW_FACET_NORMALS) { |
draw_segment_facet_n (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) norm_loop, inext, len_seg); |
} else { |
draw_segment_edge_n (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) norm_loop, inext, len_seg); |
} |
} else { |
if (cont_normal == NULL) { |
draw_segment_color (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, |
color_array[inext-1], |
color_array[inext], inext, len_seg); |
} else |
if (__TUBE_DRAW_FACET_NORMALS) { |
draw_segment_c_and_facet_n (ncp, |
(gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) norm_loop, |
color_array[inext-1], |
color_array[inext], inext, len_seg); |
} else { |
draw_segment_c_and_edge_n (ncp, |
(gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) norm_loop, |
color_array[inext-1], |
color_array[inext], inext, len_seg); |
} |
} |
} else { |
if (color_array == NULL) { |
if (cont_normal == NULL) { |
draw_segment_plain (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, inext, len_seg); |
} else |
if (__TUBE_DRAW_FACET_NORMALS) { |
draw_binorm_segment_facet_n (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) front_norm, |
(gleVector *) back_norm, |
inext, len_seg); |
} else { |
draw_binorm_segment_edge_n (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) front_norm, |
(gleVector *) back_norm, |
inext, len_seg); |
} |
} else { |
if (cont_normal == NULL) { |
draw_segment_color (ncp, (gleVector *) front_loop, |
(gleVector *) back_loop, |
color_array[inext-1], |
color_array[inext], inext, len_seg); |
} else |
if (__TUBE_DRAW_FACET_NORMALS) { |
draw_binorm_segment_c_and_facet_n (ncp, |
(gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) front_norm, |
(gleVector *) back_norm, |
color_array[inext-1], |
color_array[inext], inext, len_seg); |
} else { |
draw_binorm_segment_c_and_edge_n (ncp, |
(gleVector *) front_loop, |
(gleVector *) back_loop, |
(gleVector *) front_norm, |
(gleVector *) back_norm, |
color_array[inext-1], |
color_array[inext], inext, len_seg); |
} |
} |
} |
/* |||||||||||||||||| END SEGMENT DRAW |||||||||||||||||||| */ |
/* pop this matrix, do the next set */ |
POPMATRIX (); |
/* bump everything to the next vertex */ |
len_seg = len; |
i = inext; |
inext = inextnext; |
VEC_COPY (bi_0, bi_1); |
/* trade norm loops */ |
tmp = front_norm; |
front_norm = back_norm; |
back_norm = tmp; |
/* reflect the up vector in the bisecting plane */ |
VEC_REFLECT (yup, yup, bi_0); |
} |
/* be sure to free it all up */ |
free (mem_anchor); |
} |
/* ============================================================ */ |
Copyright © 2008 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2008-02-08