1
2/*
3 * A demo of the GLU polygon tesselation functions written by Bogdan Sikorski.
4 * Updated for GLU 1.3 tessellation by Gareth Hughes <gareth@valinux.com>
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include "glut_wrap.h"
11
12#define MAX_POINTS	256
13#define MAX_CONTOURS	32
14#define MAX_TRIANGLES	256
15
16#ifdef GLU_VERSION_1_2
17
18typedef enum{ QUIT, TESSELATE, CLEAR } menu_entries;
19typedef enum{ DEFINE, TESSELATED } mode_type;
20
21static GLsizei		width, height;
22static GLuint		contour_cnt;
23static GLuint		triangle_cnt;
24
25static mode_type 	mode;
26static int		menu;
27
28static GLuint		list_start;
29
30static GLfloat		edge_color[3];
31
32static struct {
33   GLfloat	p[MAX_POINTS][2];
34   GLuint	point_cnt;
35} contours[MAX_CONTOURS];
36
37static struct {
38   GLsizei	no;
39   GLfloat	p[3][2];
40   GLclampf	color[3][3];
41} triangles[MAX_TRIANGLES];
42
43
44
45static void GLAPIENTRY error_callback( GLenum err )
46{
47   int		len, i;
48   char const	*str;
49
50   glColor3f( 0.9, 0.9, 0.9 );
51   glRasterPos2i( 5, 5 );
52
53   str = (const char *) gluErrorString( err );
54   len = strlen( str );
55
56   for ( i = 0 ; i < len ; i++ ) {
57      glutBitmapCharacter( GLUT_BITMAP_9_BY_15, str[i] );
58   }
59}
60
61static void GLAPIENTRY begin_callback( GLenum mode )
62{
63   /* Allow multiple triangles to be output inside the begin/end pair. */
64   triangle_cnt = 0;
65   triangles[triangle_cnt].no = 0;
66}
67
68static void GLAPIENTRY edge_callback( GLenum flag )
69{
70   /* Persist the edge flag across triangles. */
71   if ( flag == GL_TRUE ) {
72      edge_color[0] = 1.0;
73      edge_color[1] = 1.0;
74      edge_color[2] = 0.5;
75   } else {
76      edge_color[0] = 1.0;
77      edge_color[1] = 0.0;
78      edge_color[2] = 0.0;
79   }
80}
81
82static void GLAPIENTRY end_callback(void)
83{
84   GLuint	i;
85
86   glBegin( GL_LINES );
87
88   /* Output the three edges of each triangle as lines colored
89      according to their edge flag. */
90   for ( i = 0 ; i < triangle_cnt ; i++ ) {
91      glColor3f( triangles[i].color[0][0],
92		 triangles[i].color[0][1],
93		 triangles[i].color[0][2] );
94
95      glVertex2f( triangles[i].p[0][0], triangles[i].p[0][1] );
96      glVertex2f( triangles[i].p[1][0], triangles[i].p[1][1] );
97
98      glColor3f( triangles[i].color[1][0],
99		 triangles[i].color[1][1],
100		 triangles[i].color[1][2] );
101
102      glVertex2f( triangles[i].p[1][0], triangles[i].p[1][1] );
103      glVertex2f( triangles[i].p[2][0], triangles[i].p[2][1] );
104
105      glColor3f( triangles[i].color[2][0],
106		 triangles[i].color[2][1],
107		 triangles[i].color[2][2] );
108
109      glVertex2f( triangles[i].p[2][0], triangles[i].p[2][1] );
110      glVertex2f( triangles[i].p[0][0], triangles[i].p[0][1] );
111   }
112
113   glEnd();
114}
115
116static void GLAPIENTRY vertex_callback( void *data )
117{
118   GLsizei	no;
119   GLfloat	*p;
120
121   p = (GLfloat *) data;
122   no = triangles[triangle_cnt].no;
123
124   triangles[triangle_cnt].p[no][0] = p[0];
125   triangles[triangle_cnt].p[no][1] = p[1];
126
127   triangles[triangle_cnt].color[no][0] = edge_color[0];
128   triangles[triangle_cnt].color[no][1] = edge_color[1];
129   triangles[triangle_cnt].color[no][2] = edge_color[2];
130
131   /* After every three vertices, initialize the next triangle. */
132   if ( ++(triangles[triangle_cnt].no) == 3 ) {
133      triangle_cnt++;
134      triangles[triangle_cnt].no = 0;
135   }
136}
137
138static void GLAPIENTRY combine_callback( GLdouble coords[3],
139				       GLdouble *vertex_data[4],
140				       GLfloat weight[4], void **data )
141{
142   GLfloat	*vertex;
143
144   vertex = (GLfloat *) malloc( 2 * sizeof(GLfloat) );
145
146   vertex[0] = (GLfloat) coords[0];
147   vertex[1] = (GLfloat) coords[1];
148
149   *data = vertex;
150}
151
152
153static void set_screen_wh( GLsizei w, GLsizei h )
154{
155   width = w;
156   height = h;
157}
158
159static void tesse( void )
160{
161   GLUtesselator	*tobj;
162   GLdouble		data[3];
163   GLuint		i, j, point_cnt;
164
165   list_start = glGenLists( 2 );
166
167   tobj = gluNewTess();
168
169   if ( tobj != NULL ) {
170      gluTessNormal( tobj, 0.0, 0.0, 1.0 );
171      gluTessCallback( tobj, GLU_TESS_BEGIN, glBegin );
172      gluTessCallback( tobj, GLU_TESS_VERTEX, glVertex2fv );
173      gluTessCallback( tobj, GLU_TESS_END, glEnd );
174      gluTessCallback( tobj, GLU_TESS_ERROR, error_callback );
175      gluTessCallback( tobj, GLU_TESS_COMBINE, combine_callback );
176
177      glNewList( list_start, GL_COMPILE );
178      gluBeginPolygon( tobj );
179
180      for ( j = 0 ; j <= contour_cnt ; j++ ) {
181	 point_cnt = contours[j].point_cnt;
182	 gluNextContour( tobj, GLU_UNKNOWN );
183
184	 for ( i = 0 ; i < point_cnt ; i++ ) {
185	    data[0] = (GLdouble)( contours[j].p[i][0] );
186	    data[1] = (GLdouble)( contours[j].p[i][1] );
187	    data[2] = 0.0;
188	    gluTessVertex( tobj, data, contours[j].p[i] );
189	 }
190      }
191
192      gluEndPolygon( tobj );
193      glEndList();
194
195      gluTessCallback( tobj, GLU_TESS_BEGIN, begin_callback );
196      gluTessCallback( tobj, GLU_TESS_VERTEX, vertex_callback );
197      gluTessCallback( tobj, GLU_TESS_END, end_callback );
198      gluTessCallback( tobj, GLU_TESS_EDGE_FLAG, edge_callback );
199
200      glNewList( list_start + 1, GL_COMPILE );
201      gluBeginPolygon( tobj );
202
203      for ( j = 0 ; j <= contour_cnt ; j++ ) {
204	 point_cnt = contours[j].point_cnt;
205	 gluNextContour( tobj, GLU_UNKNOWN );
206
207	 for ( i = 0 ; i < point_cnt ; i++ ) {
208	    data[0] = (GLdouble)( contours[j].p[i][0] );
209	    data[1] = (GLdouble)( contours[j].p[i][1] );
210	    data[2] = 0.0;
211	    gluTessVertex( tobj, data, contours[j].p[i] );
212	 }
213      }
214
215      gluEndPolygon( tobj );
216      glEndList();
217
218      gluDeleteTess( tobj );
219
220      glutMouseFunc( NULL );
221      mode = TESSELATED;
222   }
223}
224
225static void left_down( int x1, int y1 )
226{
227   GLfloat	P[2];
228   GLuint	point_cnt;
229
230   /* translate GLUT into GL coordinates */
231
232   P[0] = x1;
233   P[1] = height - y1;
234
235   point_cnt = contours[contour_cnt].point_cnt;
236
237   contours[contour_cnt].p[point_cnt][0] = P[0];
238   contours[contour_cnt].p[point_cnt][1] = P[1];
239
240   glBegin( GL_LINES );
241
242   if ( point_cnt ) {
243      glVertex2fv( contours[contour_cnt].p[point_cnt-1] );
244      glVertex2fv( P );
245   } else {
246      glVertex2fv( P );
247      glVertex2fv( P );
248   }
249
250   glEnd();
251   glFinish();
252
253   contours[contour_cnt].point_cnt++;
254}
255
256static void middle_down( int x1, int y1 )
257{
258   GLuint	point_cnt;
259   (void) x1;
260   (void) y1;
261
262   point_cnt = contours[contour_cnt].point_cnt;
263
264   if ( point_cnt > 2 ) {
265      glBegin( GL_LINES );
266
267      glVertex2fv( contours[contour_cnt].p[0] );
268      glVertex2fv( contours[contour_cnt].p[point_cnt-1] );
269
270      contours[contour_cnt].p[point_cnt][0] = -1;
271
272      glEnd();
273      glFinish();
274
275      contour_cnt++;
276      contours[contour_cnt].point_cnt = 0;
277   }
278}
279
280static void mouse_clicked( int button, int state, int x, int y )
281{
282   x -= x%10;
283   y -= y%10;
284
285   switch ( button ) {
286   case GLUT_LEFT_BUTTON:
287      if ( state == GLUT_DOWN ) {
288	 left_down( x, y );
289      }
290      break;
291   case GLUT_MIDDLE_BUTTON:
292      if ( state == GLUT_DOWN ) {
293	 middle_down( x, y );
294      }
295      break;
296   }
297}
298
299static void display( void )
300{
301   GLuint i,j;
302   GLsizei ii, jj;
303   GLuint point_cnt;
304
305   glClear( GL_COLOR_BUFFER_BIT );
306
307   switch ( mode ) {
308   case DEFINE:
309      /* draw grid */
310      glColor3f( 0.6, 0.5, 0.5 );
311
312      glBegin( GL_LINES );
313
314      for ( ii = 0 ; ii < width ; ii += 10 ) {
315	 for ( jj = 0 ; jj < height ; jj += 10 ) {
316	    glVertex2i( 0, jj );
317	    glVertex2i( width, jj );
318	    glVertex2i( ii, height );
319	    glVertex2i( ii, 0 );
320	 }
321      }
322
323      glEnd();
324
325      glColor3f( 1.0, 1.0, 0.0 );
326
327      for ( i = 0 ; i <= contour_cnt ; i++ ) {
328	 point_cnt = contours[i].point_cnt;
329
330	 glBegin( GL_LINES );
331
332	 switch ( point_cnt ) {
333	 case 0:
334	    break;
335	 case 1:
336	    glVertex2fv( contours[i].p[0] );
337	    glVertex2fv( contours[i].p[0] );
338	    break;
339	 case 2:
340	    glVertex2fv( contours[i].p[0] );
341	    glVertex2fv( contours[i].p[1] );
342	    break;
343	 default:
344	    --point_cnt;
345	    for ( j = 0 ; j < point_cnt ; j++ ) {
346	       glVertex2fv( contours[i].p[j] );
347	       glVertex2fv( contours[i].p[j+1] );
348	    }
349	    if ( contours[i].p[j+1][0] == -1 ) {
350	       glVertex2fv( contours[i].p[0] );
351	       glVertex2fv( contours[i].p[j] );
352	    }
353	    break;
354	 }
355
356	 glEnd();
357      }
358
359      glFinish();
360      break;
361
362   case TESSELATED:
363      /* draw triangles */
364      glColor3f( 0.7, 0.7, 0.0 );
365      glCallList( list_start );
366
367      glLineWidth( 2.0 );
368      glCallList( list_start + 1 );
369      glLineWidth( 1.0 );
370
371      glFlush();
372      break;
373   }
374
375   glColor3f( 1.0, 1.0, 0.0 );
376}
377
378static void clear( void )
379{
380   contour_cnt = 0;
381   contours[0].point_cnt = 0;
382   triangle_cnt = 0;
383
384   glutMouseFunc( mouse_clicked );
385
386   mode = DEFINE;
387
388   glDeleteLists( list_start, 2 );
389   list_start = 0;
390}
391
392static void quit( void )
393{
394   exit( 0 );
395}
396
397static void menu_selected( int entry )
398{
399   switch ( entry ) {
400   case CLEAR:
401      clear();
402      break;
403   case TESSELATE:
404      tesse();
405      break;
406   case QUIT:
407      quit();
408      break;
409   }
410
411   glutPostRedisplay();
412}
413
414static void key_pressed( unsigned char key, int x, int y )
415{
416   (void) x;
417   (void) y;
418
419   switch ( key ) {
420   case 'c':
421   case 'C':
422      clear();
423      break;
424   case 't':
425   case 'T':
426      tesse();
427      break;
428   case 27:
429   case 'q':
430   case 'Q':
431      quit();
432      break;
433   }
434
435   glutPostRedisplay();
436}
437
438static void myinit( void )
439{
440   /* clear background to gray */
441   glClearColor( 0.4, 0.4, 0.4, 0.0 );
442   glShadeModel( GL_FLAT );
443   glPolygonMode( GL_FRONT, GL_FILL );
444
445   menu = glutCreateMenu( menu_selected );
446
447   glutAddMenuEntry( "clear", CLEAR );
448   glutAddMenuEntry( "tesselate", TESSELATE );
449   glutAddMenuEntry( "quit", QUIT );
450
451   glutAttachMenu( GLUT_RIGHT_BUTTON );
452
453   glutMouseFunc( mouse_clicked );
454   glutKeyboardFunc( key_pressed );
455
456   contour_cnt = 0;
457   mode = DEFINE;
458}
459
460static void reshape( GLsizei w, GLsizei h )
461{
462   glViewport( 0, 0, w, h );
463
464   glMatrixMode( GL_PROJECTION );
465   glLoadIdentity();
466   glOrtho( 0.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0 );
467
468   glMatrixMode( GL_MODELVIEW );
469   glLoadIdentity();
470
471   set_screen_wh( w, h );
472}
473
474#endif
475
476
477static void usage( void )
478{
479   printf( "Use left mouse button to place vertices.\n" );
480   printf( "Press middle mouse button when done.\n" );
481   printf( "Select tesselate from the pop-up menu.\n" );
482}
483
484
485int main( int argc, char **argv )
486{
487   const char *version = (const char *) gluGetString( GLU_VERSION );
488   printf( "GLU version string: %s\n", version );
489   if ( strstr( version, "1.0" ) || strstr( version, "1.1" ) ) {
490      fprintf( stderr, "Sorry, this demo reqiures GLU 1.2 or later.\n" );
491      exit( 1 );
492   }
493
494   usage();
495
496   glutInitWindowSize( 400, 400 );
497   glutInit( &argc, argv );
498   glutInitDisplayMode( GLUT_SINGLE | GLUT_RGB );
499   glutCreateWindow( argv[0] );
500
501   /* GH: Bit of a hack...
502    */
503#ifdef GLU_VERSION_1_2
504   myinit();
505
506   glutDisplayFunc( display );
507   glutReshapeFunc( reshape );
508
509   glutMainLoop();
510#endif
511
512   return 0;
513}
514