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.参考にしたコード
[こちら](http://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/index.php?Fl_Gl_Window%A4%CE%C7%C9%C0%B8%A5%AF%A5%E9%A5%B9%A4%CB%A4%E8%A4%EBOpenGL%C9%C1%B2%E8)や[こちら](http://seriss.com/people/erco/fltk/#OpenGLSphereWithLight)で紹介されているサンプルコードを参考にさせていただきました。

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

<details><summary>サンプルコード</summary><div>

```: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();
}
```
</div></details>

これを

```
$ g++ -O3 sample.cpp -o sample -lfltk -lfltk_gl -lGLU -lGL
```
とすればコンパイルされるはず。実行すると下図のようにteapotが描画される。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893569/8d0a52c1-6eac-3447-a4f0-4688d0d52604.png)

##5.ハマった落とし穴

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

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/893569/1dc9c411-573b-8aee-bf00-c3afc42436b8.png)


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

```
#include <FL/glut.H>
```
とすればセグフォが出ないことに気づいた。但し、これをするとなぜかglu関連の関数がないよと言われるので、これに加えて

```
#include <GL/glu.h>
```
も加える必要があった。その結果導きだした解が上記のサンプルコードである。