GLUT on FLTK でteapotを描く


GLUT on FLTK のサンプルコードと落とし穴の解決

1. GLUT+FLTKでteapotを描く

GLUTを使ったOpenGL描画とFLTKを組み合わせて使いたい。
ネットに色々と記事が転がっているが、それだと上手く行かない(Segmentation Faultが出る)という事態に遭遇したので対処法を残しておく。

2.準備

Ubuntuでのfltk開発用ライブラリのインストール

$ sudo apt install libfltk1.3-dev

でOK。

3.参考にしたコード

こちらこちらで紹介されているサンプルコードを参考にさせていただきました。

4.作成したサンプルコード

サンプルコード
sample.cpp
#include <iostream>
#include <string>

//#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <FL/glut.H>

#include <FL/Fl.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/Fl_Menu_Bar.H>

using namespace std;

const GLfloat MY_LIGHT0_POS[4] = { 2.0f, 4.0f, 1.0f, 0.0f };
const GLfloat MY_LIGHT_AMBI[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
const GLfloat MY_LIGHT_DIFF[4] = { 0.9f, 0.9f, 0.9f, 1.0f };
const GLfloat MY_LIGHT_SPEC[4] = { 0.2f, 0.2f, 0.2f, 1.0f };


class myFlGLWindow : public Fl_Gl_Window
{
public:
// Constructor
myFlGLWindow(int x_, int y_, int w_, int h_, const char* l=0)
: Fl_Gl_Window(x_, y_, w_, h_, l) {
}
// Destructor
virtual ~myFlGLWindow() {
}

void InitGL(void)
{
cout << "OpenGL Ver. " << glGetString(GL_VERSION) << endl;
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);

// Light setting
glLightfv(GL_LIGHT0, GL_AMBIENT, MY_LIGHT_AMBI);
glLightfv(GL_LIGHT0, GL_DIFFUSE, MY_LIGHT_DIFF);
glLightfv(GL_LIGHT0, GL_SPECULAR, MY_LIGHT_SPEC);
glLightfv(GL_LIGHT0, GL_POSITION, MY_LIGHT0_POS);
glEnable(GL_LIGHT0);
glEnable(GL_LIGHTING);

glShadeModel(GL_SMOOTH);

glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
}

void Resize(int w, int h)
{
cout << "Resize " << w << " x " << h << endl;
glViewport(0, 0, w, h);

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, (float)w/(float)h, 0.2f, 1000.0f);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

glTranslatef(0.0f, 0.0f, -5.0f);
}

// Drawing function here..
void Display(void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glEnable(GL_LIGHTING);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

// glut function for drawing here
glColor3f(0.0, 0.0, 1.0);
glutSolidTeapot(1.0);
}

private:
// for redrawing event
void draw(void)
{
if(!context_valid()){
InitGL();
}
if(!valid()){
Resize(w(), h());
}

// OpenGL
Display();
}

};


int main(int argc, char *argv[])
{
Fl::scheme("gtk+");
Fl::visual(FL_DOUBLE | FL_RGB);
Fl::get_system_colors();

//glutInit(&argc, argv);
//glutCreateWindow("hoge");
Fl_Window *flwin = new Fl_Window(480,480,"hoge");

//myFlGLWindow *window = new myFlGLWindow(100, 100, 480, 480, "FLTK + GLUT TEST");
myFlGLWindow *window = new myFlGLWindow(10, 10, flwin->w()-20, flwin->h()-20);
flwin->resizable(window);
flwin->show();
return Fl::run();
}

これを

$ g++ -O3 sample.cpp -o sample -lfltk -lfltk_gl -lGLU -lGL

とすればコンパイルされるはず。実行すると下図のようにteapotが描画される。

5.ハマった落とし穴

以下の点でかなりハマってしまった。

当初、冒頭のインクルードファイルの指定を

#include <GL/gl.h>
#include <GL/glut.h>

としていて問題なく動いていた。但し、このままではglutSolidTeapotを呼んだ時に「先にglutInitを呼んでおけ」とエラーが出るので、glutInit関数を呼ぶようにしていた(サンプルコード下の方のglutInit行のコメントアウトを外していた)。
※なお、fltkのサイトのマニュアルにはglutInitを呼ぶな、との指示があるので、いずれにせよこの方法は非推奨と思われる。

システムアップデートに伴い上記の対応ではsegmentation faultが出るように(何かがnull pointerになっている?)。glutInitの後にglutCreateWindowを呼ぶとセグフォは回避されるが、変な空ウインドウが出来ることになってしまう(下図、teapotが描かれているウィンドウの右側)。

暫くもがいた結果、インクルードファイルをGL/glut.hでなく

#include <FL/glut.H>

とすればセグフォが出ないことに気づいた。但し、これをするとなぜかglu関連の関数がないよと言われるので、これに加えて

#include <GL/glu.h>

も加える必要があった。その結果導きだした解が上記のサンプルコードである。