void draw_segment_color (int ncp, /* number of contour points */ gleDouble front_contour[][3], gleDouble back_contour[][3], float color_last[3], float color_next[3], int inext, double len) { int j; /* draw the tube segment */ BGNTMESH (inext, len); for (j=0; j<ncp; j++) { C3F (color_last); V3F (front_contour[j], j, FRONT); C3F (color_next); V3F (back_contour[j], j, BACK); } if (__TUBE_CLOSE_CONTOUR) { /* connect back up to first point of contour */ C3F (color_last); V3F (front_contour[0], 0, FRONT); C3F (color_next); V3F (back_contour[0], 0, BACK); } ENDTMESH (); }
void draw_binorm_segment_c_and_edge_n (int ncp, /* number of contour points */ double front_contour[][3], double back_contour[][3], double front_norm[][3], double back_norm[][3], gleColor color_last, gleColor color_next, int inext, double len) { int j; /* draw the tube segment */ BGNTMESH (inext, len); for (j=0; j<ncp; j++) { C3F (color_last); N3F_D (front_norm[j]); V3F_D (front_contour[j], j, FRONT); C3F (color_next); N3F_D (back_norm[j]); V3F_D (back_contour[j], j, BACK); } if (__TUBE_CLOSE_CONTOUR) { /* connect back up to first point of contour */ C3F (color_last); N3F_D (front_norm[0]); V3F_D (front_contour[0], 0, FRONT); C3F (color_next); N3F_D (back_norm[0]); V3F_D (back_contour[0], 0, BACK); } ENDTMESH (); }
void draw_binorm_segment_c_and_facet_n (int ncp, double front_contour[][3], double back_contour[][3], double front_norm[][3], double back_norm[][3], float color_last[3], float color_next[3], int inext, double len) { int j; /* draw the tube segment */ BGNTMESH (inext, len); for (j=0; j<ncp-1; j++) { C3F (color_last); N3F_D (front_norm[j]); V3F_D (front_contour[j], j, FRONT); C3F (color_next); N3F_D (back_norm[j]); V3F_D (back_contour[j], j, BACK); C3F (color_last); N3F_D (front_norm[j]); V3F_D (front_contour[j+1], j+1, FRONT); C3F (color_next); N3F_D (back_norm[j]); V3F_D (back_contour[j+1], j+1, BACK); } if (__TUBE_CLOSE_CONTOUR) { /* connect back up to first point of contour */ C3F (color_last); N3F_D (front_norm[ncp-1]); V3F_D (front_contour[ncp-1], ncp-1, FRONT); C3F (color_next); N3F_D (back_norm[ncp-1]); V3F_D (back_contour[ncp-1], ncp-1, BACK); C3F (color_last); N3F_D (front_norm[ncp-1]); V3F_D (front_contour[0], 0, FRONT); C3F (color_next); N3F_D (back_norm[ncp-1]); V3F_D (back_contour[0], 0, BACK); } ENDTMESH (); }
void draw_cut_style_cap_callback (int iloop, double cap[][3], float face_color[3], gleDouble cut_vector[3], gleDouble bisect_vector[3], double norms[][3], int frontwards) { int i; if (face_color != NULL) C3F (face_color); if (frontwards) { /* if lighting is on, specify the endcap normal */ if (cut_vector != NULL) { /* if normal pointing in wrong direction, flip it. */ if (cut_vector[2] < 0.0) { VEC_SCALE (cut_vector, -1.0, cut_vector); } N3F_D (cut_vector); } BGNPOLYGON(); for (i=0; i<iloop; i++) { V3F_D (cap[i], i, FRONT_CAP); } ENDPOLYGON(); } else { /* if lighting is on, specify the endcap normal */ if (cut_vector != NULL) { /* if normal pointing in wrong direction, flip it. */ if (cut_vector[2] > 0.0) { VEC_SCALE (cut_vector, -1.0, cut_vector); } N3F_D (cut_vector); } /* the sense of the loop is reversed for backfacing culling */ BGNPOLYGON(); for (i=iloop-1; i>-1; i--) { V3F_D (cap[i], i, BACK_CAP); } ENDPOLYGON(); } }
/* * This little routine draws the little idd-biddy fillet triangle with * the right color, normal, etc. * * HACK ALERT -- there are two aspects to this routine/interface that * are "unfinished". * 1) the third point of the triangle should get a color thats * interpolated beween the front and back color. The interpolant * is not currently being computed. The error introduced by not * doing this should be tiny and/or non-exitant in almost all * expected uses of this code. * * 2) additional normal vectors should be supplied, and these should * be interpolated to fit. Currently, this is not being done. As * above, the expected error of not doing this should be tiny and/or * non-existant in almost all expected uses of this code. */ static void draw_fillet_triangle_n_norms (gleDouble va[3], gleDouble vb[3], gleDouble vc[3], int face, float front_color[3], float back_color[3], double na[3], double nb[3]) { if (front_color != NULL) C3F (front_color); BGNTMESH (-5, 0.0); if (__TUBE_DRAW_FACET_NORMALS) { N3F_D (na); if (face) { V3F (va, -1, FILLET); V3F (vb, -1, FILLET); } else { V3F (vb, -1, FILLET); V3F (va, -1, FILLET); } V3F (vc, -1, FILLET); } else { if (face) { N3F_D (na); V3F (va, -1, FILLET); N3F_D (nb); V3F (vb, -1, FILLET); } else { N3F_D (nb); V3F (vb, -1, FILLET); N3F_D (na); V3F (va, -1, FILLET); N3F_D (nb); } V3F (vc, -1, FILLET); } ENDTMESH (); }
/* ARGSUSED6 */ static void draw_fillet_triangle_plain (gleDouble va[3], gleDouble vb[3], gleDouble vc[3], int face, float front_color[3], float back_color[3]) { if (front_color != NULL) C3F (front_color); BGNTMESH (-5, 0.0); if (face) { V3F (va, -1, FILLET); V3F (vb, -1, FILLET); } else { V3F (vb, -1, FILLET); V3F (va, -1, FILLET); } V3F (vc, -1, FILLET); ENDTMESH (); }
void extrusion_round_or_cut_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 tube_len, seg_len; gleDouble diff[3]; gleDouble bi_0[3], bi_1[3]; /* bisecting plane */ gleDouble bisector_0[3], bisector_1[3]; /* bisecting plane */ gleDouble cut_0[3], cut_1[3]; /* cutting planes */ gleDouble lcut_0[3], lcut_1[3]; /* cutting planes */ int valid_cut_0, valid_cut_1; /* flag -- cut vector is valid */ gleDouble end_point_0[3], end_point_1[3]; gleDouble torsion_point_0[3], torsion_point_1[3]; gleDouble isect_point[3]; gleDouble origin[3], neg_z[3]; gleDouble yup[3]; /* alternate up vector */ gleDouble *front_cap, *back_cap; /* arrays containing the end caps */ gleDouble *front_loop, *back_loop; /* arrays containing the tube ends */ double *front_norm, *back_norm; /* arrays containing normal vecs */ double *norm_loop=0x0, *tmp; /* normal vectors, cast into 3d from 2d */ int *front_is_trimmed, *back_is_trimmed; /* T or F */ float *front_color, *back_color; /* pointers to segment colors */ gleCapCallback cap_callback = 0x0 ; /* function callback to draw cap */ gleCapCallback tmp_cap_callback = 0x0; /* function callback to draw cap */ int join_style_is_cut; /* TRUE if join style is cut */ double dot; /* partial dot product */ char *mem_anchor; int first_time = TRUE; gleDouble *cut_vec; /* create a local, block scope copy of of the join style. * this will alleviate wasted cycles and register write-backs */ /* choose the right callback, depending on the choosen join style */ if (__TUBE_CUT_JOIN) { join_style_is_cut = TRUE; cap_callback = draw_cut_style_cap_callback; } else { join_style_is_cut = FALSE; cap_callback = draw_round_style_cap_callback; } /* 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; /* malloc the data areas that we'll need to store the end-caps */ mem_anchor = malloc (4 * 3*ncp*sizeof(gleDouble) + 2 * 3*ncp*sizeof(double) + 2 * 1*ncp*sizeof(int)); front_norm = (double *) mem_anchor; back_norm = front_norm + 3*ncp; front_loop = (gleDouble *) (back_norm + 3*ncp); back_loop = front_loop + 3*ncp; front_cap = back_loop + 3*ncp; back_cap = front_cap + 3*ncp; front_is_trimmed = (int *) (back_cap + 3*ncp); back_is_trimmed = front_is_trimmed + ncp; /* ======================================= */ /* |-|-|-|-|-|-|-|-| SET UP FOR FIRST SEGMENT |-|-|-|-|-|-|-| */ /* ignore all segments of zero length */ i = 1; inext = i; FIND_NON_DEGENERATE_POINT (inext, npoints, seg_len, diff, point_array); tube_len = seg_len; /* store for later use */ /* may as well get the normals set up now */ if (cont_normal != NULL) { if (xform_array == NULL) { norm_loop = front_norm; back_norm = norm_loop; 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; } } } else { front_norm = back_norm = norm_loop = NULL; } /* get the bisecting plane */ bisecting_plane (bi_0, point_array[i-1], point_array[i], point_array[inext]); /* compute cutting plane */ CUTTING_PLANE (valid_cut_0, cut_0, point_array[i-1], point_array[i], point_array[inext]); /* reflect the up vector in the bisecting plane */ VEC_REFLECT (yup, yup, bi_0); /* |-|-|-|-|-|-|-|-| START LOOP OVER SEGMENTS |-|-|-|-|-|-|-| */ /* 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, seg_len, diff, point_array); /* get the far bisecting plane */ bisecting_plane (bi_1, point_array[i], point_array[inext], point_array[inextnext]); /* compute cutting plane */ CUTTING_PLANE (valid_cut_1, cut_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 cutting planes into the local coordinate system */ MAT_DOT_VEC_3X3 (lcut_0, m, cut_0); MAT_DOT_VEC_3X3 (lcut_1, m, cut_1); /* 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] = -tube_len; /* draw the tube */ /* --------- START OF TMESH GENERATION -------------- */ for (j=0; j<ncp; j++) { /* set up the endpoints for segment clipping */ if (xform_array == NULL) { VEC_COPY_2 (end_point_0, contour[j]); VEC_COPY_2 (end_point_1, contour[j]); VEC_COPY_2 (torsion_point_0, contour[j]); VEC_COPY_2 (torsion_point_1, contour[j]); } 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 (torsion_point_0, xform_array[inext], contour[j]); MAT_DOT_VEC_2X3 (end_point_1, xform_array[inext], contour[j]); MAT_DOT_VEC_2X3 (torsion_point_1, xform_array[inext-1], contour[j]); /* if there are normals and there are affine xforms, * then compute local coordinate system normals. * Set up the back normals. (The front normals we inherit * from previous pass through the loop). */ if (cont_normal != NULL) { /* do up the normal vectors with the inverse transpose */ NORM_XFORM_2X2 ( (&back_norm[3*j]), xform_array[inext], cont_normal [j]); } } end_point_0 [2] = 0.0; torsion_point_0 [2] = 0.0; end_point_1 [2] = - tube_len; torsion_point_1 [2] = - tube_len; /* The two end-points define a line. Intersect this line * against the clipping plane defined by the PREVIOUS * tube segment. */ /* if this and the last tube are co-linear, don't cut the angle * if you do, a divide by zero will result. This and last tube * are co-linear when the cut vector is of zero length */ if (valid_cut_0 && join_style_is_cut) { INNERSECT (isect_point, /* isect point (returned) */ origin, /* point on intersecting plane */ lcut_0, /* normal vector to plane */ end_point_0, /* point on line */ end_point_1); /* another point on the line */ /* determine whether the raw end of the extrusion would have * been cut, by checking to see if the raw and is on the * far end of the half-plane defined by the cut vector. * If the raw end is not "cut", then it is "trimmed". */ if (lcut_0[2] < 0.0) { VEC_SCALE (lcut_0, -1.0, lcut_0); } dot = lcut_0[0] * end_point_0[0]; dot += lcut_0[1] * end_point_0[1]; VEC_COPY ((&front_loop[3*j]), isect_point); } else { /* actual value of dot not interseting; need * only be positive so that if test below failes */ dot = 1.0; VEC_COPY ((&front_loop[3*j]), end_point_0); } INNERSECT (isect_point, /* intersection point (returned) */ origin, /* point on intersecting plane */ bisector_0, /* normal vector to plane */ end_point_0, /* point on line */ torsion_point_1); /* another point on the line */ /* trim out interior of intersecting tube */ /* ... but save the untrimmed version for drawing the endcaps */ /* ... note that cap contains valid data ONLY when is_trimmed * is TRUE. */ if ((dot <= 0.0) || (isect_point[2] < front_loop[3*j+2])) { /* if ((dot <= 0.0) || (front_loop[3*j+2] > 0.0)) { */ VEC_COPY ((&front_cap[3*j]), (&front_loop [3*j])); VEC_COPY ((&front_loop[3*j]), isect_point); front_is_trimmed[j] = TRUE; } else { front_is_trimmed[j] = FALSE; } /* if intersection is behind the end of the segment, * truncate to the end of the segment * Note that coding front_loop [3*j+2] = -tube_len; * doesn't work when twists are involved, */ if (front_loop[3*j+2] < -tube_len) { VEC_COPY( (&front_loop[3*j]), end_point_1); } /* --------------------------------------------------- */ /* The two end-points define a line. We did one endpoint * above. Now do the other.Intersect this line * against the clipping plane defined by the NEXT * tube segment. */ /* if this and the last tube are co-linear, don't cut the angle * if you do, a divide by zero will result. This and last tube * are co-linear when the cut vector is of zero length */ if (valid_cut_1 && join_style_is_cut) { INNERSECT (isect_point, /* isect point (returned) */ neg_z, /* point on intersecting plane */ lcut_1, /* normal vector to plane */ end_point_1, /* point on line */ end_point_0); /* another point on the line */ if (lcut_1[2] > 0.0) { VEC_SCALE (lcut_1, -1.0, lcut_1); } dot = lcut_1[0] * end_point_1[0]; dot += lcut_1[1] * end_point_1[1]; VEC_COPY ((&back_loop[3*j]), isect_point); } else { /* actual value of dot not interseting; need * only be positive so that if test below failes */ dot = 1.0; VEC_COPY ((&back_loop[3*j]), end_point_1); } INNERSECT (isect_point, /* intersection point (returned) */ neg_z, /* point on intersecting plane */ bisector_1, /* normal vector to plane */ torsion_point_0, /* point on line */ end_point_1); /* another point on the line */ /* cut out interior of intersecting tube */ /* ... but save the uncut version for drawing the endcaps */ /* ... note that cap contains valid data ONLY when is *_trimmed is TRUE. */ /* if ((dot <= 0.0) || (back_loop[3*j+2] < -tube_len)) { */ if ((dot <= 0.0) || (isect_point[2] > back_loop[3*j+2])) { VEC_COPY ((&back_cap[3*j]), (&back_loop [3*j])); VEC_COPY ((&back_loop[3*j]), isect_point); back_is_trimmed[j] = TRUE; } else { back_is_trimmed[j] = FALSE; } /* if intersection is behind the end of the segment, * truncate to the end of the segment * Note that coding back_loop [3*j+2] = 0.0; * doesn't work when twists are involved, */ if (back_loop[3*j+2] > 0.0) { VEC_COPY( (&back_loop[3*j]), end_point_0); } } /* --------- END OF TMESH GENERATION -------------- */ /* |||||||||||||||||| 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) { if (color_array == NULL) { if (cont_normal == NULL) { draw_segment_plain (ncp, (gleVector *) front_loop, (gleVector *) back_loop, inext, seg_len); } else if (__TUBE_DRAW_FACET_NORMALS) { draw_segment_facet_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop, inext, seg_len); } else { draw_segment_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop, inext, seg_len); } } else { if (cont_normal == NULL) { draw_segment_color (ncp, (gleVector *) front_loop, (gleVector *) back_loop, color_array[inext-1], color_array[inext], inext, seg_len); } 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, seg_len); } 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, seg_len); } } } else { if (color_array == NULL) { if (cont_normal == NULL) { draw_segment_plain (ncp, (gleVector *) front_loop, (gleVector *) back_loop, inext, seg_len); } 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, seg_len); } else { draw_binorm_segment_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) front_norm, (gleVector *) back_norm, inext, seg_len); } } else { if (cont_normal == NULL) { draw_segment_color (ncp, (gleVector *) front_loop, (gleVector *) back_loop, color_array[inext-1], color_array[inext], inext, seg_len); } 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, seg_len); } 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, seg_len); } } } /* |||||||||||||||||| END SEGMENT DRAW |||||||||||||||||||| */ /* 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 (first_time) { first_time = FALSE; tmp_cap_callback = cap_callback; cap_callback = null_cap_callback; if (__TUBE_DRAW_CAP) { if (color_array != NULL) C3F (color_array[inext-1]); draw_angle_style_front_cap (ncp, bisector_0, (gleDouble (*)[3]) front_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 */ /* $$$$$$$$$$$$$$$$ BEGIN -1, FILLET & JOIN DRAW $$$$$$$$$$$$$$$$$ */ /* * Now, draw the fillet triangles, and the join-caps. */ if (color_array != NULL) { front_color = color_array[inext-1]; back_color = color_array[inext]; } else { front_color = NULL; back_color = NULL; } if (cont_normal == NULL) { /* the flag valid-cut is true if the cut vector has a valid * value (i.e. if a degenerate case has not occured). */ if (valid_cut_0) { cut_vec = lcut_0; } else { cut_vec = NULL; } draw_fillets_and_join_plain (ncp, (gleVector *) front_loop, (gleVector *) front_cap, front_is_trimmed, origin, bisector_0, front_color, back_color, cut_vec, TRUE, cap_callback); /* 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 (inext == npoints-2) { if (__TUBE_DRAW_CAP) { if (color_array != NULL) C3F (color_array[inext]); draw_angle_style_back_cap (ncp, bisector_1, (gleDouble (*)[3]) back_loop); cap_callback = null_cap_callback; } } else { /* restore ability to draw cap */ cap_callback = tmp_cap_callback; } /* v^v^v^v^v^v^v^v^v END END CAPS v^v^v^v^v^v^v^v^v^v^v^v */ /* the flag valid-cut is true if the cut vector has a valid * value (i.e. if a degenerate case has not occured). */ if (valid_cut_1) { cut_vec = lcut_1; } else { cut_vec = NULL; } draw_fillets_and_join_plain (ncp, (gleVector *) back_loop, (gleVector *) back_cap, back_is_trimmed, neg_z, bisector_1, back_color, front_color, cut_vec, FALSE, cap_callback); } else { /* the flag valid-cut is true if the cut vector has a valid * value (i.e. if a degenerate case has not occured). */ if (valid_cut_0) { cut_vec = lcut_0; } else { cut_vec = NULL; } draw_fillets_and_join_n_norms (ncp, (gleVector *) front_loop, (gleVector *) front_cap, front_is_trimmed, origin, bisector_0, (gleVector *) front_norm, front_color, back_color, cut_vec, TRUE, cap_callback); /* 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 (inext == npoints-2) { if (__TUBE_DRAW_CAP) { if (color_array != NULL) C3F (color_array[inext]); draw_angle_style_back_cap (ncp, bisector_1, (gleDouble (*)[3]) back_loop); cap_callback = null_cap_callback; } } else { /* restore ability to draw cap */ cap_callback = tmp_cap_callback; } /* v^v^v^v^v^v^v^v^v END END CAPS v^v^v^v^v^v^v^v^v^v^v^v */ /* the flag valid-cut is true if the cut vector has a valid * value (i.e. if a degenerate case has not occured). */ if (valid_cut_1) { cut_vec = lcut_1; } else { cut_vec = NULL; } draw_fillets_and_join_n_norms (ncp, (gleVector *) back_loop, (gleVector *) back_cap, back_is_trimmed, neg_z, bisector_1, (gleVector *) back_norm, back_color, front_color, cut_vec, FALSE, cap_callback); } /* $$$$$$$$$$$$$$$$ END FILLET & JOIN DRAW $$$$$$$$$$$$$$$$$ */ /* pop this matrix, do the next set */ POPMATRIX (); /* slosh stuff over to next vertex */ tmp = front_norm; front_norm = back_norm; back_norm = tmp; tube_len = seg_len; i = inext; inext = inextnext; VEC_COPY (bi_0, bi_1); VEC_COPY (cut_0, cut_1); valid_cut_0 = valid_cut_1; /* reflect the up vector in the bisecting plane */ VEC_REFLECT (yup, yup, bi_0); } /* |-|-|-|-|-|-|-|-| END LOOP OVER SEGMENTS |-|-|-|-|-|-|-| */ free (mem_anchor); }
static void draw_cut_style_cap_callback (int iloop, double cap[][3], float face_color[3], gleDouble cut_vector[3], gleDouble bisect_vector[3], double norms[][3], int frontwards) { #ifdef DELICATE_TESSELATOR int i; int is_colinear; double *previous_vertex = 0x0; double *first_vertex = 0x0; #endif /* DELICATE_TESSELATOR */ #ifdef OPENGL_10 GLUtriangulatorObj *tobj; tobj = gluNewTess (); gluTessCallback (tobj, GLU_BEGIN, glBegin); gluTessCallback (tobj, GLU_VERTEX, glVertex3dv); gluTessCallback (tobj, GLU_END, glEnd); #endif /* OPENGL_10 */ if (face_color != NULL) C3F (face_color); if (frontwards) { /* if lighting is on, specify the endcap normal */ if (cut_vector != NULL) { /* if normal pointing in wrong direction, flip it. */ if (cut_vector[2] < 0.0) { VEC_SCALE (cut_vector, -1.0, cut_vector); } N3F_D (cut_vector); } #ifdef GL_32 BGNPOLYGON(); for (i=0; i<iloop; i++) { V3F_D (cap[i], i, FRONT_CAP); } ENDPOLYGON(); #endif /* GL_32 */ #ifdef OPENGL_10 /* If you have a tesselator that is happy with anything, * including degenerate points, colinear segments, etc. * then define this. Otherwise, pick one of the others. * * I beleive that the stock SGI tesselator is "lenient", * despite explicit disclaimers in the documentation. * (circa 1995). * * The Mesa tesselator is not at all forgiving of * degenerate points. * (circa 1997-1998) */ #ifdef LENIENT_TESSELATOR gluBeginPolygon (tobj); for (i=0; i<iloop; i++) { gluTessVertex (tobj, cap[i], cap[i]); } gluEndPolygon (tobj); #endif /* LENIENT_TESSELATOR */ #ifdef DELICATE_TESSELATOR gluBeginPolygon (tobj); first_vertex = 0x0; previous_vertex = cap[iloop-1]; for (i=0; i<iloop-1; i++) { COLINEAR (is_colinear, previous_vertex, cap[i], cap[i+1]); if (!is_colinear) { gluTessVertex (tobj, cap[i], cap[i]); previous_vertex = cap[i]; if (!first_vertex) first_vertex = previous_vertex; } } if (!first_vertex) first_vertex = cap[0]; COLINEAR (is_colinear, previous_vertex, cap[iloop-1], first_vertex); if (!is_colinear) gluTessVertex (tobj, cap[iloop-1], cap[iloop-1]); gluEndPolygon (tobj); #endif /* DELICATE_TESSELATOR */ #endif /* OPENGL_10 */ } else { /* if lighting is on, specify the endcap normal */ if (cut_vector != NULL) { /* if normal pointing in wrong direction, flip it. */ if (cut_vector[2] > 0.0) { VEC_SCALE (cut_vector, -1.0, cut_vector); } N3F_D (cut_vector); } /* the sense of the loop is reversed for backfacing culling */ #ifdef GL_32 BGNPOLYGON(); for (i=iloop-1; i>-1; i--) { V3F_D (cap[i], i, BACK_CAP); } ENDPOLYGON(); #endif /* GL_32 */ #ifdef OPENGL_10 #ifdef LENIENT_TESSELATOR gluBeginPolygon (tobj); for (i=iloop-1; i>-1; i--) { gluTessVertex (tobj, cap[i], cap[i]); } gluEndPolygon (tobj); #endif /* LENIENT_TESSELATOR */ #ifdef DELICATE_TESSELATOR gluBeginPolygon (tobj); first_vertex = 0x0; previous_vertex = cap[0]; for (i=iloop-1; i>0; i--) { COLINEAR (is_colinear, previous_vertex, cap[i], cap[i-1]); if (!is_colinear) { gluTessVertex (tobj, cap[i], cap[i]); previous_vertex = cap[i]; if (!first_vertex) first_vertex = previous_vertex; } } if (!first_vertex) first_vertex = cap[iloop-1]; COLINEAR (is_colinear, previous_vertex, cap[0], first_vertex); if (!is_colinear) gluTessVertex (tobj, cap[0], cap[0]); gluEndPolygon (tobj); #endif /* DELICATE_TESSELATOR */ #endif /* OPENGL_10 */ } #ifdef OPENGL_10 gluDeleteTess (tobj); #endif /* OPENGL_10 */ }
void draw_round_style_cap_callback (int ncp, double cap[][3], float face_color[3], gleDouble cut[3], gleDouble bi[3], double norms[][3], int frontwards) { double axis[3]; double xycut[3]; double theta; double *last_contour, *next_contour; double *last_norm, *next_norm; double *cap_z; double *tmp; char *malloced_area; int i, j, k; double m[4][4]; if (face_color != NULL) C3F (face_color); /* ------------ start setting up rotation matrix ------------- */ /* if the cut vector is NULL (this should only occur in * a degenerate case), then we can't draw anything. return. */ if (cut == NULL) return; /* make sure that the cut vector points inwards */ if (cut[2] > 0.0) { VEC_SCALE (cut, -1.0, cut); } /* make sure that the bi vector points outwards */ if (bi[2] < 0.0) { VEC_SCALE (bi, -1.0, bi); } /* determine the axis we are to rotate about to get bi-contour. * Note that the axis will always lie in the x-y plane */ VEC_CROSS_PRODUCT (axis, cut, bi); /* reverse the cut vector for the back cap -- * need to do this to get angle right */ if (!frontwards) { VEC_SCALE (cut, -1.0, cut); } /* get angle to rotate by -- arccos of dot product of cut with cut * projected into the x-y plane */ xycut [0] = 0.0; xycut [1] = 0.0; xycut [2] = 1.0; VEC_PERP (xycut, cut, xycut); VEC_NORMALIZE (xycut); VEC_DOT_PRODUCT (theta, xycut, cut); theta = acos (theta); /* we'll tesselate round joins into a number of teeny pieces */ theta /= (double) __ROUND_TESS_PIECES; /* get the matrix */ urot_axis_d (m, theta, axis); /* ------------ done setting up rotation matrix ------------- */ /* This malloc is a fancy version of: * last_contour = (double *) malloc (3*ncp*sizeof(double); * next_contour = (double *) malloc (3*ncp*sizeof(double); */ malloced_area = malloc ((4*3+1) *ncp*sizeof (double)); last_contour = (double *) malloced_area; next_contour = last_contour + 3*ncp; cap_z = next_contour + 3*ncp; last_norm = cap_z + ncp; next_norm = last_norm + 3*ncp; /* make first copy of contour */ if (frontwards) { for (j=0; j<ncp; j++) { last_contour[3*j] = cap[j][0]; last_contour[3*j+1] = cap[j][1]; last_contour[3*j+2] = cap_z[j] = cap[j][2]; } if (norms != NULL) { for (j=0; j<ncp; j++) { VEC_COPY ((&last_norm[3*j]), norms[j]); } } } else { /* in order for backfacing polygon removal to work correctly, have * to have the sense in which the joins are drawn to be reversed * for the back cap. This can be done by reversing the order of * the contour points. Normals are a bit trickier, since the * reversal is off-by-one for facet normals as compared to edge * normals. */ for (j=0; j<ncp; j++) { k = ncp - j - 1; last_contour[3*k] = cap[j][0]; last_contour[3*k+1] = cap[j][1]; last_contour[3*k+2] = cap_z[k] = cap[j][2]; } if (norms != NULL) { if (__TUBE_DRAW_FACET_NORMALS) { for (j=0; j<ncp-1; j++) { k = ncp - j - 2; VEC_COPY ((&last_norm[3*k]), norms[j]); } } else { for (j=0; j<ncp; j++) { k = ncp - j - 1; VEC_COPY ((&last_norm[3*k]), norms[j]); } } } } /* &&&&&&&&&&&&&& start drawing cap &&&&&&&&&&&&& */ for (i=0; i<__ROUND_TESS_PIECES; i++) { for (j=0; j<ncp; j++) { next_contour [3*j+2] -= cap_z[j]; last_contour [3*j+2] -= cap_z[j]; MAT_DOT_VEC_3X3 ( (&next_contour[3*j]), m, (&last_contour[3*j])); next_contour [3*j+2] += cap_z[j]; last_contour [3*j+2] += cap_z[j]; } if (norms != NULL) { for (j=0; j<ncp; j++) { MAT_DOT_VEC_3X3 ( (&next_norm[3*j]), m, (&last_norm[3*j])); } } /* OK, now render it all */ if (norms == NULL) { draw_segment_plain (ncp, (gleVector *) next_contour, (gleVector *) last_contour, 0, 0.0); } else if (__TUBE_DRAW_FACET_NORMALS) { draw_binorm_segment_facet_n (ncp, (gleVector *) next_contour, (gleVector *) last_contour, (gleVector *) next_norm, (gleVector *) last_norm, 0, 0.0); } else { draw_binorm_segment_edge_n (ncp, (gleVector *) next_contour, (gleVector *) last_contour, (gleVector *) next_norm, (gleVector *) last_norm, 0, 0.0); } /* swap contours */ tmp = next_contour; next_contour = last_contour; last_contour = tmp; tmp = next_norm; next_norm = last_norm; last_norm = tmp; } /* &&&&&&&&&&&&&& end drawing cap &&&&&&&&&&&&& */ /* Thou shalt not leak memory */ free (malloced_area); }
/* ARGSUSED4 */ static void draw_cut_style_cap_callback (int iloop, double cap[][3], float face_color[3], gleDouble cut_vector[3], gleDouble bisect_vector[3], double norms[][3], int frontwards) { int i; #ifdef OPENGL_10 GLUtriangulatorObj *tobj; tobj = gluNewTess (); gluTessCallback (tobj, GLU_BEGIN, (void (CALLBACK*)()) glBegin); gluTessCallback (tobj, GLU_VERTEX, (void (CALLBACK*)()) glVertex3dv); gluTessCallback (tobj, GLU_END, (void (CALLBACK*)()) glEnd); #endif /* OPENGL_10 */ if (face_color != NULL) C3F (face_color); if (frontwards) { /* if lighting is on, specify the endcap normal */ if (cut_vector != NULL) { /* if normal pointing in wrong direction, flip it. */ if (cut_vector[2] < 0.0) { VEC_SCALE (cut_vector, -1.0, cut_vector); } N3F_D (cut_vector); } #ifdef GL_32 BGNPOLYGON(); for (i=0; i<iloop; i++) { V3F_D (cap[i], i, FRONT_CAP); } ENDPOLYGON(); #endif /* GL_32 */ #ifdef OPENGL_10 gluBeginPolygon (tobj); for (i=0; i<iloop; i++) { gluTessVertex (tobj, cap[i], cap[i]); } gluEndPolygon (tobj); #endif /* OPENGL_10 */ } else { /* if lighting is on, specify the endcap normal */ if (cut_vector != NULL) { /* if normal pointing in wrong direction, flip it. */ if (cut_vector[2] > 0.0) { VEC_SCALE (cut_vector, -1.0, cut_vector); } N3F_D (cut_vector); } /* the sense of the loop is reversed for backfacing culling */ #ifdef GL_32 BGNPOLYGON(); for (i=iloop-1; i>-1; i--) { V3F_D (cap[i], i, BACK_CAP); } ENDPOLYGON(); #endif /* GL_32 */ #ifdef OPENGL_10 gluBeginPolygon (tobj); for (i=iloop-1; i>-1; i--) { gluTessVertex (tobj, cap[i], cap[i]); } gluEndPolygon (tobj); #endif /* OPENGL_10 */ } #ifdef OPENGL_10 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); }
void draw_segment_c_and_facet_n (int ncp, /* number of contour points */ gleDouble front_contour[][3], gleDouble back_contour[][3], double norm_cont[][3], float color_last[3], float color_next[3], int inext, double len) { int j; /* Note about this code: * At first, when looking at this code, it appears to be really dumb: * the N3F() call appears to be repeated multiple times, for no * apparent purpose. It would seem that a performance improvement * would be gained by stripping it out. !DONT DO IT! * When there are no local lights or viewers, the V3F() subroutine * does not trigger a recalculation of the lighting equations. * However, we MUST trigger lighting, since otherwise colors come out * wrong. Trigger lighting by doing an N3F call. */ /* draw the tube segment */ BGNTMESH (inext, len); for (j=0; j<ncp-1; j++) { C3F (color_last); N3F_D (norm_cont[j]); V3F (front_contour[j], j, FRONT); C3F (color_next); N3F_D (norm_cont[j]); V3F (back_contour[j], j, BACK); C3F (color_last); N3F_D (norm_cont[j]); V3F (front_contour[j+1], j+1, FRONT); C3F (color_next); N3F_D (norm_cont[j]); V3F (back_contour[j+1], j+1, BACK); } if (__TUBE_CLOSE_CONTOUR) { /* connect back up to first point of contour */ C3F (color_last); N3F_D (norm_cont[ncp-1]); V3F (front_contour[ncp-1], ncp-1, FRONT); C3F (color_next); N3F_D (norm_cont[ncp-1]); V3F (back_contour[ncp-1], ncp-1, BACK); C3F (color_last); N3F_D (norm_cont[ncp-1]); V3F (front_contour[0], 0, FRONT); C3F (color_next); N3F_D (norm_cont[ncp-1]); V3F (back_contour[0], 0, BACK); } ENDTMESH (); }