Hatena::ブログ(Diary)

C++でゲームプログラミング

2012-06-08

[][]久々に OGLplus を触ってみた

TL にちょろっと話が出てきたので久々に試してみました。

今は ver 0.14.0 ですかね。

かなり構造が変わったみたいで、以前から問題になっていた関数ポインタ回りが改善されていました。

これでやっと OGLplus 側のコードを変更しないでも Windows で使用できるようになりました。


[ソース]

#include <string>
#define GL_VERSION_4_1 1

#define OGLPLUS_NO_SITE_CONFIG
#define OGLPLUS_USE_GLEW 1

#include <oglplus/gl.hpp>
#include <oglplus/all.hpp>

#include <GL/Glut.h>

#include <memory>
#include <cstdlib>

namespace oglplus {

// example.hpp
struct Example
{
    virtual ~Example(void)
    { }

    // Hint for the main function whether to continue rendering
    // Implementations of the main function may choose to ignore
    // the result of this function
    virtual bool Continue(double duration)
    {
        return duration < 3.0; // [seconds]
    }

    virtual void Reshape(size_t width, size_t height) = 0;

    virtual bool UsesMouseMotion(void) const
    {
        return false;
    }

    virtual void MouseMoveNormalized(float x, float y, float aspect)
    {
    }

    virtual void MouseMove(size_t x, size_t y, size_t width, size_t height)
    {
        return MouseMoveNormalized(
            (float(x) - width * 0.5f) / (width * 0.5f),
            (float(y) - height* 0.5f) / (height* 0.5f),
            float(width)/height
        );
    }

    virtual void Render(double time) = 0;
};

std::unique_ptr<Example> makeExample(void);


// 001_triangle.cpp
class TriangleExample : public Example
{
private:
    // wrapper around the current OpenGL context
    Context gl;

    // Vertex shader
    VertexShader vs;

    // Fragment shader
    FragmentShader fs;

    // Program
    Program prog;

    // A vertex array object for the rendered triangle
    VertexArray triangle;
    // VBO for the triangle's vertices
    Buffer verts;
public:
    TriangleExample(void)
    {
        // Set the vertex shader source
        vs.Source(" \
            #version 330\n \
            in vec3 Position; \
            void main(void) \
            { \
                gl_Position = vec4(Position, 1.0); \
            } \
        ");
        // compile it
        vs.Compile();

        // set the fragment shader source
        fs.Source(" \
            #version 330\n \
            out vec4 fragColor; \
            void main(void) \
            { \
                fragColor = vec4(1.0, 0.0, 0.0, 1.0); \
            } \
        ");
        // compile it
        fs.Compile();

        // attach the shaders to the program
        prog.AttachShader(vs);
        prog.AttachShader(fs);
        // link and use it
        prog.Link();
        prog.Use();

        // bind the VAO for the triangle
        triangle.Bind();

        GLfloat triangle_verts[9] = {
            0.0f, 0.0f, 0.0f,
            1.0f, 0.0f, 0.0f,
            0.0f, 1.0f, 0.0f
        };
        // bind the VBO for the triangle vertices
        verts.Bind(Buffer::Target::Array);
        // upload the data
        Buffer::Data(Buffer::Target::Array, 9, triangle_verts);
        // setup the vertex attribs array for the vertices
        VertexAttribArray vert_attr(prog, "Position");
        vert_attr.Setup(3, DataType::Float);
        vert_attr.Enable();

        Context::ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        gl.ClearDepth(1.0f);
    }

    void Reshape(size_t width, size_t height)
    {
        gl.Viewport(width, height);
    }

    void Render(double)
    {
        gl.Clear().ColorBuffer().DepthBuffer();

        gl.DrawArrays(PrimitiveType::Triangles, 0, 3);
    }
};

std::unique_ptr<Example> makeExample(void)
{
    return std::unique_ptr<Example>(new TriangleExample);
}


} // namespace oglplus

std::shared_ptr<oglplus::Example> example;

void
display(){
    example->Render(0.0);
    glutSwapBuffers();
    glFlush();
}

void
resize(int w, int h){
    example->Reshape(w, h);
}

void
idle(){
    glutPostRedisplay();
}

int
main(int argc, char *argv[]){
    
    // glut の初期化
    glutInit(&argc, argv);
    glutInitWindowSize(500, 500);
    glutCreateWindow(argv[0]);
    glutDisplayFunc(display);
    glutReshapeFunc(resize);
    glutIdleFunc(idle);
    glutPostRedisplay();

    // glew の初期化
    auto err = glewInit();
    if (err != GLEW_OK)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    }

    // glew の初期化後にインスタンスを生成
    example = oglplus::makeExample();
    
    // メインループ
    glutMainLoop();
    return 0;
}

とりあえず、手元に合った適当な OGLplus のコードで試してみました。

基本的なコードは変わってないかな?

あと直接は試してないですが、MSVC 11 Beta にも対応しているみたいですね。

うむ、いい感じに開発が進んでいる様子。

2012-01-27

[][]OGLplus 0.9.0

すっかり忘れていたんですが、いつの間にか OGLplus が 0.9.0 になっていました。


あと破壊的な変更点があったのでいくつか。


Matrix4f と Matrix4d の名前が変更

名前が変更されました。


Matrix4f → Mat4f
Matrix4d → Mat4d

なんで変わったんだろう。


Uniform がクラステンプレートに変更

今まで Set に渡していた型がテンプレート引数で受け取るようになったみたい。


[befor]

glp::Uniform<glp::Mat4f>(prog, "CameraMatrix").SetMatrix(
    glp::CamMatrixf::Orbiting(
        glp::Vec3f(0.0f, 10.0f, 0.0f),
        14.5,
        glp::Degrees(0.0),
        glp::Degrees(0.0)
    )
);

glp::Uniform(prog, "LightPos").Set(glp::Vec3f(1.0f, 2.0f, 3.0f));

[after]

glp::Uniform<glp::Mat4f>(prog, "CameraMatrix").Set(
    glp::CamMatrixf::Orbiting(
        glp::Vec3f(0.0f, 10.0f, 0.0f),
        14.5,
        glp::Degrees(0.0),
        glp::Degrees(0.0)
    )
);

glp::Uniform<glp::Vec3f>(prog, "LightPos").Set(glp::Vec3f(1.0f, 2.0f, 3.0f));

あとヘルパ関数として SetUniform も追加されいました。

ただし、Matrix の場合はキャストしないとうまく動きませんでした。


glp::SetUniform(prog, "CameraMatrix").Set(
    glp::Mat4f(glp::CamMatrixf::Orbiting(
        glp::Vec3f(0.0f, 10.0f, 0.0f),
        14.5,
        glp::Degrees(0.0),
        glp::Degrees(0.0)
    ))
);

glp::SetUniform(prog, "LightPos", glp::Vec3f(1.0f, 2.0f, 3.0f));

その他の変更点は下記のサイトを参照して下さい。

[参照]

http://kifri.fri.uniza.sk/~chochlik/oglplus/html/index.html

http://sourceforge.net/projects/oglplus/files/oglplus-0.9.0/

http://www.oschina.net/news/24942/oglplus-0-9-0-released

http://kifri.fri.uniza.sk/~chochlik/oglplus/html/group__shader__variables.html#ga3bb8bb500b2aa279317ba345e4abe110

2011-11-28

[][][]Boost.Asio で Web から画像を読み込んでくる

Boost.Asio を使用して、HTTP 通信で、画像の読み込みを行います。

ついでに読み込んだデータを OGLplus で描画とかしてみます。


[ソース]

#include <vector>
#include <GL/glew.h>

#include <oglplus/gl.hpp>
// OGLplus 0.5.0 以降だと near far を undef しておかないと動かない
#undef near
#undef far

// #include <oglplus/all.hpp>
#include <oglplus/context.hpp>
#include <oglplus/buffer.hpp>
#include <oglplus/vertex_attrib.hpp>
#include <oglplus/vertex_array.hpp>
#include <oglplus/shader.hpp>
#include <oglplus/program.hpp>
#include <oglplus/pixel_data.hpp>
#include <oglplus/texture.hpp>
#include <oglplus/image.hpp>
#include <oglplus/images/load.hpp>
#include <oglplus/shapes/draw.hpp>

#include <GL/Glut.h>

#define BOOST_DATE_TIME_NO_LIB
#define BOOST_REGEX_NO_LIB
#define BOOST_ERROR_CODE_HEADER_ONLY
#define BOOST_SYSTEM_NO_LIB

#include <boost/asio.hpp>
#include <gl/graphics.hpp>
#include <fstream>
#include <sstream>


namespace glp = oglplus;
typedef glp::Context glc;


template<typename Stream>
Stream&
load_image(
    Stream& s,
    std::string const& path,
    std::string const& host
){
    if(!s){
        std::cout << "Unable to connect" << std::endl;
        abort();
    }

    s << "GET " << path << " HTTP/1.0\r\n";
    s << "Host: "<< host << "\r\n";
    s << "\r\n";
    s << std::flush;  // バッファに溜めずに確実にネットワーク送信

    // 受信
    std::string line;
    while(std::getline(s, line) && !line.empty() && line != "\r"){
        std::cout << line << std::endl;
    }
    return s;
}


struct texture : boost::noncopyable{
    texture(){

        vs.Source(R"delimiter(
            #version 330
            in vec4 Position;
            in vec2 TexCoord;
            out vec2 vertTexCoord;

            void main(void){
                vertTexCoord = TexCoord;
                gl_Position = Position;
            }
        )delimiter");
        vs.Compile();

        fs.Source(R"delimiter(
            #version 330
            uniform sampler2D TexUnit;
            in vec2 vertTexCoord;
            out vec4 fragColor;

            void main(void){
                fragColor = texture(TexUnit, vertTexCoord);
                vec4 t  = texture(TexUnit, vertTexCoord);
                fragColor = vec4(t.rgb, 1.0);
            }
        )delimiter");
        fs.Compile();

        // attach the shaders to the program
        prog.AttachShader(vs);
        prog.AttachShader(fs);
        // link and use it
        prog.Link();
        prog.Use();
        // bind the VAO for the texture
        vertex_array.Bind();

        // bind the VBO for the texture vertices
        verts.Bind(glp::Buffer::Target::Array);
        {
            GLfloat data[12] = {
                1.0f, 1.0f,
                1.0f, -1.0f,
                -1.0f, 1.0f,
                -1.0f, 1.0f,
                1.0f, -1.0f,
                -1.0f, -1.0f
            };

            // upload the data
            glp::Buffer::Data(glp::Buffer::Target::Array,
                12,
                data
            );
            // setup the vertex attribs array for the vertices
            glp::VertexAttribArray vert_attr(prog, "Position");
            vert_attr.Setup(2, glp::DataType::Float);
            vert_attr.Enable();
        }
        
        texcoords.Bind(glp::Buffer::Target::Array);
        {
            GLfloat data[12] = {
                1.0f, 1.0f,
                1.0f, 0.0f,
                0.0f, 1.0f,
                0.0f, 1.0f,
                1.0f, 0.0f,
                0.0f, 0.0f
            };
            // upload the data
            glp::Buffer::Data(glp::Buffer::Target::Array,
                12,
                data
            );
            glp::VertexAttribArray attr(prog, "TexCoord");
            attr.Setup(2, glp::DataType::Float);
            attr.Enable();
        }

        // setup the texture
        tex.Bind(glp::Texture::Target::_2D);
        {
            tex.MinFilter(glp::Texture::Target::_2D, glp::TextureMinFilter::Linear);
            tex.MagFilter(glp::Texture::Target::_2D, glp::TextureMagFilter::Linear);
            tex.WrapS(glp::Texture::Target::_2D, glp::TextureWrap::Repeat);
            tex.WrapT(glp::Texture::Target::_2D, glp::TextureWrap::Repeat);

            namespace ip = boost::asio::ip;
            // web から画像を読み込んでくる
            // @manga_osyo のアイコン
            ip::tcp::iostream s( "a2.twimg.com", "http" );
            if(!s){
                std::cout << "Unable to connect" << std::endl;
                abort();
            }
            std::string const path = "/profile_images/1662110818/bambi.PNG";
            std::string const host = "a2.twimg.com";

            tex.Image2D(
                glp::Texture::Target::_2D,
                glp::images::PNG(load_image(
                    s, path, host
                ))
            );
        }

    }
    
    void
    render() const{
        vertex_array.Bind();
        glc::DrawArrays(glp::PrimitiveType::Triangles, 0, 6);
    }
private:

    glp::VertexShader vs;
    glp::FragmentShader fs;
    glp::Program prog;

    glp::VertexArray vertex_array;
    glp::Buffer verts;
    glp::Buffer texcoords;

    glp::Texture tex;
};


int
main(int argc, char *argv[]) try{
    namespace glp = oglplus;
    typedef glp::Context glc;
    
    gl::graphics g(argc, argv, 500, 500, "test");
    
    // glew の初期化
    auto err = glewInit();
    if (err != GLEW_OK)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    }
    
    glc::ClearColor(0.5f, 0.7f, 0.9f, 1.0f);
    glc::ClearDepth(1.0f);

    // テクスチャポリゴンの生成
    texture tex;
    
    gl::displayfunc(g, [&](){
        glc::Clear().ColorBuffer().DepthBuffer();
        tex.render();
    });
    
    gl::reshapefunc(g, [&](int width, int height){
        glc::Viewport(width, height);
    });

    // キー入力イベント
    gl::keyboardfunc(g, [&tex](unsigned char key, int x, int y){
        if(key == 'q') return exit(0);
    });
    
    g.run();
    return 0;
}
catch(glp::Error const& error){
    std::cout << error.what() << std::endl;
    std::cout << "GLSymbol          " << error.GLSymbol          () << std::endl;
    std::cout << "File              " << error.File              () << std::endl;
    std::cout << "Func              " << error.Func              () << std::endl;
    std::cout << "Line              " << error.Line              () << std::endl;
    std::cout << "ClassName         " << error.ClassName         () << std::endl;
    std::cout << "ObjectDescription " << error.ObjectDescription () << std::endl;
}

[出力]

HTTP/1.1 200 OK
x-amz-id-2: Wa09wGDzkwFuok1YsW693owl0hz7lumLYccbjNqFFYKWQnbpxlb1BZh+9+BY+bu+
x-amz-request-id: 6251203F97195E3D
Date: Mon, 28 Nov 2011 08:18:39 GMT
Last-Modified: Mon, 28 Nov 2011 08:04:41 GMT
ETag: "43078357c49e2a0719d4be75c028e01b"
Accept-Ranges: bytes
Content-Type: image/png
Content-Length: 364906
Server: AmazonS3
Cache-Control: private, max-age=604800
Age: 0
Expires: Mon, 05 Dec 2011 08:18:39 GMT
Connection: close

f:id:osyo-manga:20111128172638p:image

思ったよりも簡単に出来たので満足。


[boost]

  • ver 1.48.0

[コンパイラ]

  • g++ (GCC) 4.7.0 20111112 (experimental)

[参照]

http://www.tohoho-web.com/ex/http.htm

2011-11-03

[]OGLplus で画像の描画

OGLplus を使用したテクスチャの描画です。

OGLplus では、libpng で .png の画像ファイルを読み込むことが出来るので、今回はそれを使用します。

使用するためには予め libpng を導入しておく必要があります。



example だと <oglplus/bound/texture.hpp> を使っているみたいですが、そんなものは存在しなかったので、直接書いています。

まぁやっていることはそんなに変わらないはず。


[ソース]

#include <vector>
#include <GL/glew.h>

#include <oglplus/gl.hpp>
// OGLplus 0.5.0 以降だと near far を undef しておかないと動かない
#undef near
#undef far

//#include <oglplus/all.hpp>
#include <oglplus/context.hpp>
#include <oglplus/buffer.hpp>
#include <oglplus/vertex_attrib.hpp>
#include <oglplus/vertex_array.hpp>
#include <oglplus/shader.hpp>
#include <oglplus/program.hpp>
#include <oglplus/pixel_data.hpp>
#include <oglplus/texture.hpp>
#include <oglplus/image.hpp>
#include <oglplus/images/load.hpp>
#include <oglplus/shapes/draw.hpp>

#include <GL/Glut.h>

#include <gl/graphics.hpp>
#include <boost/optional.hpp>
#include <fstream>
#include <sstream>

namespace glp = oglplus;
typedef glp::Context glc;


template<typename Shader, typename Char>
boost::optional<std::string>
shader_compile(Shader& shader, Char const* source){
    shader.Source(source);
    try{
        shader.Compile();
        return boost::none;
    }
    catch (glp::CompileError const& error) {
        std::cout << error.what() << std::endl;
        return error.Log();
    }
}

template<typename Shader, typename Char>
boost::optional<std::string>
shader_compile(Shader& shader, std::basic_ifstream<Char> const& file){
    std::stringstream buffer;
    buffer << file.rdbuf();
    return shader_compile(shader, buffer.str().c_str());
}


struct texture : boost::noncopyable{
    
    texture(){

        load_shader();

        // bind the VAO for the texture
        vertex_array.Bind();

        // bind the VBO for the texture vertices
        verts.Bind(glp::Buffer::Target::Array);
        {
            GLfloat data[12] = {
                1.0f, 1.0f,
                1.0f, -1.0f,
                -1.0f, 1.0f,
                -1.0f, 1.0f,
                1.0f, -1.0f,
                -1.0f, -1.0f
            };

            // upload the data
            glp::Buffer::Data(glp::Buffer::Target::Array,
                12,
                data
            );
            // setup the vertex attribs array for the vertices
            glp::VertexAttribArray vert_attr(prog, "Position");
            vert_attr.Setup(2, glp::DataType::Float);
            vert_attr.Enable();
        }
        
        texcoords.Bind(glp::Buffer::Target::Array);
        {
            GLfloat data[12] = {
                1.0f, 1.0f,
                1.0f, 0.0f,
                0.0f, 1.0f,
                0.0f, 1.0f,
                1.0f, 0.0f,
                0.0f, 0.0f
            };
            // upload the data
            glp::Buffer::Data(glp::Buffer::Target::Array,
                12,
                data
            );
            glp::VertexAttribArray attr(prog, "TexCoord");
            attr.Setup(2, glp::DataType::Float);
            attr.Enable();
        }

        // setup the texture
        tex.Bind(glp::Texture::Target::_2D);
        {
            tex.MinFilter(glp::Texture::Target::_2D, glp::TextureMinFilter::Linear);
            tex.MagFilter(glp::Texture::Target::_2D, glp::TextureMagFilter::Linear);
            tex.WrapS(glp::Texture::Target::_2D, glp::TextureWrap::Repeat);
            tex.WrapT(glp::Texture::Target::_2D, glp::TextureWrap::Repeat);

            std::ifstream file("cpplands.png", std::ios::binary);
            tex.Image2D(
                glp::Texture::Target::_2D,
                glp::images::PNG(file)
            );
            // OGLplus で用意されている読み込み処理では
            // std::ios::binary が付いておらず失敗する
//            tex.Image2D(glp::Texture::Target::_2D, glp::images::LoadTexture("cpplands"));
        }
    }
    
    void
    render() const{
        vertex_array.Bind();
        glc::DrawArrays(glp::PrimitiveType::Triangles, 0, 6);
    }
    void
    reload_shader(std::string name = "basic"){
        prog.DetachShader(vs);
        prog.DetachShader(fs);
        load_shader(name);
        std::cout << "reloaded shader" << std::endl;
    }

private:
    void
    load_shader(std::string name = "basic"){
        if(auto error = shader_compile(vs, std::ifstream(name + ".vert"))){
            std::cout << "==== VertexShader Compile Error ====" << std::endl;
            std::cout << *error << std::endl;
            return;
        }

        if(auto error = shader_compile(fs, std::ifstream(name + ".frag"))){
            std::cout << "==== FragmentShader Compile Error ====" << std::endl;
            std::cout << *error << std::endl;
            return;
        }
        // attach the shaders to the program
        prog.AttachShader(vs);
        prog.AttachShader(fs);
        // link and use it
        prog.Link();
        prog.Use();
    }

    // Vertex shader
    glp::VertexShader vs;

    // Fragment shader
    glp::FragmentShader fs;

    // Program
    glp::Program prog;

    // A vertex array object for the rendered texture
    glp::VertexArray vertex_array;
    // VBO for the texture's vertices
    glp::Buffer verts;
    
    glp::Buffer texcoords;

    // The stained glass texture
    glp::Texture tex;
};


int
main(int argc, char *argv[]) try{
    namespace glp = oglplus;
    typedef glp::Context glc;
    
    gl::graphics g(argc, argv, 800, 600, "test");
    
    // glew の初期化
    auto err = glewInit();
    if (err != GLEW_OK)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
    }
    
    glc::ClearColor(0.5f, 0.7f, 0.9f, 1.0f);
    glc::ClearDepth(1.0f);

    // テクスチャポリゴンの生成
    texture tex;

    //----------------------------------------------------------------------    
    
    gl::displayfunc(g, [&](){
        glc::Clear().ColorBuffer().DepthBuffer();
        tex.render();
    });
    
    gl::reshapefunc(g, [&](int width, int height){
        glc::Viewport(width, height);
    });

    // キー入力イベント
    gl::keyboardfunc(g, [&tex](unsigned char key, int x, int y){
        if(key == 'q') return exit(0);
        if(key == 'u') return tex.reload_shader(), glutPostRedisplay();
    });

    g.run();
    return 0;
}
catch(glp::Error const& error){
    std::cout << error.what() << std::endl;
    std::cout << "GLSymbol          " << error.GLSymbol          () << std::endl;
    std::cout << "File              " << error.File              () << std::endl;
    std::cout << "Func              " << error.Func              () << std::endl;
    std::cout << "Line              " << error.Line              () << std::endl;
    std::cout << "ClassName         " << error.ClassName         () << std::endl;
    std::cout << "ObjectDescription " << error.ObjectDescription () << std::endl;
}

[basic.vert]

#version 330
in vec4 Position;
in vec2 TexCoord;
out vec2 vertTexCoord;

void main(void){
    vertTexCoord = TexCoord;
    gl_Position = Position;
}

[basic.frag]

#version 330
uniform sampler2D TexUnit;
in vec2 vertTexCoord;
out vec4 fragColor;

void main(void){
    fragColor = texture(TexUnit, vertTexCoord);
    vec4 t  = texture(TexUnit, vertTexCoord);
    fragColor = vec4(t.rgb, 1.0);
}

[出力]

f:id:osyo-manga:20111103084413p:image


[注意]

oglplus::images::LoadTexture を使用して画像ファイルを読み込む場合、std::ios::binary オプションが付いておらずに読み込みに失敗してしまいます。

これを防ぐためには、OGLplus のコードを直接書き換えるか、ユーザコードで、std::ifstream で読み込む必要があります。

[OGLplus]

  • 0.7.0

2011-11-02

[]OGLplus のエラーハンドリング

OGLplus では、OpenGL でエラーが発生した場合に、例外をスローします。

スローした例外から詳細な情報(ソースファイルの位置や OpenGLのAPIなど)を取得することが出来ます。


[ソース]

#include <vector>
#include <iostream>

#include <GL/glew.h>
#include <oglplus/gl.hpp>
#include <oglplus/context.hpp>
#include <oglplus/shader.hpp>
#include <GL/Glut.h>

namespace glp = oglplus;
typedef glp::Context glc;


int
main(int argc, char *argv[]) try{
    
    // Shader を使うために初期化
    glutCreateWindow("");
    auto err = glewInit();
    if (err != GLEW_OK){
        std::cout << "Error:" << glewGetErrorString(err) << std::endl;
    }
    
    glp::VertexShader vs;

    // 適当にエラーを履かせてみる
    // vs.Source(R"delimiter(
    //     #version 330
    //     in vec3 Position;
    //     void main(void){
    //         gl_Position = vec4(Position, 1.0);
    //     }
    // )delimiter");
    
    // コンパイル
    vs.Compile();
    std::cout << "SUCCESS" << std::endl;
    
    return 0;
}
catch(glp::Error const& error){
    std::cout << error.what() << std::endl;
    std::cout << "GLSymbol          " << error.GLSymbol          () << std::endl;
    std::cout << "File              " << error.File              () << std::endl;
    std::cout << "Func              " << error.Func              () << std::endl;
    std::cout << "Line              " << error.Line              () << std::endl;
    std::cout << "ClassName         " << error.ClassName         () << std::endl;
    std::cout << "ObjectDescription " << error.ObjectDescription () << std::endl;
}

[出力]

OpenGL shading language compilation error
GLSymbol          CompileShader
File              D:/opengl/oglplus/include/oglplus/shader.hpp
Func              Compile
Line              220
ClassName         Shader
ObjectDescription 

[OGLplus]

  • 0.7.0

[参照]

http://kifri.fri.uniza.sk/~chochlik/oglplus/html/index.html#feat_error_handling

http://kifri.fri.uniza.sk/~chochlik/oglplus/html/group__error__handling.html

http://kifri.fri.uniza.sk/~chochlik/oglplus/html/error_8hpp_source.html

[]OGLplus 0.7.0


またバージョンアップしたみたいです。

ペースが早い…。

HPのレイアウトもちょっと変わっていますね。

チュートリアルもいつの間にか出来ていたので、初めて使う場合はここら辺を読んでみるといいかも?

そろそろリリースノートが欲しいところ。