Vala, OpenGL and Gtk+ sample code with shaders on Gnu/Linux

This is the follow up post for the discoveries which I was doing for myself to unify OpenGL support with Gtk+. Here, you can find a first post, I have shared towards this direction.
The content of the trivial shader program: vertex-shader.txt

#version 130

attribute vec4 vPosition;

void main () {
	gl_Position = vPosition;
}

Here,
fragment-shader.txt

#version 130

precision mediump float;
uniform vec4 vColor;

void main () {
	gl_FragColor = vColor;
}
using GL;

namespace Evg {
	public class Shader {
		private bool loaded;
		private uint type;
		private uint id;

		public Shader () {
			this.loaded = false;
			this.id = 0;
		}

		~Shader () {
			this.delete ();
		}

		public bool load_from_string (string shader, uint type) {
			this.id = glCreateShader (type);

			stdout.printf ("id is %u\n", id);

			glShaderSource (id, shader);
			glCompileShader (id);

			int status;
			glGetShaderiv (id, GL_COMPILE_STATUS, out status);

			if (status == GL_FALSE) {
				stdout.printf ("shader info log: '%s'\n", glGetShaderInfoLog (this.id));
				return false;
			}

			this.type = type;
			this.loaded = true;

			return true;
		}

		public bool load_from_file (string path, uint type) {
			File file = File.new_for_path (path);

			if (!file.query_exists ()) {
				stderr.printf ("File '%s' doesn't exist.\n", file.get_path ());
				return false;
			}

			try {
				DataInputStream dis = new DataInputStream (file.read ());
				StringBuilder sb = new StringBuilder ();
				string line;

				while ((line = dis.read_line ()) != null) {
					sb.append_printf ("%s\n", line);
				}

				return this.load_from_string (sb.str, type);
			} catch (Error e) {
				error ("%s", e.message);
			}
		}

		public bool is_loaded () {
			return this.loaded;
		}

		public uint get_id () {
			return this.id;
		}

		public void delete () {
			if (!this.loaded) return;
			this.loaded = false;
			glDeleteShader (this.id);
		}
	}
}
using GL;

namespace Evg {
	public class Program {
		private bool linked;
		private uint id;

		public Program () {
			this.id = 0;
			this.linked = false;
		}

		public uint get_id () {
			return this.id;
		}

		public bool create () {
			this.id = glCreateProgram ();
			return (this.id != 0);
		}

		public bool attach_shader (Evg.Shader shader) {
			if (!shader.is_loaded ())
				return false;

			glAttachShader (this.id, shader.get_id ());
			return true;
		}

		public bool is_linked () {
			return this.linked;
		}

		public bool is_created () {
			return (this.id != 0);
		}

		public bool link () {
			glLinkProgram (this.id);

			int status;
			glGetProgramiv (this.id, GL_LINK_STATUS, out status);
			this.linked = (status == GL_TRUE);

			if (!this.linked) {
				stdout.printf ("program info log: '%s'\n", glGetProgramInfoLog (this.id));
			}

			return this.linked;
		}

		public void use () {
			if (this.linked)
				glUseProgram (this.id);
		}

		public void delete () {
			if (!this.linked)
				return;

			this.linked = false;
			glDeleteProgram (this.id);
			this.id = 0;
		}

		~Program () {
			this.delete ();
		}
	}
}
using GLX;
using GL;

class GLXSample : Gtk.Window {

    private unowned X.Display xdisplay;
    private GLX.Context context;
    private GLX.XVisualInfo xvinfo;

	private Evg.Shader vertex_shader;
	private Evg.Shader fragment_shader;
	private Evg.Program program;
	private Triangle triangle;

    public GLXSample () {
        this.title = "OpenGL with GLX";
        this.set_reallocate_redraws (true);
        this.destroy.connect (Gtk.main_quit);

        int[] attrlist = {
            GLX_RGBA,
            GLX_RED_SIZE, 1,
            GLX_GREEN_SIZE, 1,
            GLX_BLUE_SIZE, 1,
            GLX_DOUBLEBUFFER, 0
        };

        this.xdisplay = Gdk.x11_get_default_xdisplay ();

        if (!glXQueryExtension (xdisplay, null, null)) {
            stderr.printf ("OpenGL not supported\n");
        }

        this.xvinfo = glXChooseVisual (xdisplay,
									   Gdk.x11_get_default_screen (),
									   attrlist );

        if (xvinfo == null) {
            stderr.printf ("Error configuring OpenGL\n");
        }

        var drawing_area = new Gtk.DrawingArea ();
        drawing_area.set_size_request (300, 300);
        drawing_area.set_double_buffered (false);

        this.context = glXCreateContext (xdisplay, xvinfo, null, true);

        drawing_area.configure_event.connect (on_configure_event);
		drawing_area.realize.connect (on_realize);
        drawing_area.draw.connect (on_draw);

        this.add (drawing_area);
    }

	private void on_realize (Gtk.Widget widget) {
		if (!glXMakeCurrent (xdisplay,
							 Gdk.X11Window.get_xid (widget.get_window ()),
							 context))
            return;

		stdout.printf ("Vendor: ........... %s\n", glGetString (GL_VENDOR));
		stdout.printf ("Renderer: ......... %s\n", glGetString (GL_RENDERER));
		stdout.printf ("Version: .......... %s\n", glGetString (GL_VERSION));
		stdout.printf ("GLSL version: ..... %s\n",
					   glGetString (GL_SHADING_LANGUAGE_VERSION));

		this.vertex_shader = new Evg.Shader ();

		stdout.printf ("loading vertex shader\n");
		vertex_shader.load_from_file ("vertex-shader.txt", GL_VERTEX_SHADER);
		stdout.printf ("vertex shader is loaded %s\n",
					   vertex_shader.is_loaded ().to_string ());

		this.fragment_shader = new Evg.Shader ();

		stdout.printf ("loading fragment shader\n");
		fragment_shader.load_from_file ("fragment-shader.txt", GL_FRAGMENT_SHADER);
		stdout.printf ("fragment shader is loaded %s\n",
					   fragment_shader.is_loaded ().to_string ());

		this.program = new Evg.Program ();
		this.program.create ();
		stdout.printf ("program is created %s\n", program.is_created ().to_string ());
		this.program.attach_shader (this.vertex_shader);
		this.program.attach_shader (this.fragment_shader);
		this.program.link ();
		this.program.use ();
		stdout.printf ("program is linked %s\n", program.is_linked ().to_string ());

		this.triangle = new Triangle (this.program.get_id ());
	}

    private bool on_configure_event (Gtk.Widget widget, Gdk.EventConfigure event) {
        if (!glXMakeCurrent (xdisplay,
							 Gdk.X11Window.get_xid (widget.get_window ()),
							 context))
            return false;

		int width = widget.get_allocated_width ();
		int height = widget.get_allocated_height ();
		int size = int.min (width, height);

		glViewport ((width - size) / 2, (height - size) / 2, size, size);

        return true;
    }

    private bool on_draw (Gtk.Widget widget, Cairo.Context cr) {
        if (!glXMakeCurrent (xdisplay,
							 Gdk.X11Window.get_xid (widget.get_window ()),
							 context))
            return false;

        glClear( GL_COLOR_BUFFER_BIT );

		this.triangle.draw ();

        glXSwapBuffers (xdisplay, Gdk.X11Window.get_xid (widget.get_window ()));

        return true;
    }
}

public class Triangle {
	private float[] color = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f };
	private float[] triangle_vertex = {
         0.0f,  0.622008459f, 0.0f,
        -0.5f, -0.311004243f, 0.0f,
         0.5f, -0.311004243f, 0.0f
    };

	private uint program_id;

	public Triangle (uint program_id) {
		this.program_id = program_id;
	}

	public void draw () {
		int position_id = glGetAttribLocation (this.program_id, "vPosition");
		glEnableVertexAttribArray (position_id);
		glVertexAttribPointer (position_id, 3, GL_FLOAT, false, 12, triangle_vertex);

		int color_id = glGetUniformLocation (this.program_id, "vColor");
		glUniform4fv(color_id, 1, color);

		glDrawArrays (GL_TRIANGLES, 0, 3);

		glDisableVertexAttribArray (position_id);
	}
}

void main (string[] args) {
    Gtk.init (ref args);

    var sample = new GLXSample ();
    sample.show_all ();

    Gtk.main ();
}

The content of the makefile:

all:
	valac-0.18 -o glx-test glx-test.vala evg-shader.vala evg-program.vala --vapidir ../vapi --pkg gl --pkg gl3 --pkg gio-2.0 --pkg gtk+-3.0 --pkg glx --pkg gdk-x11-3.0 #--ccode
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s