/* nurbs.c - Edouard Thiel - 28/02/2000 Editeur de nurbs - carreau 4x4, degré 3, non rationnel. Les appels à OpenGL sont abondament commentés. Le programme permet de tester les effets des paramétrages de rendu d'un nurbs. Usage : Bouton1 : trackball Bouton2 : zoom sur l'angle de vue Bouton3 : rapprocher/éloigner Dans la fenêtre Edit, il suffit d'appuyer sur [Enter] dans n'importe quel champ de texte pour afficher la nouvelle nurbs. */ #include He_node *princ, *gla, *panel, *fen_edit, *pan_edit, *text_ctrlpoints[4][4][3], *text_knots_u[8], *text_knots_v[8], *text_step_mess, *text_step_u, *text_step_v, *text_sam_tol_mess, *text_sam_tol_val, *text_par_tol_mess, *text_par_tol_val; He_trackball info_tb; /*---------------------------- C O U L E U R S ------------------------------*/ void init_eclairage () { GLfloat ambient[] = {0.4, 0.4, 0.4, 1.0}; GLfloat position[] = {0.0, 0.0, 2.0, 1.0}; glEnable (GL_LIGHTING); glEnable (GL_LIGHT0); glLightfv (GL_LIGHT0, GL_AMBIENT, ambient); glLightfv (GL_LIGHT0, GL_POSITION, position); /* On autorise l'éclairage des 2 côtés : cette fonction fait produire des normales opposées pour chaque côté d'un polygone */ glLightModeli (GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); } void lumieres_surface () { GLfloat mat_diffuse[] = {0.6, 0.6, 0.3, 1.0}; GLfloat mat_specular[] = {1.0, 1.0, 0.5, 1.0}; GLfloat mat_shininess[] = {50.0}; GLfloat no_mat[] = {0.0, 0.0, 0.0, 1.0}; /* C'est surtout pour GL_SPECULAR qu'il faut mettre GL_FRONT_AND_BACK */ glMaterialfv (GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse); glMaterialfv (GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); glMaterialfv (GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); glMaterialfv (GL_FRONT_AND_BACK, GL_EMISSION, no_mat); } /*-------------------------------- N U R B S --------------------------------*/ GLfloat ctrlpoints[4][4][3] = { { { -3, -3, -3}, { -1, -3, 0}, { 1, -3, 0}, { 3, -3, 2} }, { { -3, -1, 0}, { -1, -1, -1}, { 1, -1, -1}, { 3, -1, 1} }, { { -3, 1, 0}, { -1, 1, -1}, { 1, 1, -1}, { 3, 1, 1} }, { { -3, 3, 2}, { -1, 3, 0}, { 1, 3, 0}, { 3, 3, 2} } }; GLfloat knots_u[8] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 }, knots_v[8] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 }; GLUnurbsObj *theNurb = NULL; enum { CALC_SAMPLING, CALC_STEP, CALC_PARAM }; int calc_mode = CALC_STEP; void nurbsError (GLenum errorCode) { const GLubyte *estring; estring = gluErrorString(errorCode); HeSimpleDialog ( HE_DIALOG_BELL, HE_DIALOG_TITLE, "Nurbs error:", HE_DIALOG_MESSAGE, estring, HE_DIALOG_BUTTON, "Ok", 0); } void init_nurbs () { /* Alloue la mémoire ; pour libérer la mémoire on fait gluDeleteNurbsRendere(theNurb); */ theNurb = gluNewNurbsRenderer(); /* Mode de dessin */ gluNurbsProperty (theNurb, GLU_DISPLAY_MODE, GLU_FILL); /* Appelle une callback en cas d'erreur (en particulier sur les vecteurs de noeuds) */ gluNurbsCallback (theNurb, GLU_ERROR, (GLvoid (*) ()) nurbsError); /* Fixe la longueur du plus grand segment produit par la triangulation à xxx pixels. Attention si on fait un glScale (ou un zoom d'angle) il faut adapter la valeur. GLU_PATH_LENGTH est la méthode par défaut. gluNurbsProperty (theNurb, GLU_SAMPLING_METHOD, GLU_PATH_LENGTH); gluNurbsProperty (theNurb, GLU_SAMPLING_TOLERANCE, sampling_tolerance); */ gluNurbsProperty (theNurb, GLU_SAMPLING_METHOD, GLU_DOMAIN_DISTANCE); gluNurbsProperty (theNurb, GLU_U_STEP, 20.0); gluNurbsProperty (theNurb, GLU_V_STEP, 20.0); } void dessin_nurbs () { gluBeginSurface (theNurb); gluNurbsSurface (theNurb, 8, knots_u, /* noeuds en u et v */ 8, knots_v, /* le nombre = ordre*2 = 2*degré+2 */ 4*3, 3, &ctrlpoints[0][0][0], /* points de contrôles et décalages */ 4, 4, /* ordres (degrés+1) */ GL_MAP2_VERTEX_3); /* ou GL_MAP2_VERTEX_4 pour les poids w */ gluEndSurface (theNurb); } /*----------------------------- D E S S I N S -------------------------------*/ void dessin_point (GLfloat x, GLfloat y, GLfloat z) { GLfloat d = 0.05; glBegin(GL_LINE_LOOP); glVertex3f(x-d, y-d, z); glVertex3f(x+d, y-d, z); glVertex3f(x+d, y+d, z); glVertex3f(x-d, y+d, z); glEnd(); } void dessin_points_controle () { int i, j; for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) dessin_point (ctrlpoints[i][j][0], ctrlpoints[i][j][1], ctrlpoints[i][j][2]); } void dessin_polygone_controle () { int i, j; for (i = 0; i < 4; i++) { glBegin(GL_LINE_STRIP); for (j = 0; j < 4; j++) glVertex3f(ctrlpoints[i][j][0], ctrlpoints[i][j][1], ctrlpoints[i][j][2]); glEnd(); } for (j = 0; j < 4; j++) { glBegin(GL_LINE_STRIP); for (i = 0; i < 4; i++) glVertex3f(ctrlpoints[i][j][0], ctrlpoints[i][j][1], ctrlpoints[i][j][2]); glEnd(); } } /*---------------------------------- G L ------------------------------------*/ void initgl_proc (He_node *hn) { /* Init trackball */ HeTbInitRotations(&info_tb); HeTbInitFovy(&info_tb, 20, 5, 120); HeTbInitZdist(&info_tb, 30, 10, 50); /* Autorise le Z-buffer */ glEnable(GL_DEPTH_TEST); /* Autorise le calcul automatique des normales à la surface */ glEnable (GL_AUTO_NORMAL); /* Normalisation automatique */ glEnable (GL_NORMALIZE); init_eclairage (); init_nurbs (); } void dessine_proc (He_node *hn) { /* MODE PERSPECTIVE : * * gluPerspective(fovy, aspect, near, far) * fovy : angle 0.0 .. 180.0 * aspect : ratio width/height * near, far : distances des plans de coupes sur -Oz ; * tjrs positives * * - pour conserver les proportions de perspective on recalcule "aspect". * - le zoom du trackball est utilisé pour l'angle de vue. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective( HeTbGetFovy(&info_tb), (GLdouble) HeGetWidth(hn)/HeGetHeight(hn), 1, 100 ); /* On va dessiner la scène ; au lieu de bouger la caméra (gluLookAT) * on bouge le repère, ce qui revient au même. Toutes les opérations de * bougé sont implémentées dans l'ordre du repère local. */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* Par défaut la caméra est en 0,0,0 et regarde vers -Oz. * On recule tout ce qui va être dessiné ; de la sorte on peut travailler * avec des z autour de 0, et pas uniquement < 0. */ glTranslated (0, 0, -HeTbGetZdist(&info_tb)); /* On tourne le repère d'après le trackball */ HeTbMultRotations(&info_tb); /* Init du fond (bleu opaque) et du Zbuffer */ glClearColor(.3,.4,.6,1); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glDisable(GL_LIGHTING); glColor3f(1,0,0); dessin_polygone_controle (); glColor3f(0,1,0); dessin_points_controle (); glEnable(GL_LIGHTING); lumieres_surface(); dessin_nurbs (); } /*-------------------------- I N T E R F A C E ------------------------------*/ void princ_resize (He_node *hn, int width, int height) { HeSetWidth (panel, width); HeExpand (gla, NULL, HE_BOTTOM_RIGHT); } void glarea_event (He_node *hn, He_event *hev) { switch (hev->type) { case ButtonPress : HeTbMemoPointer (&info_tb, hev); break; case MotionNotify : switch (hev->sb) { case 1 : HeTbEventRotations(&info_tb, hn, hev); HePostRepaint(hn); break; case 2 : HeTbEventFovy (&info_tb, hn, hev); HePostRepaint(hn); break; case 3 : HeTbEventZdist (&info_tb, hn, hev); HePostRepaint(hn); break; } break; } } void edit_notify_proc (He_node *hn) { int i, j, k; for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) for (k = 0; k < 3; k++) ctrlpoints[i][j][k] = atof(HeGetTextValue(text_ctrlpoints[i][j][k])); for (i = 0; i < 8; i++) knots_u[i] = atof(HeGetTextValue(text_knots_u[i])); for (i = 0; i < 8; i++) knots_v[i] = atof(HeGetTextValue(text_knots_v[i])); switch (calc_mode) { case CALC_SAMPLING : gluNurbsProperty (theNurb, GLU_SAMPLING_METHOD, GLU_PATH_LENGTH); gluNurbsProperty (theNurb, GLU_SAMPLING_TOLERANCE, atof(HeGetTextValue(text_sam_tol_val)) ); break; case CALC_STEP : gluNurbsProperty (theNurb, GLU_SAMPLING_METHOD, GLU_DOMAIN_DISTANCE); gluNurbsProperty (theNurb, GLU_U_STEP, atof(HeGetTextValue(text_step_u)) ); gluNurbsProperty (theNurb, GLU_V_STEP, atof(HeGetTextValue(text_step_v)) ); break; case CALC_PARAM : gluNurbsProperty (theNurb, GLU_SAMPLING_METHOD, GLU_PARAMETRIC_ERROR); gluNurbsProperty (theNurb, GLU_PARAMETRIC_TOLERANCE, atof(HeGetTextValue(text_par_tol_val)) ); break; } HePostRepaint (gla); } void edit_toggle1_proc (He_node *hn) { char *nom; if (HeGetToggleValue(hn) == FALSE) return; nom = HeGetToggleLabel(hn); if (strcmp(nom, "Fill") == 0) { gluNurbsProperty (theNurb, GLU_DISPLAY_MODE, GLU_FILL); } else if (strcmp(nom, "Outline polygon") == 0) { gluNurbsProperty (theNurb, GLU_DISPLAY_MODE, GLU_OUTLINE_POLYGON); } else if (strcmp(nom, "Outline patch") == 0) { gluNurbsProperty (theNurb, GLU_DISPLAY_MODE, GLU_OUTLINE_PATCH); } edit_notify_proc (hn); } void edit_toggle2_proc (He_node *hn) { char *nom = HeGetToggleLabel(hn); int etat = HeGetToggleValue(hn); if (strcmp(nom, "Path length") == 0) { HeSetActive (text_sam_tol_mess, etat); HeSetActive (text_sam_tol_val, etat); if (etat) calc_mode = CALC_SAMPLING; } else if (strcmp(nom, "Domain distance") == 0) { HeSetActive (text_step_mess, etat); HeSetActive (text_step_u, etat); HeSetActive (text_step_v, etat); if (etat) calc_mode = CALC_STEP; } else if (strcmp(nom, "Parametric error") == 0) { HeSetActive (text_par_tol_mess, etat); HeSetActive (text_par_tol_val, etat); if (etat) calc_mode = CALC_PARAM; } if (etat) edit_notify_proc (hn); } void edit_change_noeuds_uv (GLfloat *knots) { char bla[100]; int i; for (i = 0; i < 8; i++) { sprintf (bla, "%.1f", knots[i]); HeSetTextValue (text_knots_u[i], bla); HeSetTextValue (text_knots_v[i], bla); } } void edit_bezier_proc (He_node *hn) { GLfloat knots[] = { 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 }; edit_change_noeuds_uv (knots); edit_notify_proc (hn); } void edit_bspline_unif_proc (He_node *hn) { GLfloat knots[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 }; edit_change_noeuds_uv (knots); edit_notify_proc (hn); } void init_fen_edit () { int i, j, k; He_node *tmp, *tmp1; char bla[100]; fen_edit = HeCreateFrame (); HeSetFrameLabel (fen_edit, "Edition du Nurbs non rationnel de degré 3"); pan_edit = HeCreatePanel (fen_edit); HeCreateMessageP (pan_edit, "Coordonnées (x,y,z) des points de contrôle :", TRUE); HeSetPanelLayout (pan_edit, HE_LINE_FEED); for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { for (k = 0; k < 3; k++) { text_ctrlpoints[i][j][k] = tmp = HeCreateText (pan_edit); HeSetTextVisibleLen (tmp, 5); if (k == 0 && j > 0) HeMoveX (tmp, 20); sprintf (bla, "%.1f", ctrlpoints[i][j][k]); HeSetTextValue (tmp, bla); HeSetTextNotifyProc (tmp, edit_notify_proc); } } HeSetPanelLayout (pan_edit, HE_LINE_FEED); } tmp = HeCreateMessageP (pan_edit, "Noeuds prédéfinis :", TRUE); HeMoveY (tmp, 15); HeCreateButtonP(pan_edit, "Bezier", edit_bezier_proc, NULL); HeCreateButtonP(pan_edit, "B-spline uniforme", edit_bspline_unif_proc, NULL); HeSetPanelLayout (pan_edit, HE_LINE_FEED); HeCreateMessageP (pan_edit, "Noeuds u :", TRUE); for (i = 0; i < 8; i++) { text_knots_u[i] = tmp = HeCreateText (pan_edit); HeSetTextVisibleLen (tmp, 5); sprintf (bla, "%.1f", knots_u[i]); HeSetTextValue (tmp, bla); HeSetTextNotifyProc (tmp, edit_notify_proc); } HeSetPanelLayout (pan_edit, HE_LINE_FEED); HeCreateMessageP (pan_edit, "Noeuds v :", TRUE); for (i = 0; i < 8; i++) { text_knots_v[i] = tmp = HeCreateText (pan_edit); HeSetTextVisibleLen (tmp, 5); sprintf (bla, "%.1f", knots_v[i]); HeSetTextValue (tmp, bla); HeSetTextNotifyProc (tmp, edit_notify_proc); } HeSetPanelLayout (pan_edit, HE_LINE_FEED); HeCreateMessageP (pan_edit, "Mode d'affichage : ", TRUE); tmp = HeCreateToggle (pan_edit, HE_RADIO); HeSetToggleLabel (tmp, "Fill"); HeSetToggleNotifyProc (tmp, edit_toggle1_proc); HeSetToggleValue (tmp, TRUE); tmp1 = tmp; tmp = HeCreateToggle (pan_edit, HE_RADIO); HeSetToggleLabel (tmp, "Outline polygon"); HeSetToggleNotifyProc (tmp, edit_toggle1_proc); HeGroupToggle (tmp, tmp1); tmp1 = tmp; tmp = HeCreateToggle (pan_edit, HE_RADIO); HeSetToggleLabel (tmp, "Outline patch"); HeSetToggleNotifyProc (tmp, edit_toggle1_proc); HeGroupToggle (tmp, tmp1); HeSetPanelLayout (pan_edit, HE_LINE_FEED); HeCreateMessageP (pan_edit, "Echantillonnage : ", TRUE); tmp = HeCreateToggle (pan_edit, HE_RADIO); HeSetToggleLabel (tmp, "Path length"); HeSetToggleNotifyProc (tmp, edit_toggle2_proc); tmp1 = tmp; tmp = HeCreateToggle (pan_edit, HE_RADIO); HeSetToggleLabel (tmp, "Domain distance"); HeSetToggleNotifyProc (tmp, edit_toggle2_proc); HeGroupToggle (tmp, tmp1); HeSetToggleValue (tmp, TRUE); tmp1 = tmp; tmp = HeCreateToggle (pan_edit, HE_RADIO); HeSetToggleLabel (tmp, "Parametric error"); HeSetToggleNotifyProc (tmp, edit_toggle2_proc); HeGroupToggle (tmp, tmp1); HeSetPanelLayout (pan_edit, HE_LINE_FEED); text_sam_tol_mess = tmp = HeCreateMessageP (pan_edit, "Sampling tol. :", TRUE); HeSetActive (tmp, FALSE); text_sam_tol_val = tmp = HeCreateText (pan_edit); HeSetActive (tmp, FALSE); HeSetTextVisibleLen (tmp, 6); HeSetTextValue (tmp, "50.0"); HeSetTextNotifyProc (tmp, edit_notify_proc); text_step_mess = HeCreateMessageP (pan_edit, "Step :", TRUE); text_step_u = tmp = HeCreateText (pan_edit); HeSetTextVisibleLen (tmp, 6); HeSetTextValue (tmp, "20.0"); HeSetTextNotifyProc (tmp, edit_notify_proc); text_step_v = tmp = HeCreateText (pan_edit); HeSetTextVisibleLen (tmp, 6); HeSetTextValue (tmp, "20.0"); HeSetTextNotifyProc (tmp, edit_notify_proc); text_par_tol_mess = tmp = HeCreateMessageP (pan_edit, "Parametric tol. :", TRUE); HeSetActive (tmp, FALSE); text_par_tol_val = tmp = HeCreateText (pan_edit); HeSetActive (tmp, FALSE); HeSetTextVisibleLen (tmp, 6); HeSetTextValue (tmp, "0.5"); HeSetTextNotifyProc (tmp, edit_notify_proc); HeSetPanelLayout (pan_edit, HE_LINE_FEED); HeFit (pan_edit); HeFit (fen_edit); } void show_edit_proc (He_node *hn) { HeSetShow (fen_edit, !HeGetShow(fen_edit)); } void butt_proc (He_node *hn) { char *name = HeGetButtonLabel (hn); if (strcmp (name, "Quit") == 0) HeQuit(0); } /*------------------------------- M A I N -----------------------------------*/ int main (int argc, char *argv[]) { int attr_list[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 1, None }; HeInit (&argc, &argv); princ = HeCreateFrame(); HeSetFrameLabel (princ, "Carreau de Nurbs 4x4"); HeSetFrameResizeProc (princ, princ_resize); panel = HeCreatePanel (princ); HeCreateButtonP (panel, "Quit", butt_proc, NULL); HeCreateButtonP (panel, "Edit", show_edit_proc, NULL); HeFit (panel); gla = HeCreateGLArea (princ, attr_list, NULL); HeSetY (gla, HeGetHeight(panel)+2); HeSetWidth (gla, 500); HeSetHeight (gla, 400); HeSetGLAreaInitProc (gla, initgl_proc); HeSetGLAreaRepaintProc (gla, dessine_proc); HeSetGLAreaEventProc (gla, glarea_event); HeSetTip (gla, "Bouton 1 : rotations\n" "Bouton 2 : angle de vue\n" "Bouton 3 : distance" ); HeFit (princ); init_fen_edit(); return HeMainLoop (princ); }