1 /*
2  * 3-D gear wheels.  This program is in the public domain.
3  *
4  * Brian Paul
5  */
6 
7 /* Conversion to GLUT by Mark J. Kilgard */
8 
9 /* Conversion to GtkGLExt by Naofumi Yasufuku */
10 
11 #include <stdlib.h>
12 #include <string.h>
13 #include <math.h>
14 
15 #include <gtk/gtk.h>
16 #include <gdk/gdkkeysyms.h>
17 
18 #include <gtk/gtkgl.h>
19 
20 #ifdef G_OS_WIN32
21 #define WIN32_LEAN_AND_MEAN 1
22 #include <windows.h>
23 #endif
24 
25 #include <GL/gl.h>
26 #include <GL/glu.h>
27 
28 /*
29  * Draw a gear wheel.  You'll probably want to call this function when
30  * building a display list since we do a lot of trig here.
31  *
32  * Input:  inner_radius - radius of hole at center
33  * outer_radius - radius at center of teeth
34  * width - width of gear
35  * teeth - number of teeth
36  * tooth_depth - depth of tooth
37  */
38 
39 static void
gear(GLfloat inner_radius,GLfloat outer_radius,GLfloat width,GLint teeth,GLfloat tooth_depth)40 gear(GLfloat inner_radius,
41      GLfloat outer_radius,
42      GLfloat width,
43      GLint   teeth,
44      GLfloat tooth_depth)
45 {
46   GLint i;
47   GLfloat r0, r1, r2;
48   GLfloat angle, da;
49   GLfloat u, v, len;
50 
51   r0 = inner_radius;
52   r1 = outer_radius - tooth_depth / 2.0;
53   r2 = outer_radius + tooth_depth / 2.0;
54 
55   da = 2.0 * G_PI / teeth / 4.0;
56 
57   glShadeModel(GL_FLAT);
58 
59   glNormal3f(0.0, 0.0, 1.0);
60 
61   /* draw front face */
62   glBegin(GL_QUAD_STRIP);
63   for (i = 0; i <= teeth; i++) {
64     angle = i * 2.0 * G_PI / teeth;
65     glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
66     glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
67     if (i < teeth) {
68       glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
69       glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
70     }
71   }
72   glEnd();
73 
74   /* draw front sides of teeth */
75   glBegin(GL_QUADS);
76   da = 2.0 * G_PI / teeth / 4.0;
77   for (i = 0; i < teeth; i++) {
78     angle = i * 2.0 * G_PI / teeth;
79 
80     glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
81     glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
82     glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
83     glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
84   }
85   glEnd();
86 
87   glNormal3f(0.0, 0.0, -1.0);
88 
89   /* draw back face */
90   glBegin(GL_QUAD_STRIP);
91   for (i = 0; i <= teeth; i++) {
92     angle = i * 2.0 * G_PI / teeth;
93     glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
94     glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
95     if (i < teeth) {
96       glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
97       glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
98     }
99   }
100   glEnd();
101 
102   /* draw back sides of teeth */
103   glBegin(GL_QUADS);
104   da = 2.0 * G_PI / teeth / 4.0;
105   for (i = 0; i < teeth; i++) {
106     angle = i * 2.0 * G_PI / teeth;
107 
108     glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
109     glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
110     glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
111     glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
112   }
113   glEnd();
114 
115   /* draw outward faces of teeth */
116   glBegin(GL_QUAD_STRIP);
117   for (i = 0; i < teeth; i++) {
118     angle = i * 2.0 * G_PI / teeth;
119 
120     glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
121     glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
122     u = r2 * cos(angle + da) - r1 * cos(angle);
123     v = r2 * sin(angle + da) - r1 * sin(angle);
124     len = sqrt(u * u + v * v);
125     u /= len;
126     v /= len;
127     glNormal3f(v, -u, 0.0);
128     glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
129     glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
130     glNormal3f(cos(angle), sin(angle), 0.0);
131     glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5);
132     glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5);
133     u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
134     v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
135     glNormal3f(v, -u, 0.0);
136     glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5);
137     glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5);
138     glNormal3f(cos(angle), sin(angle), 0.0);
139   }
140 
141   glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5);
142   glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5);
143 
144   glEnd();
145 
146   glShadeModel(GL_SMOOTH);
147 
148   /* draw inside radius cylinder */
149   glBegin(GL_QUAD_STRIP);
150   for (i = 0; i <= teeth; i++) {
151     angle = i * 2.0 * G_PI / teeth;
152     glNormal3f(-cos(angle), -sin(angle), 0.0);
153     glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
154     glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
155   }
156   glEnd();
157 
158 }
159 
160 static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0;
161 static GLint gear1, gear2, gear3;
162 static GLfloat angle = 0.0;
163 
164 static GTimer *timer = NULL;
165 static gint frames = 0;
166 
167 static gboolean is_sync = TRUE;
168 
169 static gboolean
draw(GtkWidget * widget,GdkEventExpose * event,gpointer data)170 draw (GtkWidget      *widget,
171       GdkEventExpose *event,
172       gpointer        data)
173 {
174   GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
175   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
176 
177   /*** OpenGL BEGIN ***/
178   if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
179     return FALSE;
180 
181   glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
182 
183   glPushMatrix ();
184     glRotatef (view_rotx, 1.0, 0.0, 0.0);
185     glRotatef (view_roty, 0.0, 1.0, 0.0);
186     glRotatef (view_rotz, 0.0, 0.0, 1.0);
187 
188     glPushMatrix ();
189       glTranslatef (-3.0, -2.0, 0.0);
190       glRotatef (angle, 0.0, 0.0, 1.0);
191       glCallList (gear1);
192     glPopMatrix ();
193 
194     glPushMatrix ();
195       glTranslatef (3.1, -2.0, 0.0);
196       glRotatef (-2.0 * angle - 9.0, 0.0, 0.0, 1.0);
197       glCallList (gear2);
198     glPopMatrix ();
199 
200     glPushMatrix ();
201       glTranslatef (-3.1, 4.2, 0.0);
202       glRotatef (-2.0 * angle - 25.0, 0.0, 0.0, 1.0);
203       glCallList (gear3);
204     glPopMatrix ();
205 
206   glPopMatrix ();
207 
208   if (gdk_gl_drawable_is_double_buffered (gldrawable))
209     gdk_gl_drawable_swap_buffers (gldrawable);
210   else
211     glFlush ();
212 
213   gdk_gl_drawable_gl_end (gldrawable);
214   /*** OpenGL END ***/
215 
216   frames++;
217 
218   {
219     gdouble seconds = g_timer_elapsed (timer, NULL);
220     if (seconds >= 5.0) {
221       gdouble fps = frames / seconds;
222       g_print ("%d frames in %6.3f seconds = %6.3f FPS\n", frames, seconds, fps);
223       g_timer_reset (timer);
224       frames = 0;
225     }
226   }
227 
228   return TRUE;
229 }
230 
231 /* new window size or exposure */
232 static gboolean
reshape(GtkWidget * widget,GdkEventConfigure * event,gpointer data)233 reshape (GtkWidget         *widget,
234 	 GdkEventConfigure *event,
235 	 gpointer           data)
236 {
237   GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
238   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
239 
240   GLfloat h = (GLfloat) (widget->allocation.height) / (GLfloat) (widget->allocation.width);
241 
242   /*** OpenGL BEGIN ***/
243   if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
244     return FALSE;
245 
246   glViewport (0, 0, widget->allocation.width, widget->allocation.height);
247   glMatrixMode (GL_PROJECTION);
248   glLoadIdentity ();
249   glFrustum (-1.0, 1.0, -h, h, 5.0, 60.0);
250   glMatrixMode (GL_MODELVIEW);
251   glLoadIdentity ();
252   glTranslatef (0.0, 0.0, -40.0);
253 
254   gdk_gl_drawable_gl_end (gldrawable);
255   /*** OpenGL END ***/
256 
257   return TRUE;
258 }
259 
260 static void
init(GtkWidget * widget,gpointer data)261 init(GtkWidget *widget,
262      gpointer   data)
263 {
264   GdkGLContext *glcontext = gtk_widget_get_gl_context (widget);
265   GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget);
266 
267   static GLfloat pos[4] = {5.0, 5.0, 10.0, 0.0};
268   static GLfloat red[4] = {0.8, 0.1, 0.0, 1.0};
269   static GLfloat green[4] = {0.0, 0.8, 0.2, 1.0};
270   static GLfloat blue[4] = {0.2, 0.2, 1.0, 1.0};
271 
272   /*** OpenGL BEGIN ***/
273   if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext))
274     return;
275 
276   glLightfv (GL_LIGHT0, GL_POSITION, pos);
277   glEnable (GL_CULL_FACE);
278   glEnable (GL_LIGHTING);
279   glEnable (GL_LIGHT0);
280   glEnable (GL_DEPTH_TEST);
281 
282   /* make the gears */
283   gear1 = glGenLists (1);
284   glNewList (gear1, GL_COMPILE);
285     glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
286     gear (1.0, 4.0, 1.0, 20, 0.7);
287   glEndList ();
288 
289   gear2 = glGenLists (1);
290   glNewList (gear2, GL_COMPILE);
291     glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
292     gear (0.5, 2.0, 2.0, 10, 0.7);
293   glEndList ();
294 
295   gear3 = glGenLists (1);
296   glNewList (gear3, GL_COMPILE);
297     glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
298     gear (1.3, 2.0, 0.5, 10, 0.7);
299   glEndList ();
300 
301   glEnable (GL_NORMALIZE);
302 
303   g_print ("\n");
304   g_print ("GL_RENDERER   = %s\n", (char *) glGetString (GL_RENDERER));
305   g_print ("GL_VERSION    = %s\n", (char *) glGetString (GL_VERSION));
306   g_print ("GL_VENDOR     = %s\n", (char *) glGetString (GL_VENDOR));
307   g_print ("GL_EXTENSIONS = %s\n", (char *) glGetString (GL_EXTENSIONS));
308   g_print ("\n");
309 
310   gdk_gl_drawable_gl_end (gldrawable);
311   /*** OpenGL END ***/
312 
313   /* create timer */
314   if (timer == NULL)
315     timer = g_timer_new ();
316 
317   g_timer_start (timer);
318 }
319 
320 static gboolean
idle(GtkWidget * widget)321 idle (GtkWidget *widget)
322 {
323   angle += 2.0;
324 
325   /* Invalidate the whole window. */
326   gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
327 
328   /* Update synchronously (fast). */
329   if (is_sync)
330     gdk_window_process_updates (widget->window, FALSE);
331 
332   return TRUE;
333 }
334 
335 static guint idle_id = 0;
336 
337 static void
idle_add(GtkWidget * widget)338 idle_add (GtkWidget *widget)
339 {
340   if (idle_id == 0)
341     {
342       idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW,
343                                  (GSourceFunc) idle,
344                                  widget,
345                                  NULL);
346     }
347 }
348 
349 static void
idle_remove(GtkWidget * widget)350 idle_remove (GtkWidget *widget)
351 {
352   if (idle_id != 0)
353     {
354       g_source_remove (idle_id);
355       idle_id = 0;
356     }
357 }
358 
359 static gboolean
map(GtkWidget * widget,GdkEventAny * event,gpointer data)360 map (GtkWidget   *widget,
361      GdkEventAny *event,
362      gpointer     data)
363 {
364   idle_add (widget);
365 
366   return TRUE;
367 }
368 
369 static gboolean
unmap(GtkWidget * widget,GdkEventAny * event,gpointer data)370 unmap (GtkWidget   *widget,
371        GdkEventAny *event,
372        gpointer     data)
373 {
374   idle_remove (widget);
375 
376   return TRUE;
377 }
378 
379 static gboolean
visible(GtkWidget * widget,GdkEventVisibility * event,gpointer data)380 visible (GtkWidget          *widget,
381 	 GdkEventVisibility *event,
382 	 gpointer            data)
383 {
384   if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
385     idle_remove (widget);
386   else
387     idle_add (widget);
388 
389   return TRUE;
390 }
391 
392 /* change view angle, exit upon ESC */
393 static gboolean
key(GtkWidget * widget,GdkEventKey * event,gpointer data)394 key (GtkWidget   *widget,
395      GdkEventKey *event,
396      gpointer     data)
397 {
398   switch (event->keyval)
399     {
400     case GDK_z:
401       view_rotz += 5.0;
402       break;
403     case GDK_Z:
404       view_rotz -= 5.0;
405       break;
406     case GDK_Up:
407       view_rotx += 5.0;
408       break;
409     case GDK_Down:
410       view_rotx -= 5.0;
411       break;
412     case GDK_Left:
413       view_roty += 5.0;
414       break;
415     case GDK_Right:
416       view_roty -= 5.0;
417       break;
418     case GDK_Escape:
419       gtk_main_quit ();
420       break;
421     default:
422       return FALSE;
423     }
424 
425   gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
426 
427   return TRUE;
428 }
429 
430 int
main(int argc,char * argv[])431 main (int   argc,
432       char *argv[])
433 {
434   GdkGLConfig *glconfig;
435   GtkWidget *window;
436   GtkWidget *vbox;
437   GtkWidget *drawing_area;
438   GtkWidget *button;
439   int i;
440 
441   /*
442    * Init GTK.
443    */
444 
445   gtk_init (&argc, &argv);
446 
447   /*
448    * Init GtkGLExt.
449    */
450 
451   gtk_gl_init (&argc, &argv);
452 
453   /*
454    * Command line options.
455    */
456 
457   for (i = 0; i < argc; i++)
458     {
459       if (strcmp (argv[i], "--async") == 0)
460         is_sync = FALSE;
461     }
462 
463   /*
464    * Configure OpenGL-capable visual.
465    */
466 
467   /* Try double-buffered visual */
468   glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB    |
469 					GDK_GL_MODE_DEPTH  |
470 					GDK_GL_MODE_DOUBLE);
471   if (glconfig == NULL)
472     {
473       g_print ("*** Cannot find the double-buffered visual.\n");
474       g_print ("*** Trying single-buffered visual.\n");
475 
476       /* Try single-buffered visual */
477       glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB   |
478 					    GDK_GL_MODE_DEPTH);
479       if (glconfig == NULL)
480 	{
481 	  g_print ("*** No appropriate OpenGL-capable visual found.\n");
482 	  exit (1);
483 	}
484     }
485 
486   /*
487    * Top-level window.
488    */
489 
490   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
491   gtk_window_set_title (GTK_WINDOW (window), "gears");
492 
493   /* Get automatically redrawn if any of their children changed allocation. */
494   gtk_container_set_reallocate_redraws (GTK_CONTAINER (window), TRUE);
495 
496   g_signal_connect (G_OBJECT (window), "delete_event",
497 		    G_CALLBACK (gtk_main_quit), NULL);
498 
499   /*
500    * VBox.
501    */
502 
503   vbox = gtk_vbox_new (FALSE, 0);
504   gtk_container_add (GTK_CONTAINER (window), vbox);
505   gtk_widget_show (vbox);
506 
507   /*
508    * Drawing area for drawing OpenGL scene.
509    */
510 
511   drawing_area = gtk_drawing_area_new ();
512   gtk_widget_set_size_request (drawing_area, 300, 300);
513 
514   /* Set OpenGL-capability to the widget. */
515   gtk_widget_set_gl_capability (drawing_area,
516 				glconfig,
517 				NULL,
518 				TRUE,
519 				GDK_GL_RGBA_TYPE);
520 
521   gtk_widget_add_events (drawing_area,
522 			 GDK_VISIBILITY_NOTIFY_MASK);
523 
524   g_signal_connect_after (G_OBJECT (drawing_area), "realize",
525                           G_CALLBACK (init), NULL);
526   g_signal_connect (G_OBJECT (drawing_area), "configure_event",
527 		    G_CALLBACK (reshape), NULL);
528   g_signal_connect (G_OBJECT (drawing_area), "expose_event",
529 		    G_CALLBACK (draw), NULL);
530   g_signal_connect (G_OBJECT (drawing_area), "map_event",
531 		    G_CALLBACK (map), NULL);
532   g_signal_connect (G_OBJECT (drawing_area), "unmap_event",
533 		    G_CALLBACK (unmap), NULL);
534   g_signal_connect (G_OBJECT (drawing_area), "visibility_notify_event",
535 		    G_CALLBACK (visible), NULL);
536 
537   g_signal_connect_swapped (G_OBJECT (window), "key_press_event",
538 			    G_CALLBACK (key), drawing_area);
539 
540   gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0);
541 
542   gtk_widget_show (drawing_area);
543 
544   /*
545    * Simple quit button.
546    */
547 
548   button = gtk_button_new_with_label ("Quit");
549 
550   g_signal_connect (G_OBJECT (button), "clicked",
551 		    G_CALLBACK (gtk_main_quit), NULL);
552 
553   gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
554 
555   gtk_widget_show (button);
556 
557   /*
558    * Show window.
559    */
560 
561   gtk_widget_show (window);
562 
563   /*
564    * Main loop.
565    */
566 
567   gtk_main ();
568 
569   return 0;
570 }
571