]> Pileus Git - aweather/blobdiff - src/gears.c
OpenGL debugging
[aweather] / src / gears.c
diff --git a/src/gears.c b/src/gears.c
new file mode 100644 (file)
index 0000000..e41754f
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <gtk/gtkgl.h>
+
+#ifdef G_OS_WIN32
+#define WIN32_LEAN_AND_MEAN 1
+#include <windows.h>
+#endif
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+/*
+ * 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;
+}