/* * Mesa 3-D graphics library * Version: 3.3 * Copyright (C) 1995-2000 Brian Paul * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * NURBS implementation written by Bogdan Sikorski (bogdan@cira.it) * See README2 for more info. */ #ifdef PC_HEADER #include "all.h" #else #include #include #include "gluP.h" #include "nurbs.h" #endif GLenum test_knot(GLint nknots, GLfloat * knot, GLint order) { GLsizei i; GLint knot_mult; GLfloat tmp_knot; tmp_knot = knot[0]; knot_mult = 1; for (i = 1; i < nknots; i++) { if (knot[i] < tmp_knot) return GLU_NURBS_ERROR4; if (fabs(tmp_knot - knot[i]) > EPSILON) { if (knot_mult > order) return GLU_NURBS_ERROR5; knot_mult = 1; tmp_knot = knot[i]; } else ++knot_mult; } return GLU_NO_ERROR; } static int /* qsort function */ #if defined(WIN32) && !defined(OPENSTEP) __cdecl #endif knot_sort(const void *a, const void *b) { GLfloat x, y; x = *((GLfloat *) a); y = *((GLfloat *) b); if (fabs(x - y) < EPSILON) return 0; if (x > y) return 1; return -1; } /* insert into dest knot all values within the valid range from src knot */ /* that do not appear in dest */ void collect_unified_knot(knot_str_type * dest, knot_str_type * src, GLfloat maximal_min_knot, GLfloat minimal_max_knot) { GLfloat *src_knot, *dest_knot; GLint src_t_min, src_t_max, dest_t_min, dest_t_max; GLint src_nknots, dest_nknots; GLint i, j, k, new_cnt; GLboolean not_found_flag; src_knot = src->unified_knot; dest_knot = dest->unified_knot; src_t_min = src->t_min; src_t_max = src->t_max; dest_t_min = dest->t_min; dest_t_max = dest->t_max; src_nknots = src->unified_nknots; dest_nknots = dest->unified_nknots; k = new_cnt = dest_nknots; for (i = src_t_min; i <= src_t_max; i++) if (src_knot[i] - maximal_min_knot > -EPSILON && src_knot[i] - minimal_max_knot < EPSILON) { not_found_flag = GL_TRUE; for (j = dest_t_min; j <= dest_t_max; j++) if (fabs(dest_knot[j] - src_knot[i]) < EPSILON) { not_found_flag = GL_FALSE; break; } if (not_found_flag) { /* knot from src is not in dest - add this knot to dest */ dest_knot[k++] = src_knot[i]; ++new_cnt; ++(dest->t_max); /* the valid range widens */ ++(dest->delta_nknots); /* increment the extra knot value counter */ } } dest->unified_nknots = new_cnt; qsort((void *) dest_knot, (size_t) new_cnt, (size_t) sizeof(GLfloat), &knot_sort); } /* basing on the new common knot range for all attributes set */ /* t_min and t_max values for each knot - they will be used later on */ /* by explode_knot() and calc_new_ctrl_pts */ static void set_new_t_min_t_max(knot_str_type * geom_knot, knot_str_type * color_knot, knot_str_type * normal_knot, knot_str_type * texture_knot, GLfloat maximal_min_knot, GLfloat minimal_max_knot) { GLuint t_min = 0, t_max = 0, cnt = 0; if (minimal_max_knot - maximal_min_knot < EPSILON) { /* knot common range empty */ geom_knot->t_min = geom_knot->t_max = 0; color_knot->t_min = color_knot->t_max = 0; normal_knot->t_min = normal_knot->t_max = 0; texture_knot->t_min = texture_knot->t_max = 0; } else { if (geom_knot->unified_knot != NULL) { cnt = geom_knot->unified_nknots; for (t_min = 0; t_min < cnt; t_min++) if (fabs((geom_knot->unified_knot)[t_min] - maximal_min_knot) < EPSILON) break; for (t_max = cnt - 1; t_max; t_max--) if (fabs((geom_knot->unified_knot)[t_max] - minimal_max_knot) < EPSILON) break; } else if (geom_knot->nknots) { cnt = geom_knot->nknots; for (t_min = 0; t_min < cnt; t_min++) if (fabs((geom_knot->knot)[t_min] - maximal_min_knot) < EPSILON) break; for (t_max = cnt - 1; t_max; t_max--) if (fabs((geom_knot->knot)[t_max] - minimal_max_knot) < EPSILON) break; } geom_knot->t_min = t_min; geom_knot->t_max = t_max; if (color_knot->unified_knot != NULL) { cnt = color_knot->unified_nknots; for (t_min = 0; t_min < cnt; t_min++) if (fabs((color_knot->unified_knot)[t_min] - maximal_min_knot) < EPSILON) break; for (t_max = cnt - 1; t_max; t_max--) if (fabs((color_knot->unified_knot)[t_max] - minimal_max_knot) < EPSILON) break; color_knot->t_min = t_min; color_knot->t_max = t_max; } if (normal_knot->unified_knot != NULL) { cnt = normal_knot->unified_nknots; for (t_min = 0; t_min < cnt; t_min++) if (fabs((normal_knot->unified_knot)[t_min] - maximal_min_knot) < EPSILON) break; for (t_max = cnt - 1; t_max; t_max--) if (fabs((normal_knot->unified_knot)[t_max] - minimal_max_knot) < EPSILON) break; normal_knot->t_min = t_min; normal_knot->t_max = t_max; } if (texture_knot->unified_knot != NULL) { cnt = texture_knot->unified_nknots; for (t_min = 0; t_min < cnt; t_min++) if (fabs((texture_knot->unified_knot)[t_min] - maximal_min_knot) < EPSILON) break; for (t_max = cnt - 1; t_max; t_max--) if (fabs((texture_knot->unified_knot)[t_max] - minimal_max_knot) < EPSILON) break; texture_knot->t_min = t_min; texture_knot->t_max = t_max; } } } /* modify all knot valid ranges in such a way that all have the same */ /* range, common to all knots */ /* do this by knot insertion */ GLenum select_knot_working_range(GLUnurbsObj * nobj, knot_str_type * geom_knot, knot_str_type * color_knot, knot_str_type * normal_knot, knot_str_type * texture_knot) { GLint max_nknots; GLfloat maximal_min_knot, minimal_max_knot; GLint i; /* find the maximum modified knot length */ max_nknots = geom_knot->nknots; if (color_knot->unified_knot) max_nknots += color_knot->nknots; if (normal_knot->unified_knot) max_nknots += normal_knot->nknots; if (texture_knot->unified_knot) max_nknots += texture_knot->nknots; maximal_min_knot = (geom_knot->knot)[geom_knot->t_min]; minimal_max_knot = (geom_knot->knot)[geom_knot->t_max]; /* any attirb data ? */ if (max_nknots != geom_knot->nknots) { /* allocate space for the unified knots */ if ((geom_knot->unified_knot = (GLfloat *) malloc(sizeof(GLfloat) * max_nknots)) == NULL) { call_user_error(nobj, GLU_OUT_OF_MEMORY); return GLU_ERROR; } /* copy the original knot to the unified one */ geom_knot->unified_nknots = geom_knot->nknots; for (i = 0; i < geom_knot->nknots; i++) (geom_knot->unified_knot)[i] = (geom_knot->knot)[i]; if (color_knot->unified_knot) { if ((color_knot->knot)[color_knot->t_min] - maximal_min_knot > EPSILON) maximal_min_knot = (color_knot->knot)[color_knot->t_min]; if (minimal_max_knot - (color_knot->knot)[color_knot->t_max] > EPSILON) minimal_max_knot = (color_knot->knot)[color_knot->t_max]; if ((color_knot->unified_knot = (GLfloat *) malloc(sizeof(GLfloat) * max_nknots)) == NULL) { free(geom_knot->unified_knot); call_user_error(nobj, GLU_OUT_OF_MEMORY); return GLU_ERROR; } /* copy the original knot to the unified one */ color_knot->unified_nknots = color_knot->nknots; for (i = 0; i < color_knot->nknots; i++) (color_knot->unified_knot)[i] = (color_knot->knot)[i]; } if (normal_knot->unified_knot) { if ((normal_knot->knot)[normal_knot->t_min] - maximal_min_knot > EPSILON) maximal_min_knot = (normal_knot->knot)[normal_knot->t_min]; if (minimal_max_knot - (normal_knot->knot)[normal_knot->t_max] > EPSILON) minimal_max_knot = (normal_knot->knot)[normal_knot->t_max]; if ((normal_knot->unified_knot = (GLfloat *) malloc(sizeof(GLfloat) * max_nknots)) == NULL) { free(geom_knot->unified_knot); free(color_knot->unified_knot); call_user_error(nobj, GLU_OUT_OF_MEMORY); return GLU_ERROR; } /* copy the original knot to the unified one */ normal_knot->unified_nknots = normal_knot->nknots; for (i = 0; i < normal_knot->nknots; i++) (normal_knot->unified_knot)[i] = (normal_knot->knot)[i]; } if (texture_knot->unified_knot) { if ((texture_knot->knot)[texture_knot->t_min] - maximal_min_knot > EPSILON) maximal_min_knot = (texture_knot->knot)[texture_knot->t_min]; if (minimal_max_knot - (texture_knot->knot)[texture_knot->t_max] > EPSILON) minimal_max_knot = (texture_knot->knot)[texture_knot->t_max]; if ((texture_knot->unified_knot = (GLfloat *) malloc(sizeof(GLfloat) * max_nknots)) == NULL) { free(geom_knot->unified_knot); free(color_knot->unified_knot); free(normal_knot->unified_knot); call_user_error(nobj, GLU_OUT_OF_MEMORY); return GLU_ERROR; } /* copy the original knot to the unified one */ texture_knot->unified_nknots = texture_knot->nknots; for (i = 0; i < texture_knot->nknots; i++) (texture_knot->unified_knot)[i] = (texture_knot->knot)[i]; } /* work on the geometry knot with all additional knot values */ /* appearing in attirbutive knots */ if (minimal_max_knot - maximal_min_knot < EPSILON) { /* empty working range */ geom_knot->unified_nknots = 0; color_knot->unified_nknots = 0; normal_knot->unified_nknots = 0; texture_knot->unified_nknots = 0; } else { if (color_knot->unified_knot) collect_unified_knot(geom_knot, color_knot, maximal_min_knot, minimal_max_knot); if (normal_knot->unified_knot) collect_unified_knot(geom_knot, normal_knot, maximal_min_knot, minimal_max_knot); if (texture_knot->unified_knot) collect_unified_knot(geom_knot, texture_knot, maximal_min_knot, minimal_max_knot); /* since we have now built the "unified" geometry knot */ /* add same knot values to all attributive knots */ if (color_knot->unified_knot) collect_unified_knot(color_knot, geom_knot, maximal_min_knot, minimal_max_knot); if (normal_knot->unified_knot) collect_unified_knot(normal_knot, geom_knot, maximal_min_knot, minimal_max_knot); if (texture_knot->unified_knot) collect_unified_knot(texture_knot, geom_knot, maximal_min_knot, minimal_max_knot); } } set_new_t_min_t_max(geom_knot, color_knot, normal_knot, texture_knot, maximal_min_knot, minimal_max_knot); return GLU_NO_ERROR; } void free_unified_knots(knot_str_type * geom_knot, knot_str_type * color_knot, knot_str_type * normal_knot, knot_str_type * texture_knot) { if (geom_knot->unified_knot) free(geom_knot->unified_knot); if (color_knot->unified_knot) free(color_knot->unified_knot); if (normal_knot->unified_knot) free(normal_knot->unified_knot); if (texture_knot->unified_knot) free(texture_knot->unified_knot); } GLenum explode_knot(knot_str_type * the_knot) { GLfloat *knot, *new_knot; GLint nknots, n_new_knots = 0; GLint t_min, t_max; GLint ord; GLsizei i, j, k; GLfloat tmp_float; if (the_knot->unified_knot) { knot = the_knot->unified_knot; nknots = the_knot->unified_nknots; } else { knot = the_knot->knot; nknots = the_knot->nknots; } ord = the_knot->order; t_min = the_knot->t_min; t_max = the_knot->t_max; for (i = t_min; i <= t_max;) { tmp_float = knot[i]; for (j = 0; j < ord && (i + j) <= t_max; j++) if (fabs(tmp_float - knot[i + j]) > EPSILON) break; n_new_knots += ord - j; i += j; } /* alloc space for new_knot */ if ( (new_knot = (GLfloat *) malloc(sizeof(GLfloat) * (nknots + n_new_knots + 1))) == NULL) { return GLU_OUT_OF_MEMORY; } /* fill in new knot */ for (j = 0; j < t_min; j++) new_knot[j] = knot[j]; for (i = j; i <= t_max; i++) { tmp_float = knot[i]; for (k = 0; k < ord; k++) { new_knot[j++] = knot[i]; if (tmp_float == knot[i + 1]) i++; } } for (i = t_max + 1; i < (int) nknots; i++) new_knot[j++] = knot[i]; /* fill in the knot structure */ the_knot->new_knot = new_knot; the_knot->delta_nknots += n_new_knots; the_knot->t_max += n_new_knots; return GLU_NO_ERROR; } GLenum calc_alphas(knot_str_type * the_knot) { GLfloat tmp_float; int i, j, k, m, n; int order; GLfloat *alpha, *alpha_new, *tmp_alpha; GLfloat denom; GLfloat *knot, *new_knot; knot = the_knot->knot; order = the_knot->order; new_knot = the_knot->new_knot; n = the_knot->nknots - the_knot->order; m = n + the_knot->delta_nknots; if ((alpha = (GLfloat *) malloc(sizeof(GLfloat) * n * m)) == NULL) { return GLU_OUT_OF_MEMORY; } if ((alpha_new = (GLfloat *) malloc(sizeof(GLfloat) * n * m)) == NULL) { free(alpha); return GLU_OUT_OF_MEMORY; } for (j = 0; j < m; j++) { for (i = 0; i < n; i++) { if ((knot[i] <= new_knot[j]) && (new_knot[j] < knot[i + 1])) tmp_float = 1.0; else tmp_float = 0.0; alpha[i + j * n] = tmp_float; } } for (k = 1; k < order; k++) { for (j = 0; j < m; j++) for (i = 0; i < n; i++) { denom = knot[i + k] - knot[i]; if (fabs(denom) < EPSILON) tmp_float = 0.0; else tmp_float = (new_knot[j + k] - knot[i]) / denom * alpha[i + j * n]; denom = knot[i + k + 1] - knot[i + 1]; if (fabs(denom) > EPSILON) tmp_float += (knot[i + k + 1] - new_knot[j + k]) / denom * alpha[(i + 1) + j * n]; alpha_new[i + j * n] = tmp_float; } tmp_alpha = alpha_new; alpha_new = alpha; alpha = tmp_alpha; } the_knot->alpha = alpha; free(alpha_new); return GLU_NO_ERROR; } GLenum calc_new_ctrl_pts(GLfloat * ctrl, GLint stride, knot_str_type * the_knot, GLint dim, GLfloat ** new_ctrl, GLint * ncontrol) { GLsizei i, j, k, l, m, n; GLsizei index1, index2; GLfloat *alpha; GLfloat *new_knot; new_knot = the_knot->new_knot; n = the_knot->nknots - the_knot->order; alpha = the_knot->alpha; m = the_knot->t_max + 1 - the_knot->t_min - the_knot->order; k = the_knot->t_min; /* allocate space for new control points */ if ((*new_ctrl = (GLfloat *) malloc(sizeof(GLfloat) * dim * m)) == NULL) { return GLU_OUT_OF_MEMORY; } for (j = 0; j < m; j++) { for (l = 0; l < dim; l++) (*new_ctrl)[j * dim + l] = 0.0; for (i = 0; i < n; i++) { index1 = i + (j + k) * n; index2 = i * stride; for (l = 0; l < dim; l++) (*new_ctrl)[j * dim + l] += alpha[index1] * ctrl[index2 + l]; } } *ncontrol = (GLint) m; return GLU_NO_ERROR; } static GLint calc_factor(GLfloat * pts, GLint order, GLint indx, GLint stride, GLfloat tolerance, GLint dim) { GLdouble model[16], proj[16]; GLint viewport[4]; GLdouble x, y, z, w, winx1, winy1, winz, winx2, winy2; GLint i; GLdouble len, dx, dy; glGetDoublev(GL_MODELVIEW_MATRIX, model); glGetDoublev(GL_PROJECTION_MATRIX, proj); glGetIntegerv(GL_VIEWPORT, viewport); if (dim == 4) { w = (GLdouble) pts[indx + 3]; x = (GLdouble) pts[indx] / w; y = (GLdouble) pts[indx + 1] / w; z = (GLdouble) pts[indx + 2] / w; gluProject(x, y, z, model, proj, viewport, &winx1, &winy1, &winz); len = 0.0; for (i = 1; i < order; i++) { w = (GLdouble) pts[indx + i * stride + 3]; x = (GLdouble) pts[indx + i * stride] / w; y = (GLdouble) pts[indx + i * stride + 1] / w; z = (GLdouble) pts[indx + i * stride + 2] / w; if (gluProject (x, y, z, model, proj, viewport, &winx2, &winy2, &winz)) { dx = winx2 - winx1; dy = winy2 - winy1; len += sqrt(dx * dx + dy * dy); } winx1 = winx2; winy1 = winy2; } } else { x = (GLdouble) pts[indx]; y = (GLdouble) pts[indx + 1]; if (dim == 2) z = 0.0; else z = (GLdouble) pts[indx + 2]; gluProject(x, y, z, model, proj, viewport, &winx1, &winy1, &winz); len = 0.0; for (i = 1; i < order; i++) { x = (GLdouble) pts[indx + i * stride]; y = (GLdouble) pts[indx + i * stride + 1]; if (dim == 2) z = 0.0; else z = (GLdouble) pts[indx + i * stride + 2]; if (gluProject (x, y, z, model, proj, viewport, &winx2, &winy2, &winz)) { dx = winx2 - winx1; dy = winy2 - winy1; len += sqrt(dx * dx + dy * dy); } winx1 = winx2; winy1 = winy2; } } len /= tolerance; return ((GLint) len + 1); } /* we can't use the Mesa evaluators - no way to get the point coords */ /* so we use our own Bezier point calculus routines */ /* because I'm lazy, I reuse the ones from eval.c */ static void bezier_curve(GLfloat * cp, GLfloat * out, GLfloat t, GLuint dim, GLuint order, GLint offset) { GLfloat s, powert; GLuint i, k, bincoeff; if (order >= 2) { bincoeff = order - 1; s = 1.0 - t; for (k = 0; k < dim; k++) out[k] = s * cp[k] + bincoeff * t * cp[offset + k]; for (i = 2, cp += 2 * offset, powert = t * t; i < order; i++, powert *= t, cp += offset) { bincoeff *= order - i; bincoeff /= i; for (k = 0; k < dim; k++) out[k] = s * out[k] + bincoeff * powert * cp[k]; } } else { /* order=1 -> constant curve */ for (k = 0; k < dim; k++) out[k] = cp[k]; } } static GLint calc_parametric_factor(GLfloat * pts, GLint order, GLint indx, GLint stride, GLfloat tolerance, GLint dim) { GLdouble model[16], proj[16]; GLint viewport[4]; GLdouble x, y, z, w, x1, y1, z1, x2, y2, z2, x3, y3, z3; GLint i; GLint P; GLfloat bez_pt[4]; GLdouble len = 0.0, tmp, z_med; P = 2 * (order + 2); glGetDoublev(GL_MODELVIEW_MATRIX, model); glGetDoublev(GL_PROJECTION_MATRIX, proj); glGetIntegerv(GL_VIEWPORT, viewport); z_med = (viewport[2] + viewport[3]) * 0.5; switch (dim) { case 4: for (i = 1; i < P; i++) { bezier_curve(pts + indx, bez_pt, (GLfloat) i / (GLfloat) P, 4, order, stride); w = (GLdouble) bez_pt[3]; x = (GLdouble) bez_pt[0] / w; y = (GLdouble) bez_pt[1] / w; z = (GLdouble) bez_pt[2] / w; gluProject(x, y, z, model, proj, viewport, &x3, &y3, &z3); z3 *= z_med; bezier_curve(pts + indx, bez_pt, (GLfloat) (i - 1) / (GLfloat) P, 4, order, stride); w = (GLdouble) bez_pt[3]; x = (GLdouble) bez_pt[0] / w; y = (GLdouble) bez_pt[1] / w; z = (GLdouble) bez_pt[2] / w; gluProject(x, y, z, model, proj, viewport, &x1, &y1, &z1); z1 *= z_med; bezier_curve(pts + indx, bez_pt, (GLfloat) (i + 1) / (GLfloat) P, 4, order, stride); w = (GLdouble) bez_pt[3]; x = (GLdouble) bez_pt[0] / w; y = (GLdouble) bez_pt[1] / w; z = (GLdouble) bez_pt[2] / w; gluProject(x, y, z, model, proj, viewport, &x2, &y2, &z2); z2 *= z_med; /* calc distance between point (x3,y3,z3) and line segment */ /* */ x = x2 - x1; y = y2 - y1; z = z2 - z1; tmp = sqrt(x * x + y * y + z * z); x /= tmp; y /= tmp; z /= tmp; tmp = x3 * x + y3 * y + z3 * z - x1 * x - y1 * y - z1 * z; x = x1 + x * tmp - x3; y = y1 + y * tmp - y3; z = z1 + z * tmp - z3; tmp = sqrt(x * x + y * y + z * z); if (tmp > len) len = tmp; } break; case 3: for (i = 1; i < P; i++) { bezier_curve(pts + indx, bez_pt, (GLfloat) i / (GLfloat) P, 3, order, stride); x = (GLdouble) bez_pt[0]; y = (GLdouble) bez_pt[1]; z = (GLdouble) bez_pt[2]; gluProject(x, y, z, model, proj, viewport, &x3, &y3, &z3); z3 *= z_med; bezier_curve(pts + indx, bez_pt, (GLfloat) (i - 1) / (GLfloat) P, 3, order, stride); x = (GLdouble) bez_pt[0]; y = (GLdouble) bez_pt[1]; z = (GLdouble) bez_pt[2]; gluProject(x, y, z, model, proj, viewport, &x1, &y1, &z1); z1 *= z_med; bezier_curve(pts + indx, bez_pt, (GLfloat) (i + 1) / (GLfloat) P, 3, order, stride); x = (GLdouble) bez_pt[0]; y = (GLdouble) bez_pt[1]; z = (GLdouble) bez_pt[2]; gluProject(x, y, z, model, proj, viewport, &x2, &y2, &z2); z2 *= z_med; /* calc distance between point (x3,y3,z3) and line segment */ /* */ x = x2 - x1; y = y2 - y1; z = z2 - z1; tmp = sqrt(x * x + y * y + z * z); x /= tmp; y /= tmp; z /= tmp; tmp = x3 * x + y3 * y + z3 * z - x1 * x - y1 * y - z1 * z; x = x1 + x * tmp - x3; y = y1 + y * tmp - y3; z = z1 + z * tmp - z3; tmp = sqrt(x * x + y * y + z * z); if (tmp > len) len = tmp; } break; case 2: for (i = 1; i < P; i++) { bezier_curve(pts + indx, bez_pt, (GLfloat) i / (GLfloat) P, 2, order, stride); x = (GLdouble) bez_pt[0]; y = (GLdouble) bez_pt[1]; z = 0.0; gluProject(x, y, z, model, proj, viewport, &x3, &y3, &z3); z3 *= z_med; bezier_curve(pts + indx, bez_pt, (GLfloat) (i - 1) / (GLfloat) P, 2, order, stride); x = (GLdouble) bez_pt[0]; y = (GLdouble) bez_pt[1]; z = 0.0; gluProject(x, y, z, model, proj, viewport, &x1, &y1, &z1); z1 *= z_med; bezier_curve(pts + indx, bez_pt, (GLfloat) (i + 1) / (GLfloat) P, 2, order, stride); x = (GLdouble) bez_pt[0]; y = (GLdouble) bez_pt[1]; z = 0.0; gluProject(x, y, z, model, proj, viewport, &x2, &y2, &z2); z2 *= z_med; /* calc distance between point (x3,y3,z3) and line segment */ /* */ x = x2 - x1; y = y2 - y1; z = z2 - z1; tmp = sqrt(x * x + y * y + z * z); x /= tmp; y /= tmp; z /= tmp; tmp = x3 * x + y3 * y + z3 * z - x1 * x - y1 * y - z1 * z; x = x1 + x * tmp - x3; y = y1 + y * tmp - y3; z = z1 + z * tmp - z3; tmp = sqrt(x * x + y * y + z * z); if (tmp > len) len = tmp; } break; } if (len < tolerance) return (order); else return (GLint) (sqrt(len / tolerance) * (order + 2) + 1); } static GLenum calc_sampling_3D(new_ctrl_type * new_ctrl, GLfloat tolerance, GLint dim, GLint uorder, GLint vorder, GLint ** ufactors, GLint ** vfactors) { GLfloat *ctrl; GLint tmp_factor1, tmp_factor2; GLint ufactor_cnt, vfactor_cnt; GLint offset1, offset2, offset3; GLint i, j; ufactor_cnt = new_ctrl->s_bezier_cnt; vfactor_cnt = new_ctrl->t_bezier_cnt; if ((*ufactors = (GLint *) malloc(sizeof(GLint) * ufactor_cnt * 3)) == NULL) { return GLU_OUT_OF_MEMORY; } if ((*vfactors = (GLint *) malloc(sizeof(GLint) * vfactor_cnt * 3)) == NULL) { free(*ufactors); return GLU_OUT_OF_MEMORY; } ctrl = new_ctrl->geom_ctrl; offset1 = new_ctrl->geom_t_stride * vorder; offset2 = new_ctrl->geom_s_stride * uorder; for (j = 0; j < vfactor_cnt; j++) { *(*vfactors + j * 3 + 1) = tmp_factor1 = calc_factor(ctrl, vorder, j * offset1, dim, tolerance, dim); /* loop ufactor_cnt-1 times */ for (i = 1; i < ufactor_cnt; i++) { tmp_factor2 = calc_factor(ctrl, vorder, j * offset1 + i * offset2, dim, tolerance, dim); if (tmp_factor2 > tmp_factor1) tmp_factor1 = tmp_factor2; } /* last time for the opposite edge */ *(*vfactors + j * 3 + 2) = tmp_factor2 = calc_factor(ctrl, vorder, j * offset1 + i * offset2 - new_ctrl-> geom_s_stride, dim, tolerance, dim); if (tmp_factor2 > tmp_factor1) *(*vfactors + j * 3) = tmp_factor2; else *(*vfactors + j * 3) = tmp_factor1; } offset3 = new_ctrl->geom_s_stride; offset2 = new_ctrl->geom_s_stride * uorder; for (j = 0; j < ufactor_cnt; j++) { *(*ufactors + j * 3 + 1) = tmp_factor1 = calc_factor(ctrl, uorder, j * offset2, offset3, tolerance, dim); /* loop vfactor_cnt-1 times */ for (i = 1; i < vfactor_cnt; i++) { tmp_factor2 = calc_factor(ctrl, uorder, j * offset2 + i * offset1, offset3, tolerance, dim); if (tmp_factor2 > tmp_factor1) tmp_factor1 = tmp_factor2; } /* last time for the opposite edge */ *(*ufactors + j * 3 + 2) = tmp_factor2 = calc_factor(ctrl, uorder, j * offset2 + i * offset1 - new_ctrl-> geom_t_stride, offset3, tolerance, dim); if (tmp_factor2 > tmp_factor1) *(*ufactors + j * 3) = tmp_factor2; else *(*ufactors + j * 3) = tmp_factor1; } return GL_NO_ERROR; } static GLenum calc_sampling_param_3D(new_ctrl_type * new_ctrl, GLfloat tolerance, GLint dim, GLint uorder, GLint vorder, GLint ** ufactors, GLint ** vfactors) { GLfloat *ctrl; GLint tmp_factor1, tmp_factor2; GLint ufactor_cnt, vfactor_cnt; GLint offset1, offset2, offset3; GLint i, j; ufactor_cnt = new_ctrl->s_bezier_cnt; vfactor_cnt = new_ctrl->t_bezier_cnt; if ((*ufactors = (GLint *) malloc(sizeof(GLint) * ufactor_cnt * 3)) == NULL) { return GLU_OUT_OF_MEMORY; } if ((*vfactors = (GLint *) malloc(sizeof(GLint) * vfactor_cnt * 3)) == NULL) { free(*ufactors); return GLU_OUT_OF_MEMORY; } ctrl = new_ctrl->geom_ctrl; offset1 = new_ctrl->geom_t_stride * vorder; offset2 = new_ctrl->geom_s_stride * uorder; for (j = 0; j < vfactor_cnt; j++) { *(*vfactors + j * 3 + 1) = tmp_factor1 = calc_parametric_factor(ctrl, vorder, j * offset1, dim, tolerance, dim); /* loop ufactor_cnt-1 times */ for (i = 1; i < ufactor_cnt; i++) { tmp_factor2 = calc_parametric_factor(ctrl, vorder, j * offset1 + i * offset2, dim, tolerance, dim); if (tmp_factor2 > tmp_factor1) tmp_factor1 = tmp_factor2; } /* last time for the opposite edge */ *(*vfactors + j * 3 + 2) = tmp_factor2 = calc_parametric_factor(ctrl, vorder, j * offset1 + i * offset2 - new_ctrl->geom_s_stride, dim, tolerance, dim); if (tmp_factor2 > tmp_factor1) *(*vfactors + j * 3) = tmp_factor2; else *(*vfactors + j * 3) = tmp_factor1; } offset3 = new_ctrl->geom_s_stride; offset2 = new_ctrl->geom_s_stride * uorder; for (j = 0; j < ufactor_cnt; j++) { *(*ufactors + j * 3 + 1) = tmp_factor1 = calc_parametric_factor(ctrl, uorder, j * offset2, offset3, tolerance, dim); /* loop vfactor_cnt-1 times */ for (i = 1; i < vfactor_cnt; i++) { tmp_factor2 = calc_parametric_factor(ctrl, uorder, j * offset2 + i * offset1, offset3, tolerance, dim); if (tmp_factor2 > tmp_factor1) tmp_factor1 = tmp_factor2; } /* last time for the opposite edge */ *(*ufactors + j * 3 + 2) = tmp_factor2 = calc_parametric_factor(ctrl, uorder, j * offset2 + i * offset1 - new_ctrl->geom_t_stride, offset3, tolerance, dim); if (tmp_factor2 > tmp_factor1) *(*ufactors + j * 3) = tmp_factor2; else *(*ufactors + j * 3) = tmp_factor1; } return GL_NO_ERROR; } static GLenum calc_sampling_2D(GLfloat * ctrl, GLint cnt, GLint order, GLfloat tolerance, GLint dim, GLint ** factors) { GLint factor_cnt; GLint tmp_factor; GLint offset; GLint i; factor_cnt = cnt / order; if ((*factors = (GLint *) malloc(sizeof(GLint) * factor_cnt)) == NULL) { return GLU_OUT_OF_MEMORY; } offset = order * dim; for (i = 0; i < factor_cnt; i++) { tmp_factor = calc_factor(ctrl, order, i * offset, dim, tolerance, dim); if (tmp_factor == 0) (*factors)[i] = 1; else (*factors)[i] = tmp_factor; } return GL_NO_ERROR; } static void set_sampling_and_culling(GLUnurbsObj * nobj) { if (nobj->auto_load_matrix == GL_FALSE) { GLint i; GLfloat m[4]; glPushAttrib((GLbitfield) (GL_VIEWPORT_BIT | GL_TRANSFORM_BIT)); for (i = 0; i < 4; i++) m[i] = nobj->sampling_matrices.viewport[i]; glViewport(m[0], m[1], m[2], m[3]); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadMatrixf(nobj->sampling_matrices.proj); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadMatrixf(nobj->sampling_matrices.model); } } static void revert_sampling_and_culling(GLUnurbsObj * nobj) { if (nobj->auto_load_matrix == GL_FALSE) { glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glPopAttrib(); } } GLenum glu_do_sampling_3D(GLUnurbsObj * nobj, new_ctrl_type * new_ctrl, GLint ** sfactors, GLint ** tfactors) { GLint dim; GLenum err; *sfactors = NULL; *tfactors = NULL; dim = nobj->surface.geom.dim; set_sampling_and_culling(nobj); if ((err = calc_sampling_3D(new_ctrl, nobj->sampling_tolerance, dim, nobj->surface.geom.sorder, nobj->surface.geom.torder, sfactors, tfactors)) == GLU_ERROR) { revert_sampling_and_culling(nobj); call_user_error(nobj, err); return GLU_ERROR; } revert_sampling_and_culling(nobj); return GLU_NO_ERROR; } GLenum glu_do_sampling_uv(GLUnurbsObj * nobj, new_ctrl_type * new_ctrl, GLint ** sfactors, GLint ** tfactors) { GLint s_cnt, t_cnt, i; GLint u_steps, v_steps; s_cnt = new_ctrl->s_bezier_cnt; t_cnt = new_ctrl->t_bezier_cnt; *sfactors = NULL; *tfactors = NULL; if ((*sfactors = (GLint *) malloc(sizeof(GLint) * s_cnt * 3)) == NULL) { return GLU_OUT_OF_MEMORY; } if ((*tfactors = (GLint *) malloc(sizeof(GLint) * t_cnt * 3)) == NULL) { free(*sfactors); return GLU_OUT_OF_MEMORY; } u_steps = nobj->u_step; v_steps = nobj->v_step; for (i = 0; i < s_cnt; i++) { *(*sfactors + i * 3) = u_steps; *(*sfactors + i * 3 + 1) = u_steps; *(*sfactors + i * 3 + 2) = u_steps; } for (i = 0; i < t_cnt; i++) { *(*tfactors + i * 3) = v_steps; *(*tfactors + i * 3 + 1) = v_steps; *(*tfactors + i * 3 + 2) = v_steps; } return GLU_NO_ERROR; } GLenum glu_do_sampling_param_3D(GLUnurbsObj * nobj, new_ctrl_type * new_ctrl, GLint ** sfactors, GLint ** tfactors) { GLint dim; GLenum err; *sfactors = NULL; *tfactors = NULL; dim = nobj->surface.geom.dim; set_sampling_and_culling(nobj); if ( (err = calc_sampling_param_3D(new_ctrl, nobj->parametric_tolerance, dim, nobj->surface.geom.sorder, nobj->surface.geom.torder, sfactors, tfactors)) == GLU_ERROR) { revert_sampling_and_culling(nobj); call_user_error(nobj, err); return GLU_ERROR; } revert_sampling_and_culling(nobj); return GLU_NO_ERROR; } static GLenum glu_do_sampling_2D(GLUnurbsObj * nobj, GLfloat * ctrl, GLint cnt, GLint order, GLint dim, GLint ** factors) { GLenum err; set_sampling_and_culling(nobj); err = calc_sampling_2D(ctrl, cnt, order, nobj->sampling_tolerance, dim, factors); revert_sampling_and_culling(nobj); return err; } static GLenum glu_do_sampling_u(GLUnurbsObj * nobj, GLfloat * ctrl, GLint cnt, GLint order, GLint dim, GLint ** factors) { GLint i; GLint u_steps; cnt /= order; if ((*factors = (GLint *) malloc(sizeof(GLint) * cnt)) == NULL) { return GLU_OUT_OF_MEMORY; } u_steps = nobj->u_step; for (i = 0; i < cnt; i++) (*factors)[i] = u_steps; return GLU_NO_ERROR; } static GLenum glu_do_sampling_param_2D(GLUnurbsObj * nobj, GLfloat * ctrl, GLint cnt, GLint order, GLint dim, GLint ** factors) { GLint i; GLint u_steps; GLfloat tolerance; set_sampling_and_culling(nobj); tolerance = nobj->parametric_tolerance; cnt /= order; if ((*factors = (GLint *) malloc(sizeof(GLint) * cnt)) == NULL) { revert_sampling_and_culling(nobj); return GLU_OUT_OF_MEMORY; } u_steps = nobj->u_step; for (i = 0; i < cnt; i++) { (*factors)[i] = calc_parametric_factor(ctrl, order, 0, dim, tolerance, dim); } revert_sampling_and_culling(nobj); return GLU_NO_ERROR; } GLenum glu_do_sampling_crv(GLUnurbsObj * nobj, GLfloat * ctrl, GLint cnt, GLint order, GLint dim, GLint ** factors) { GLenum err; *factors = NULL; switch (nobj->sampling_method) { case GLU_PATH_LENGTH: if ((err = glu_do_sampling_2D(nobj, ctrl, cnt, order, dim, factors)) != GLU_NO_ERROR) { call_user_error(nobj, err); return GLU_ERROR; } break; case GLU_DOMAIN_DISTANCE: if ((err = glu_do_sampling_u(nobj, ctrl, cnt, order, dim, factors)) != GLU_NO_ERROR) { call_user_error(nobj, err); return GLU_ERROR; } break; case GLU_PARAMETRIC_ERROR: if ( (err = glu_do_sampling_param_2D(nobj, ctrl, cnt, order, dim, factors)) != GLU_NO_ERROR) { call_user_error(nobj, err); return GLU_ERROR; } break; default: abort(); } return GLU_NO_ERROR; } /* TODO - i don't like this culling - this one just tests if at least one */ /* ctrl point lies within the viewport . Also the point_in_viewport() */ /* should be included in the fnctions for efficiency reasons */ static GLboolean point_in_viewport(GLfloat * pt, GLint dim) { GLdouble model[16], proj[16]; GLint viewport[4]; GLdouble x, y, z, w, winx, winy, winz; glGetDoublev(GL_MODELVIEW_MATRIX, model); glGetDoublev(GL_PROJECTION_MATRIX, proj); glGetIntegerv(GL_VIEWPORT, viewport); if (dim == 3) { x = (GLdouble) pt[0]; y = (GLdouble) pt[1]; z = (GLdouble) pt[2]; gluProject(x, y, z, model, proj, viewport, &winx, &winy, &winz); } else { w = (GLdouble) pt[3]; x = (GLdouble) pt[0] / w; y = (GLdouble) pt[1] / w; z = (GLdouble) pt[2] / w; gluProject(x, y, z, model, proj, viewport, &winx, &winy, &winz); } if ((GLint) winx >= viewport[0] && (GLint) winx < viewport[2] && (GLint) winy >= viewport[1] && (GLint) winy < viewport[3]) return GL_TRUE; return GL_FALSE; } GLboolean fine_culling_test_3D(GLUnurbsObj * nobj, GLfloat * pts, GLint s_cnt, GLint t_cnt, GLint s_stride, GLint t_stride, GLint dim) { GLint i, j; if (nobj->culling == GL_FALSE) return GL_FALSE; set_sampling_and_culling(nobj); if (dim == 3) { for (i = 0; i < s_cnt; i++) for (j = 0; j < t_cnt; j++) if (point_in_viewport(pts + i * s_stride + j * t_stride, dim)) { revert_sampling_and_culling(nobj); return GL_FALSE; } } else { for (i = 0; i < s_cnt; i++) for (j = 0; j < t_cnt; j++) if (point_in_viewport(pts + i * s_stride + j * t_stride, dim)) { revert_sampling_and_culling(nobj); return GL_FALSE; } } revert_sampling_and_culling(nobj); return GL_TRUE; } /*GLboolean fine_culling_test_3D(GLUnurbsObj *nobj,GLfloat *pts,GLint s_cnt,GLint t_cnt, GLint s_stride,GLint t_stride, GLint dim) { GLint visible_cnt; GLfloat feedback_buffer[5]; GLsizei buffer_size; GLint i,j; if(nobj->culling==GL_FALSE) return GL_FALSE; buffer_size=5; set_sampling_and_culling(nobj); glFeedbackBuffer(buffer_size,GL_2D,feedback_buffer); glRenderMode(GL_FEEDBACK); if(dim==3) { for(i=0;iculling == GL_FALSE) return GL_FALSE; set_sampling_and_culling(nobj); if (dim == 3) { for (i = 0; i < cnt; i++) if (point_in_viewport(pts + i * stride, dim)) { revert_sampling_and_culling(nobj); return GL_FALSE; } } else { for (i = 0; i < cnt; i++) if (point_in_viewport(pts + i * stride, dim)) { revert_sampling_and_culling(nobj); return GL_FALSE; } } revert_sampling_and_culling(nobj); return GL_TRUE; } /*GLboolean fine_culling_test_2D(GLUnurbsObj *nobj,GLfloat *pts,GLint cnt, GLint stride, GLint dim) { GLint visible_cnt; GLfloat feedback_buffer[5]; GLsizei buffer_size; GLint i; if(nobj->culling==GL_FALSE) return GL_FALSE; buffer_size=5; set_sampling_and_culling(nobj); glFeedbackBuffer(buffer_size,GL_2D,feedback_buffer); glRenderMode(GL_FEEDBACK); glBegin(GL_LINE_LOOP); if(dim==3) { for(i=0;i