[gtk+] demos: Update the GtkGLArea demo code



commit 01d1cdc76c6454c74884bd56f06fa3b8accac7fd
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Thu Feb 5 16:39:53 2015 +0000

    demos: Update the GtkGLArea demo code
    
    Same way we updated the testglarea test code.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=741946

 demos/gtk-demo/glarea.c |  280 ++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 263 insertions(+), 17 deletions(-)
---
diff --git a/demos/gtk-demo/glarea.c b/demos/gtk-demo/glarea.c
index 8ac5b37..548c75f 100644
--- a/demos/gtk-demo/glarea.c
+++ b/demos/gtk-demo/glarea.c
@@ -3,6 +3,7 @@
  * GtkGLArea is a widget that allows custom drawing using OpenGL calls.
  */
 
+#include <math.h>
 #include <gtk/gtk.h>
 #include <epoxy/gl.h>
 
@@ -23,35 +24,274 @@ enum {
 static float rotation_angles[N_AXIS] = { 0.0 };
 
 /* The object we are drawing */
+static const GLfloat vertex_data[] = {
+  0.f,   0.5f,   0.f, 1.f,
+  0.5f, -0.366f, 0.f, 1.f,
+ -0.5f, -0.366f, 0.f, 1.f,
+};
+
+/* Initialize the GL buffers */
+static void
+init_buffers (GLuint *vao_out,
+              GLuint *buffer_out)
+{
+  GLuint vao, buffer;
+
+  /* We only use one VAO, so we always keep it bound */
+  glGenVertexArrays (1, &vao);
+  glBindVertexArray (vao);
+
+  /* This is the buffer that holds the vertices */
+  glGenBuffers (1, &buffer);
+  glBindBuffer (GL_ARRAY_BUFFER, buffer);
+  glBufferData (GL_ARRAY_BUFFER, sizeof (vertex_data), vertex_data, GL_STATIC_DRAW);
+  glBindBuffer (GL_ARRAY_BUFFER, 0);
+
+  if (vao_out != NULL)
+    *vao_out = vao;
+
+  if (buffer_out != NULL)
+    *buffer_out = buffer;
+}
+
+/* Create and compile a shader */
+static GLuint
+create_shader (int         type,
+               const char *src)
+{
+  GLuint shader;
+  int status;
+
+  shader = glCreateShader (type);
+  glShaderSource (shader, 1, &src, NULL);
+  glCompileShader (shader);
+
+  glGetShaderiv (shader, GL_COMPILE_STATUS, &status);
+  if (status == GL_FALSE)
+    {
+      int log_len;
+      char *buffer;
+
+      glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &log_len);
+
+      buffer = g_malloc (log_len + 1);
+      glGetShaderInfoLog (shader, log_len, NULL, buffer);
+
+      g_warning ("Compile failure in %s shader:\n%s\n",
+                 type == GL_VERTEX_SHADER ? "vertex" : "fragment",
+                 buffer);
+
+      g_free (buffer);
+
+      glDeleteShader (shader);
+
+      return 0;
+    }
+
+  return shader;
+}
+
+/* The code for the vertex shader we are going to use */
+static const char *vertex_shader_code =
+"#version 330\n" \
+"\n" \
+"layout(location = 0) in vec4 position;\n" \
+"uniform mat4 mvp;\n"
+"void main() {\n" \
+"  gl_Position = mvp * position;\n" \
+"}";
+
+/* The code for the fragment shader we are going to use */
+static const char *fragment_shader_code =
+"#version 330\n" \
+"\n" \
+"out vec4 outputColor;\n" \
+"void main() {\n" \
+"  float lerpVal = gl_FragCoord.y / 400.0f;\n" \
+"  outputColor = mix(vec4(1.0f, 0.85f, 0.35f, 1.0f), vec4(0.2f, 0.2f, 0.2f, 1.0f), lerpVal);\n" \
+"}";
+
+/* Initialize the shaders and link them into a program */
+static void
+init_shaders (GLuint *program_out,
+              GLuint *mvp_out)
+{
+  GLuint vertex, fragment;
+  GLuint program = 0;
+  GLuint mvp = 0;
+  int status;
+
+  vertex = create_shader (GL_VERTEX_SHADER, vertex_shader_code);
+  if (vertex == 0)
+    {
+      *program_out = 0;
+      return;
+    }
+
+  fragment = create_shader (GL_FRAGMENT_SHADER, fragment_shader_code);
+  if (fragment == 0)
+    {
+      glDeleteShader (vertex);
+      *program_out = 0;
+      return;
+    }
+
+  program = glCreateProgram ();
+  glAttachShader (program, vertex);
+  glAttachShader (program, fragment);
+
+  glLinkProgram (program);
+
+  glGetProgramiv (program, GL_LINK_STATUS, &status);
+  if (status == GL_FALSE)
+    {
+      int log_len;
+      char *buffer;
+
+      glGetProgramiv (program, GL_INFO_LOG_LENGTH, &log_len);
+
+      buffer = g_malloc (log_len + 1);
+      glGetProgramInfoLog (program, log_len, NULL, buffer);
+
+      g_warning ("Linking failure:\n%s\n", buffer);
+
+      g_free (buffer);
+
+      glDeleteProgram (program);
+      program = 0;
+
+      goto out;
+    }
+
+  /* Get the location of the "mvp" uniform */
+  mvp = glGetUniformLocation (program, "mvp");
+
+  glDetachShader (program, vertex);
+  glDetachShader (program, fragment);
+
+out:
+  glDeleteShader (vertex);
+  glDeleteShader (fragment);
+
+  if (program_out != NULL)
+    *program_out = program;
+
+  if (mvp_out != NULL)
+    *mvp_out = mvp;
+}
+
+static void
+compute_mvp (float *res,
+             float  phi,
+             float  theta,
+             float  psi)
+{
+  float x = phi * (G_PI / 180.f);
+  float y = theta * (G_PI / 180.f);
+  float z = psi * (G_PI / 180.f);
+  float c1 = cosf (x), s1 = sinf (x);
+  float c2 = cosf (y), s2 = sinf (y);
+  float c3 = cosf (z), s3 = sinf (z);
+  float c3c2 = c3 * c2;
+  float s3c1 = s3 * c1;
+  float c3s2s1 = c3 * s2 * s1;
+  float s3s1 = s3 * s1;
+  float c3s2c1 = c3 * s2 * c1;
+  float s3c2 = s3 * c2;
+  float c3c1 = c3 * c1;
+  float s3s2s1 = s3 * s2 * s1;
+  float c3s1 = c3 * s1;
+  float s3s2c1 = s3 * s2 * c1;
+  float c2s1 = c2 * s1;
+  float c2c1 = c2 * c1;
+
+  /* initialize to the identity matrix */
+  res[0] = 1.f; res[4] = 0.f;  res[8] = 0.f; res[12] = 0.f;
+  res[1] = 0.f; res[5] = 1.f;  res[9] = 0.f; res[13] = 0.f;
+  res[2] = 0.f; res[6] = 0.f; res[10] = 1.f; res[14] = 0.f;
+  res[3] = 0.f; res[7] = 0.f; res[11] = 0.f; res[15] = 1.f;
+
+  /* apply all three rotations using the three matrices:
+   *
+   * ⎡  c3 s3 0 ⎤ ⎡ c2  0 -s2 ⎤ ⎡ 1   0  0 ⎤
+   * ⎢ -s3 c3 0 ⎥ ⎢  0  1   0 ⎥ ⎢ 0  c1 s1 ⎥
+   * ⎣   0  0 1 ⎦ ⎣ s2  0  c2 ⎦ ⎣ 0 -s1 c1 ⎦
+   */
+  res[0] = c3c2;  res[4] = s3c1 + c3s2s1;  res[8] = s3s1 - c3s2c1; res[12] = 0.f;
+  res[1] = -s3c2; res[5] = c3c1 - s3s2s1;  res[9] = c3s1 + s3s2c1; res[13] = 0.f;
+  res[2] = s2;    res[6] = -c2s1;         res[10] = c2c1;          res[14] = 0.f;
+  res[3] = 0.f;   res[7] = 0.f;           res[11] = 0.f;           res[15] = 1.f;
+}
+
+static GLuint position_buffer;
+static GLuint program;
+static GLuint mvp_location;
+
+/* We need to set up our state when we realize the GtkGLArea widget */
+static void
+realize (GtkWidget *widget)
+{
+  gtk_gl_area_make_current (GTK_GL_AREA (widget));
+
+  init_buffers (&position_buffer, NULL);
+  init_shaders (&program, &mvp_location);
+}
+
+/* We should tear down the state when unrealizing */
+static void
+unrealize (GtkWidget *widget)
+{
+  gtk_gl_area_make_current (GTK_GL_AREA (widget));
+
+  glDeleteBuffers (1, &position_buffer);
+  glDeleteProgram (program);
+}
+
 static void
 draw_triangle (void)
 {
-  glColor3f (1.0f, 0.85f, 0.35f);
-  glBegin (GL_TRIANGLES);
-  {
-    glVertex3f ( 0.0,  0.6,  0.0);
-    glVertex3f (-0.2, -0.3,  0.0);
-    glVertex3f ( 0.2, -0.3,  0.0);
-  }
-  glEnd ();
+  float mvp[16];
+
+  /* Compute the model view projection matrix using the
+   * rotation angles specified through the GtkRange widgets
+   */
+  compute_mvp (mvp,
+               rotation_angles[X_AXIS],
+               rotation_angles[Y_AXIS],
+               rotation_angles[Z_AXIS]);
+
+  /* Use our shaders */
+  glUseProgram (program);
+
+  /* Update the "mvp" matrix we use in the shader */
+  glUniformMatrix4fv (mvp_location, 1, GL_FALSE, &mvp[0]);
+
+  /* Use the vertices in our buffer */
+  glBindBuffer (GL_ARRAY_BUFFER, position_buffer);
+  glEnableVertexAttribArray (0);
+  glVertexAttribPointer (0, 4, GL_FLOAT, GL_FALSE, 0, 0);
+
+  /* Draw the three vertices as a triangle */
+  glDrawArrays (GL_TRIANGLES, 0, 3);
+
+  /* We finished using the buffers and program */
+  glDisableVertexAttribArray (0);
+  glBindBuffer (GL_ARRAY_BUFFER, 0);
+  glUseProgram (0);
 }
 
-/* The main rendering callback */
 static gboolean
 render (GtkGLArea    *area,
         GdkGLContext *context)
 {
+  /* Clear the viewport */
   glClearColor (0.5, 0.5, 0.5, 1.0);
   glClear (GL_COLOR_BUFFER_BIT);
 
-  glMatrixMode (GL_MODELVIEW);
-  glLoadIdentity ();
-  glRotatef (rotation_angles[X_AXIS], 1, 0, 0);
-  glRotatef (rotation_angles[Y_AXIS], 0, 1, 0);
-  glRotatef (rotation_angles[Z_AXIS], 0, 0, 1);
-
+  /* Draw our object */
   draw_triangle ();
 
+  /* Flush the contents of the pipeline */
   glFlush ();
 
   return TRUE;
@@ -138,7 +378,7 @@ create_glarea_window (GtkWidget *do_widget)
   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (do_widget));
   gtk_window_set_title (GTK_WINDOW (window), "GtkGLArea - Golden Triangle");
-  gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
+  gtk_window_set_default_size (GTK_WINDOW (window), 400, 600);
   gtk_container_set_border_width (GTK_CONTAINER (window), 12);
   g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
 
@@ -151,7 +391,13 @@ create_glarea_window (GtkWidget *do_widget)
   gtk_widget_set_vexpand (gl_area, TRUE);
   gtk_container_add (GTK_CONTAINER (box), gl_area);
 
-  /* the main "draw" call for GtkGLArea */
+  /* We need to initialize and free GL resources, so we use
+   * the realize and unrealize signals on the widget
+   */
+  g_signal_connect (gl_area, "realize", G_CALLBACK (realize), NULL);
+  g_signal_connect (gl_area, "unrealize", G_CALLBACK (unrealize), NULL);
+
+  /* The main "draw" call for GtkGLArea */
   g_signal_connect (gl_area, "render", G_CALLBACK (render), NULL);
 
   controls = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]