/* * 3-D gear wheels. This program is in the public domain. * * Brian Paul */ /* Conversion to GLUT by Mark J. Kilgard */ /* Conversion to GtkGLExt by Naofumi Yasufuku */ #include #include #include #include #include #include #ifdef G_OS_WIN32 #define WIN32_LEAN_AND_MEAN 1 #include #endif #include #include /* * Draw a gear wheel. You'll probably want to call this function when * building a display list since we do a lot of trig here. * * Input: inner_radius - radius of hole at center * outer_radius - radius at center of teeth * width - width of gear * teeth - number of teeth * tooth_depth - depth of tooth */ static void gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, GLint teeth, GLfloat tooth_depth) { GLint i; GLfloat r0, r1, r2; GLfloat angle, da; GLfloat u, v, len; r0 = inner_radius; r1 = outer_radius - tooth_depth / 2.0; r2 = outer_radius + tooth_depth / 2.0; da = 2.0 * G_PI / teeth / 4.0; glShadeModel(GL_FLAT); glNormal3f(0.0, 0.0, 1.0); /* draw front face */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0 * G_PI / teeth; glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); if (i < teeth) { glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); } } glEnd(); /* draw front sides of teeth */ glBegin(GL_QUADS); da = 2.0 * G_PI / teeth / 4.0; for (i = 0; i < teeth; i++) { angle = i * 2.0 * G_PI / teeth; glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); } glEnd(); glNormal3f(0.0, 0.0, -1.0); /* draw back face */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0 * G_PI / teeth; glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); if (i < teeth) { glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); } } glEnd(); /* draw back sides of teeth */ glBegin(GL_QUADS); da = 2.0 * G_PI / teeth / 4.0; for (i = 0; i < teeth; i++) { angle = i * 2.0 * G_PI / teeth; glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); } glEnd(); /* draw outward faces of teeth */ glBegin(GL_QUAD_STRIP); for (i = 0; i < teeth; i++) { angle = i * 2.0 * G_PI / teeth; glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); u = r2 * cos(angle + da) - r1 * cos(angle); v = r2 * sin(angle + da) - r1 * sin(angle); len = sqrt(u * u + v * v); u /= len; v /= len; glNormal3f(v, -u, 0.0); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); glNormal3f(cos(angle), sin(angle), 0.0); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); glNormal3f(v, -u, 0.0); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); glNormal3f(cos(angle), sin(angle), 0.0); } glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5); glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5); glEnd(); glShadeModel(GL_SMOOTH); /* draw inside radius cylinder */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0 * G_PI / teeth; glNormal3f(-cos(angle), -sin(angle), 0.0); glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); } glEnd(); } static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; static GLint gear1, gear2, gear3; static GLfloat angle = 0.0; static GTimer *timer = NULL; static gint frames = 0; static gboolean is_sync = TRUE; static gboolean draw (GtkWidget *widget, GdkEventExpose *event, gpointer data) { GdkGLContext *glcontext = gtk_widget_get_gl_context (widget); GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget); /*** OpenGL BEGIN ***/ if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) return FALSE; glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix (); glRotatef (view_rotx, 1.0, 0.0, 0.0); glRotatef (view_roty, 0.0, 1.0, 0.0); glRotatef (view_rotz, 0.0, 0.0, 1.0); glPushMatrix (); glTranslatef (-3.0, -2.0, 0.0); glRotatef (angle, 0.0, 0.0, 1.0); glCallList (gear1); glPopMatrix (); glPushMatrix (); glTranslatef (3.1, -2.0, 0.0); glRotatef (-2.0 * angle - 9.0, 0.0, 0.0, 1.0); glCallList (gear2); glPopMatrix (); glPushMatrix (); glTranslatef (-3.1, 4.2, 0.0); glRotatef (-2.0 * angle - 25.0, 0.0, 0.0, 1.0); glCallList (gear3); glPopMatrix (); glPopMatrix (); if (gdk_gl_drawable_is_double_buffered (gldrawable)) gdk_gl_drawable_swap_buffers (gldrawable); else glFlush (); gdk_gl_drawable_gl_end (gldrawable); /*** OpenGL END ***/ frames++; { gdouble seconds = g_timer_elapsed (timer, NULL); if (seconds >= 5.0) { gdouble fps = frames / seconds; g_print ("%d frames in %6.3f seconds = %6.3f FPS\n", frames, seconds, fps); g_timer_reset (timer); frames = 0; } } return TRUE; } /* new window size or exposure */ static gboolean reshape (GtkWidget *widget, GdkEventConfigure *event, gpointer data) { GdkGLContext *glcontext = gtk_widget_get_gl_context (widget); GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget); GLfloat h = (GLfloat) (widget->allocation.height) / (GLfloat) (widget->allocation.width); /*** OpenGL BEGIN ***/ if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) return FALSE; glViewport (0, 0, widget->allocation.width, widget->allocation.height); glMatrixMode (GL_PROJECTION); glLoadIdentity (); glFrustum (-1.0, 1.0, -h, h, 5.0, 60.0); glMatrixMode (GL_MODELVIEW); glLoadIdentity (); glTranslatef (0.0, 0.0, -40.0); gdk_gl_drawable_gl_end (gldrawable); /*** OpenGL END ***/ return TRUE; } static void init(GtkWidget *widget, gpointer data) { GdkGLContext *glcontext = gtk_widget_get_gl_context (widget); GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (widget); static GLfloat pos[4] = {5.0, 5.0, 10.0, 0.0}; static GLfloat red[4] = {0.8, 0.1, 0.0, 1.0}; static GLfloat green[4] = {0.0, 0.8, 0.2, 1.0}; static GLfloat blue[4] = {0.2, 0.2, 1.0, 1.0}; /*** OpenGL BEGIN ***/ if (!gdk_gl_drawable_gl_begin (gldrawable, glcontext)) return; glLightfv (GL_LIGHT0, GL_POSITION, pos); glEnable (GL_CULL_FACE); glEnable (GL_LIGHTING); glEnable (GL_LIGHT0); glEnable (GL_DEPTH_TEST); /* make the gears */ gear1 = glGenLists (1); glNewList (gear1, GL_COMPILE); glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); gear (1.0, 4.0, 1.0, 20, 0.7); glEndList (); gear2 = glGenLists (1); glNewList (gear2, GL_COMPILE); glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); gear (0.5, 2.0, 2.0, 10, 0.7); glEndList (); gear3 = glGenLists (1); glNewList (gear3, GL_COMPILE); glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); gear (1.3, 2.0, 0.5, 10, 0.7); glEndList (); glEnable (GL_NORMALIZE); g_print ("\n"); g_print ("GL_RENDERER = %s\n", (char *) glGetString (GL_RENDERER)); g_print ("GL_VERSION = %s\n", (char *) glGetString (GL_VERSION)); g_print ("GL_VENDOR = %s\n", (char *) glGetString (GL_VENDOR)); g_print ("GL_EXTENSIONS = %s\n", (char *) glGetString (GL_EXTENSIONS)); g_print ("\n"); gdk_gl_drawable_gl_end (gldrawable); /*** OpenGL END ***/ /* create timer */ if (timer == NULL) timer = g_timer_new (); g_timer_start (timer); } static gboolean idle (GtkWidget *widget) { angle += 2.0; /* Invalidate the whole window. */ gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); /* Update synchronously (fast). */ if (is_sync) gdk_window_process_updates (widget->window, FALSE); return TRUE; } static guint idle_id = 0; static void idle_add (GtkWidget *widget) { if (idle_id == 0) { idle_id = g_idle_add_full (GDK_PRIORITY_REDRAW, (GSourceFunc) idle, widget, NULL); } } static void idle_remove (GtkWidget *widget) { if (idle_id != 0) { g_source_remove (idle_id); idle_id = 0; } } static gboolean map (GtkWidget *widget, GdkEventAny *event, gpointer data) { idle_add (widget); return TRUE; } static gboolean unmap (GtkWidget *widget, GdkEventAny *event, gpointer data) { idle_remove (widget); return TRUE; } static gboolean visible (GtkWidget *widget, GdkEventVisibility *event, gpointer data) { if (event->state == GDK_VISIBILITY_FULLY_OBSCURED) idle_remove (widget); else idle_add (widget); return TRUE; } /* change view angle, exit upon ESC */ static gboolean key (GtkWidget *widget, GdkEventKey *event, gpointer data) { switch (event->keyval) { case GDK_z: view_rotz += 5.0; break; case GDK_Z: view_rotz -= 5.0; break; case GDK_Up: view_rotx += 5.0; break; case GDK_Down: view_rotx -= 5.0; break; case GDK_Left: view_roty += 5.0; break; case GDK_Right: view_roty -= 5.0; break; case GDK_Escape: gtk_main_quit (); break; default: return FALSE; } gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE); return TRUE; } int main (int argc, char *argv[]) { GdkGLConfig *glconfig; GtkWidget *window; GtkWidget *vbox; GtkWidget *drawing_area; GtkWidget *button; int i; /* * Init GTK. */ gtk_init (&argc, &argv); /* * Init GtkGLExt. */ gtk_gl_init (&argc, &argv); /* * Command line options. */ for (i = 0; i < argc; i++) { if (strcmp (argv[i], "--async") == 0) is_sync = FALSE; } /* * Configure OpenGL-capable visual. */ /* Try double-buffered visual */ glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE); if (glconfig == NULL) { g_print ("*** Cannot find the double-buffered visual.\n"); g_print ("*** Trying single-buffered visual.\n"); /* Try single-buffered visual */ glconfig = gdk_gl_config_new_by_mode (GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH); if (glconfig == NULL) { g_print ("*** No appropriate OpenGL-capable visual found.\n"); exit (1); } } /* * Top-level window. */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (window), "gears"); /* Get automatically redrawn if any of their children changed allocation. */ gtk_container_set_reallocate_redraws (GTK_CONTAINER (window), TRUE); g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL); /* * VBox. */ vbox = gtk_vbox_new (FALSE, 0); gtk_container_add (GTK_CONTAINER (window), vbox); gtk_widget_show (vbox); /* * Drawing area for drawing OpenGL scene. */ drawing_area = gtk_drawing_area_new (); gtk_widget_set_size_request (drawing_area, 300, 300); /* Set OpenGL-capability to the widget. */ gtk_widget_set_gl_capability (drawing_area, glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE); gtk_widget_add_events (drawing_area, GDK_VISIBILITY_NOTIFY_MASK); g_signal_connect_after (G_OBJECT (drawing_area), "realize", G_CALLBACK (init), NULL); g_signal_connect (G_OBJECT (drawing_area), "configure_event", G_CALLBACK (reshape), NULL); g_signal_connect (G_OBJECT (drawing_area), "expose_event", G_CALLBACK (draw), NULL); g_signal_connect (G_OBJECT (drawing_area), "map_event", G_CALLBACK (map), NULL); g_signal_connect (G_OBJECT (drawing_area), "unmap_event", G_CALLBACK (unmap), NULL); g_signal_connect (G_OBJECT (drawing_area), "visibility_notify_event", G_CALLBACK (visible), NULL); g_signal_connect_swapped (G_OBJECT (window), "key_press_event", G_CALLBACK (key), drawing_area); gtk_box_pack_start (GTK_BOX (vbox), drawing_area, TRUE, TRUE, 0); gtk_widget_show (drawing_area); /* * Simple quit button. */ button = gtk_button_new_with_label ("Quit"); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (gtk_main_quit), NULL); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); gtk_widget_show (button); /* * Show window. */ gtk_widget_show (window); /* * Main loop. */ gtk_main (); return 0; }