diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2015-02-05 16:39:53 +0000 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2015-02-09 19:10:30 +0000 |
commit | 01d1cdc76c6454c74884bd56f06fa3b8accac7fd (patch) | |
tree | 375beec332777f210d7d32cae1963c9df6e255ff /demos/gtk-demo/glarea.c | |
parent | b87034a985523f71783c1f0b16a27da8a6738de5 (diff) | |
download | gtk+-01d1cdc76c6454c74884bd56f06fa3b8accac7fd.tar.gz |
demos: Update the GtkGLArea demo code
Same way we updated the testglarea test code.
https://bugzilla.gnome.org/show_bug.cgi?id=741946
Diffstat (limited to 'demos/gtk-demo/glarea.c')
-rw-r--r-- | 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 8ac5b3771b..548c75fa9f 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); |