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