MacでPTAMを使ってサッチーを呼び出すまで

PTAMでサッチーを呼び出すことに成功しました。

こういう感じですね。この動画の方が夢があるけども。
この動画あげたのは僕じゃないです。念のため。


というわけでやり方のまとめ。

前提

PTAMのコンパイルに成功し、きちんと動くようになっているのを前提とします。
コンパイルの仕方とかは既に多くの情報があるのでここでは説明の必要はないと思います。

準備するもの


PTAMはサンプルの目玉が出せるようになってればOK。


サッチーの3DモデルとOpenGLC言語メタセコイアローダは工学ナビさんが配布しているものを使用。
サッチーの3Dモデルは以下から入手。
http://www1.bbiq.jp/kougaku/ARToolKit.html
ダウンロードしたら
/PTAM/Data/sachi.mqo
/PTAM/Data/sachi.bmp
あたりに置いておく。


OpenGLC言語メタセコイアローダは以下から。
http://kougaku.blog28.fc2.com/blog-entry-282.html
ダウンロードしたらGLMetaseq2.hとegLight.hを
/PTAM/GLMetaseq2.h
/PTAM/egLight.h
あたりに置いておく。



これで準備万端。

方針

必要なファイルが揃ったので、あとはこれを表示するだけ。
しかし、OpenGLに不慣れ&めんどくさいことはしたくない!
ということで楽な方法を考える。


PTAMのデモでは特徴点の抽出に成功すると目玉が表示された
→目玉を別のオブジェクトに置き換えればいいじゃないか!


ということで目玉オブジェクトを描画する部分を
サッチーの3Dモデルを描画するように書き換える。
そんな方針でいきます。

目玉はどこへいった

そのためにまず目玉をどこで描画しているのかを探さないといけない。
これはPTAMディレクトリ中の

  • EyeGame.cc
  • EyeGame.h

がそれ。


OpenGLに詳しくないので間違ってるかもしれないけど、
Init()関数で目玉描画用のリストの作成、データの書き込みをして、
DrawStuff()関数でそのリストを使って実際に描画みたいな感じかな。
具体的にはforループで描いてるはず。


他にも影とかの描画もしてるけど、それはまた後で。

ソースの修正

あるていど当たりがついたので早速修正。

EyeGame.h

まずはヘッダファイルから。

#include "GLMetaseq2.h"
#include "egLight.h"

を追加。


さらにprotectedのメンバ変数として

  MQO_MODEL g_mqoModel;

を宣言。
このMQO_MODELてのがメタセコイアで作った3Dモデルを扱うための型になる。
実際はGLuintをtypedefしてるだけなのでそんなに問題じゃない。


ヘッダはこれだけ。

EyeGame.cc

次に本体。

#define MQO_FILE "Data/sachi.mqo"

メタセコイアデータの場所を定義しとく。
しなくてもいいけど、違うモデル使うときに変えるのが楽かなと。


Init()関数内に、

void EyeGame::Init()
{
  if(mbInitialised) return;
  mbInitialised = true;
  // Set up the display list for the sachi.
  g_mqoObject = mqoCreateModel( MQO_FILE, 0.001 );
};

こんな感じに。ざっくり変える。
mqo_CreateModel()関数はモデルデータのファイル名、スケールをそれぞれ引数にとる。
スケール小さいけど、これぐらいがちょうどよかった。
これで初期化終わり。


次は描画。
DrawStuff()関数内のforループをコメントアウトして目玉を消す。
さらに読み込んだサッチーの3Dモデルを呼び出すようにする。

/*
  for(int i=0; i<4; i++)
    {
      if(mnFrameCounter < 100)
	LookAt(i, 500.0 * (Vector<3>) (make_Vector, (i<2?-1:1)*(mnFrameCounter < 50 ? -1 : 1) * -0.4 , -0.1, 1) , 0.05 );
      else
	LookAt(i, v3CameraPos, 0.02 );
      
      glLoadIdentity();
      glMultMatrix(ase3WorldFromEye[i]);
      glScaled(mdEyeRadius, mdEyeRadius, mdEyeRadius);
      glCallList(mnEyeDisplayList);
    }
	
*/
	
      mqoCallModel( g_mqoObject );

こんな感じ。


これで目玉の代わりサッチーが表示されるようになるはず。
まだ不必要な変数、関数が残ってるからそういうのを
消したりしないといけないけどとりあえずいったん置いとく。

コンパイル

いよいよコンパイルするんだけどここがまたやっかいだった。
エラー出なかった人は素敵な電脳ライフへ。
そうでない人はぷちぷちつぶして行こう。


ちなみにエラー修正したのに同じエラーが何回も出る場合は、一度make cleanしてみるといいかも。

includeのエラー
GLMetaseq2.h:78:19: error: GL/gl.h: No such file or directory
GLMetaseq2.h:79:20: error: GL/glu.h: No such file or directory
GLMetaseq2.h:80:21: error: GL/glut.h: No such file or directory

GLMetaseq2.hでインクルードしてるファイルがないよ!というエラー。
Macの場合はGLMetase2.h開いてそれぞれ以下に修正。

#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <GLUT/glut.h>


egLight.hも同様のエラーが出るので同じように修正する。

TRUE/FALSEのエラー

次はこんなエラー。

GLMetaseq2.h:432: error: 'TRUE' was not declared in this scope
GLMetaseq2.h:440: error: 'FALSE' was not declared in this scope

GLMetaseq2.hでTRUEとかFALSEとか使ってるけど定義されてないよ!という内容。
じゃあ定義すればいい。
インクルードの下あたりに

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

と定義。

関数がいっぱいあるエラー

同じ関数の実体が違うとこで見つかったよというエラー。
ld: duplicate symbol mqoLoadTexture(char*, int*)in ARDriver.o and System.o
collect2: ld returned 1 exit status
make: *** [PTAM] Error 1


これが起こる原因はARDriver.hとSystem.ccがどちらもGLMetaseq2.hを呼んでいるため。
(というかSystem.ccがARDriver.hを呼び、ARDriver.hがGLMetaseq2.hを呼んでるんだけども。)
問題はGLMetaseq2.hに関数の中身が書いてる点。そのためにこのエラーが起きる。


じゃあヘッダファイルに書かなければ良いということでソースを分割しましょう。

  1. GLMetaseq2.cを作る。
  2. GLMetaseq2.hの関数本体の部分を切り取り、GLMetaseq2.cにコピー。最後の#endifは含まない。
  3. GLMetaseq2.cの冒頭でGLMetaseq2.hをインクルード。
  4. GLMetaseq2.hのプロトタイプ宣言の各関数の頭にexternを付ける。

extern付けると

extern GLubyte* mqoLoadTexture(char *filename,int *tex_size);
extern void mqoRegistTexture(GLuint* tex_name,GLubyte* tex_img,int tex_size);

こんな感じ。他の関数も同じように。


これでmakeするとegLight.hに含まれる関数についても同じようにエラーが出るので、
こっちも同じ手順でegLight.cを作る。
ただしegLight.hにはプロトタイプ宣言が記述されてないので、そこは自分で書く。

実体どこだよのエラー
Undefined symbols:
  "mqoCreateModel(char*, double)", referenced from:
      EyeGame::Init()     in EyeGame.o
      EyeGame::DrawStuff(TooN::Vector<3>)         in EyeGame.o
  "mqoCallModel(unsigned int)", referenced from:
      EyeGame::DrawStuff(TooN::Vector<3>)         in EyeGame.o
ld: symbol(s) not found

makeすると次はこんなエラー。
同じ関数はなくなった、けど実体もいなくなった。という内容。


さっきはソース分割したけど、そこの記述された関数を呼ぶにはどうすればいいのかという話。
そのためにはオブジェクトファイルを作ってリンクすれば解決。


これはMakefileに書き加える。
Makefileの最初の方に、

OBJECTS=	main.o\
		VideoSource_OSX.o\
		GLWindow2.o\
                ....
		ARDriver.o\
		EyeGame.o\
		Tracker.o

という記述がある。これはPTAMを作るのに必要なオブジェクトファイルを書いてある。
なのでここにGLMetaseq2.oとegLight.oを書き加えてしまえば良い。

OBJECTS=	main.o\
		VideoSource_OSX.o\
		GLWindow2.o\
                ....
		ARDriver.o\
		EyeGame.o\
		Tracker.o\
                GLMetaseq2.o\
                egLight.o


じゃあGLMetaseq2.oも作らないといけないのか!?と思うけど、
ここらへんはMakefileに書いておくだけで勝手に作ってくれるので気にしなくて大丈夫。


ちなみにインデントには必ずタブを使うこと。
スペース使うとmakeで正しく処理されない。

文字列のエラー
GLMetaseq2.c: In function 'void mqoReadMaterial(FILE*, MQO_MATERIAL*)':
GLMetaseq2.c:164: error: 'strstr' was not declared in this scope

という感じで文字列処理の関数のエラーが沢山出てくるが、
単純にstring.hが読み込まれてないだけなので、
GLMetaseq2.cでstring.hをインクルードする。

GLUTのエラー
Undefined symbols:
  "_glutWireSphere", referenced from:
      egLightOn(float, float, float)in egLight.o
ld: symbol(s) not found

今度も関数の実体がないよというエラー。しかもGLUT
これはフレームワークとしてGLUTを使うというのを教えてあげないといけない。
そのためにまたMakefileを変更する。


最初の方にある、

LINKFLAGS = -framework OpenGL  -framework VecLib -L/MY_CUSTOM_LINK_PATH/ -lGVars3 -lcvd

を、

LINKFLAGS = -framework OpenGL -framework GLUT -framework VecLib -L/MY_CUSTOM_LINK_PATH/ -lGVars3 -lcvd

に変える。これでOK。

戦いの後。

これで無事makeが通るようになったはずです。
成功した人は遅ればせながら電脳ライフへ。
そうでない人はコメントに書き込みお願いします。

いざサッチー

先ほど作ったPTAMを使うと、
(うまくいけば)目玉の代わりに下の図のようにサッチーが現れるはずです。

あれ、ちょっと待てよ、サッチー寝てないか?

というわけでこっちを見てくれないサッチー。
今のままではZ軸の+方向が平面と平行に設定されているため、
そのまま表示すると倒れたようになってしまう。
そこで、オブジェクトを回転させて軸の向きを変える必要がある。


そのためにはGLMetaseq2.cのmqoMakeObjects()関数にある、
glRotatef()のコメントアウトを外し、またmakeすれば良い。


これでようやく起き上がってくれました。
(ちょっと傾いてるのがお茶目。)

まとめ


長々と書きましたが、読んでくれた方ありがとうございます。役に立てば幸いです。
いろいろと手探りで、このやり方でいいのかという部分も多々あると思います。
そういうところはコメントなどでフォローしてくれると助かります。