Vala プログラミング

読者です 読者をやめる 読者になる 読者になる

Rust & Vala プログラミング

おながのブログ

ValaGL + Gtk.GLArea ( Vala & OpenGL)

f:id:onagat12:20160619142808g:plain

 

実行結果

(実際の実行では、スペースキーで、cubeが回転、停止します。)

 

下記のサイトで、Vala, SDL, GLEW で記述された、OpenGL 3.x に対応したrotating cubeアプリケーションが紹介されています。

30/03/2013) ValaGL(simple skeletal application)

https://github.com/lucidfox/valagl)

 

lucidfox | Vala and Modern OpenGL

http://lucidfox.dreamwidth.org/11614.html

http://en.lucidfox.org/2013/03/vala-and-modern-opengl/

 

1 GL 3.x APIに対応した vapiの作成

2 vec3vec4に対応したmatrix vector algebra、またprojections(camera)

   model matrix transformationsのVala言語による再実装

がなされています。

 

今回は、これらのプログラムを用いて、Gtk.GLArea上に同じrotating cubeを描画

します。

使用するプログラム

1 vapi

GLAreaでは、GLライブラリとしてepoxyを用いています。これに対応するように、

ここでは、CヘッダーファイルをGL/glew.hからepoxy/gl.hに変更しています。

(glepoxy.vapi)

 

2 ValaGL.Core内のプログラム

GLProgram.valaVBO.valaIBO.valaCoreError.valaCamera.vala

    MatrixMath.valaGeometryUtils.vala

(1) VBO.valaCoreError.valaCamera.valaMatrixMath.valaGeometryUtils.vala

     は変更なしで使用します。

(2) IBO.vala

     1コメントアウトします。

     52行目 //glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0);

(3) GLProgram.vala

   変更します。下記参照。

 

3   main.vala (App.valaに相当)

## main.vala

using Gtk;

using Gdk;

 

public class MainWindow : Gtk.Window

{

    private glArea gl_area;

    private float angle = 0.0f;

    private uint tick_id;

    private bool isRunning = false;

 

    public MainWindow ()

    {

        title = "GLArea Test";

        destroy.connect (Gtk.main_quit);

        set_events (EventMask.KEY_PRESS_MASK );

 

        var box = new Box (Orientation.VERTICAL, 0);

 

        gl_area = new glArea ();

 

        this.key_press_event.connect ((evt) => {

            if (evt.keyval == Gdk.Key.space) {

                GLib.message ("Key Pressed");

                if (isRunning == false) {

                    tick_id = gl_area.add_tick_callback (tick);

                } else {

                   gl_area.remove_tick_callback (tick_id);

                }

                isRunning = !isRunning;

           }

           return true;

        });

 

        var button = new Button.with_label ("Quit");

        button.clicked.connect (Gtk.main_quit);

 

        box.pack_start (gl_area, true, true);

        box.pack_start (button, false, false);

        add (box);

    }

 

    private bool tick (Widget widget, FrameClock frame_clock)

    {

        angle = angle + 1.0f;

        stdout.printf("angle: %f\n", angle);

        gl_area.update_scene_data(angle);

        gl_area.queue_render ();

 

        return true;

    }

}

 

int main (string args)

{

    Gtk.init (ref args);

 

    var mainWindow = new MainWindow();

    mainWindow.show_all ();

 

    Gtk.main ();

 

    return 0;

}

 

4   glArea.vala (Canvas.valaに相当)

## glArea.vala

using Gtk;

using Gdk;

using GL;

using ValaGL.Core;

 

public class glArea : Gtk.GLArea

{

    private GLuint vao = {0};

    private GLProgram gl_program;

    private VBO coord_vbo;

    private VBO color_vbo;

    private IBO element_ibo;

    private GLint mvp_location;

    private float rotation_angle;

 

    private GLfloat cube_vertices = {

        // front

        -1, -1, 1,

         1, -1, 1,

         1, 1, 1,

        -1, 1, 1,

        // back

        -1, -1, -1,

         1, -1, -1,

         1, 1, -1,

        -1, 1, -1

    };

 

    private GLfloat cube_colors = {

        // front colors

        1, 0, 0,

        0, 1, 0,

        0, 0, 1,

        1, 1, 1,

        // back colors

        1, 0, 0,

        0, 1, 0,

        0, 0, 1,

        1, 1, 1

    };

 

    private GLushort cube_elements = {

        // front

        0, 1, 2,

        2, 3, 0,

        // top

        1, 5, 6,

        6, 2, 1,

        // back

        7, 6, 5,

        5, 4, 7,

        // bottom

        4, 0, 3,

        3, 7, 4,

        // left

        4, 5, 1,

        1, 0, 4,

        // right

        3, 2, 6,

        6, 7, 3

    };

 

    public glArea ()

    {

        set_size_request (500, 400);

        has_depth_buffer = true;

        render.connect (on_render);

    }

 

    private bool on_render (GLContext contxt)

    {

        stdout.printf ("render\n");

        // realize

 

        glGenVertexArrays(1, vao);

        glBindVertexArray(vao[0]);

 

        try {

            gl_program = new GLProgram();

            coord_vbo = new VBO(cube_vertices);

            color_vbo = new VBO(cube_colors);

            element_ibo = new IBO(cube_elements);

        } catch (CoreError e) {

            stdout.printf("CoreError: %s\n", e.message);

        }

 

        mvp_location = gl_program.get_uniform_location("MVP");

 

        glEnableVertexAttribArray(0);

        coord_vbo.apply_as_vertex_array(0, 3);

        glEnableVertexAttribArray(1);

        color_vbo.apply_as_vertex_array(1, 3);

 

        glBindVertexArray(0);

 

        // render

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

 

        var camera = new Camera ();

        camera.set_perspective_projection (70.0f, 5.0f/4.0f, 0.01f, 100.0f);

 

        var eye = Vec3.from_data (0, 2, 0);

        var center = Vec3.from_data (0, 0, -2);

        var up = Vec3.from_data (0, 1, 0);

        camera.look_at (ref eye, ref center, ref up);

 

        var model_matrix = Mat4.identity ();

        Vec3 translation = Vec3.from_data (0, 0, -4);

        GeometryUtil.translate (ref model_matrix, ref translation);

        Vec3 rotation = Vec3.from_data (0, 1, 0);

        GeometryUtil.rotate (ref model_matrix, rotation_angle, ref rotation);

 

        gl_program.make_current();

        camera.apply (mvp_location, ref model_matrix);

 

        glBindVertexArray(vao[0]);

        glDrawElements((GLenum)GL_TRIANGLES, cube_elements.length,

       GL_UNSIGNED_SHORT, null);

 

        glBindVertexArray (0);

        glUseProgram (0);

 

        glFlush();

 

        return true;

    }

 

    public void update_scene_data (float angle)

    {

        rotation_angle = angle;

    }

}

depth_bufferを有効にするには、GLAreaクラスのプロパティhas_depth_buffer

trueにします。

 

 

5   GLProgram.vala

vertex shaderfragment shaderをここに記述します。これは、前回(2015/11/15

説明したワーニングが出ないようにするためです。各shaderプログラムを直接glShaderSource()に渡します。

(1) shaderプログラムをクラス定義(public class GLProgram)の前に挿入します。

## vertex shader

const string vert_src = {

    "#version 330 core\n" +

    "layout(location = 0) in vec3 coord3d;\n" +

    "layout(location = 1) in vec3 v_color;\n" +

    "out vec3 f_color;\n" +

    "uniform mat4 MVP;\n" +

    "void main() {\n" +

    " f_color = v_color;\n" +

    " gl_Position = MVP * vec4(coord3d, 1.0);\n" +

    "}"};

 

## fragment shader

    const string[] frag_src = {

    "#version 330 core\n" +

    "in vec3 f_color;\n" +

    "void main() {\n" +

    " gl_FragColor = vec4(f_color.x, f_color.y, f_color.z, 1.0);\n" +

    "}"};

 

(2) shaderプログラムが関連する引数を削除し、変更します。

public GLProgram () throws CoreError

vertex_shader = create_shader (GL_VERTEX_SHADER)

  fragment_shaderも同様。

 

(3) shaderプログラムの読込み部分を変更します。

create_shader_from_file メソッドは使用しない。

private static Gluint create_shader (Gluint shader_type) throws CoreErroe

if (shader_type == GL_VERTEX_SHADER) {

        glShaderSource (shader, 1, vert_src, null);

    } else {

        glShaderSource (shader, 1, frag_src, null);

    }

 

* プログラム

ValaGL-GLArea.zip (http://yahoo.jp/box/PhHb1j )

 

* ビルド

valac --vapidir=./ --pkg gtk+-3.0 --pkg gdk-pixbuf-2.0 --pkg glepoxy -X -lepoxy -X -lm

main.vala glArea.vala Camera.vala MatrixMath.vala GeometryUtil.vala GLProgram.vala

VBO.vala IBO.vala CoreError.vala