Mugichoko's blog

Mugichoko’s blog

プログラミングを中心としたメモ書き.

GLSL #2: コールバック関数の登録

目標

GLFWを使って作成したウィンドウ(下記の前回記事参照)にコールバック関数を用いた,エラーハンドリング,キー操作,スクロール操作を追加する.

mugichoko.hatenablog.com

実装環境

サンプルプログラム

window.h

ウィンドウの基底クラスを記述したヘッダファイル.前回から,コールバック関数を追加.

//
// window.h
//
#pragma once
#include <iostream>
using namespace std;
#include <GLFW/glfw3.h>

namespace gl
{
    class window
    {
    protected:
        GLFWwindow *wnd;
        int width;
        int height;
        const string name;

        int keyID;
        double xoffset;
        double yoffset;

    public:
        window(
            int w,             // Width
            int h,             // Height
            const string &name // Name
        );
        ~window();

        virtual void render() = 0;

        // ----- Callbacks
        virtual void setCallbacks();
        // Error
        static void errorCallback(
            int err,           // Error ID
            const char *desc  // Description
        );
        // Key
        static void keyCallback(
            GLFWwindow* window,
            int key,
            int scancode,
            int action,
            int mods
        );
        // Scroll
        static void scrollCallback(
            GLFWwindow* window,
            double xoffset,
            double yoffset
        );
    };
}

window.cpp

ウィンドウの基底クラスを記述したソースファイル.ヘッダファイルに書いたコールバック関数の具体的な処理を追加.デフォルトの機能として,Escキーを押すとプログラムが終了する,スクロールするとメンバ変数にオフセット値を保存するようにした.

//
// window.cpp
//
#include "window.h"

namespace gl
{
    window::window(
        int w,
        int h,
        const string &name
    ) : name(name)
    {
        width = w;
        height = h;

        try
        {
            // ----- Initialize the library
            if (!glfwInit())
            {
                throw "Failed to initialize GLFW 3...";
            }

            // ----- Create a window
            wnd = glfwCreateWindow(w, h, name.c_str(), NULL, NULL);
            if (!wnd)
            {
                throw "Failed to create a window...";
                glfwTerminate();
            }
        }
        catch (string e)
        {
            cerr << "Error: " << e.c_str() << endl;
            exit(EXIT_FAILURE);
        }

        glfwMakeContextCurrent(wnd);

        setCallbacks();
    }

    window::~window()
    {
        if (wnd)
        {
            glfwTerminate();
        }
    }

    // ----- Callbacks
    void window::setCallbacks()
    {
        // Register this pointer
        glfwSetWindowUserPointer(wnd, this);
        // Error
        glfwSetErrorCallback(errorCallback);
        // Key
        glfwSetKeyCallback(wnd, keyCallback);
        // Scroll
        glfwSetScrollCallback(wnd, scrollCallback);
    }
    // Error
    void window::errorCallback(
        int err,
        const char *desc
    )
    {
        cerr << desc << endl;
    }
    // Key
    void window::keyCallback(
        GLFWwindow* window,
        int key,
        int scancode,
        int action,
        int mods
    )
    {
        int keyID = key;

        if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
        {
            glfwSetWindowShouldClose(window, GL_TRUE);
        }
    }
    // Scroll
    void window::scrollCallback(
        GLFWwindow* window,
        double xoffset,
        double yoffset
    )
    {
        // Get arbitrary pointer
        gl::window* _this = static_cast<gl::window*>(glfwGetWindowUserPointer(window));

        _this->xoffset = xoffset;
        _this->yoffset = yoffset;
    }
}

main.cpp

メインのソースファイル.gl::windowを継承して,自分のウィンドウを作る想定です.MyWindowクラスでは,変更したいコールバック関数を記述した上で,gl::windowクラスで設定されていたコールバック関数を上書きすることで機能を追加する.今回はキー操作を行うコールバック関数に「1」キーを押すと,コマンドプロンプトに「Hello!」の文字列を表示する機能を追加した.

//
// main.cpp
//
#include "window.h"

class MyWindow : gl::window
{
public:
    MyWindow(
        int w,
        int h,
        const string name
    ): window(w, h, name)
    {
        setCallbacks();
    }

    // ----- Callbacks
    // Override the setCallbacks() function
    void setCallbacks()
    {
        // Key
        glfwSetKeyCallback(wnd, keyCallback);
    }
    // Define a new key callback
    static void keyCallback(
        GLFWwindow* window,
        int key,
        int scancode,
        int action,
        int mods
    )
    {
        if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE)
        {
            glfwSetWindowShouldClose(window, GL_TRUE);
        }
        if (key == GLFW_KEY_1 && action == GLFW_RELEASE)
        {
            cout << "Hello!!" << endl;
        }
    }

    void render()
    {
        // Rendering loop
        while (!glfwWindowShouldClose(wnd))
        {
            // Swap front and back buffers
            glfwSwapBuffers(wnd);
            // Poll for and process events
            glfwPollEvents();
        }
    }
};

int main()
{
    MyWindow wnd(640, 480, "GLFW 3 Window");
    wnd.render();

    return 0;
}

結果

動いた!目標達成です.このプログラムでは,単に真っ黒なウィンドウが作成されるだけで,何も表示されません.右上の×ボタンを押すか,Escキーを押すことで,プログラムを終了します.

f:id:Mugichoko:20170515120801j:plain