FBXSDKをmacのターミナルで使いたいので使えるようにした

## はじめに
ふと思いました。macとOpenGLでFBXモデルを表示してみたい！
OpenGLがオワコンなのはわかっています。でもやりたかったんです。

でも、そもそもFBXをどうやって読み込むのか、そこから始めないといけませんでした。
手動など色々考えましたが、辿り着いたのは[FBXSDK](https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2020-2)でした。
このFBXSDKを使えばFBXが簡単に読み込める！そう思ってた時期が自分にもありました。

C++で書いたプログラムでFBXを読み込みたかったんです。
XcodeやVSでコンパイルする方法は調べればたくさん出てくるのですが、ターミナルでコマンドを打ってコンパイルする方法ってあんまり書かれてないんですよ。
公式のサンプルはcmakeを使用してますが、cmakeを作るのも面倒です。

gccもしくはg++で一発でコンパイル&リンクしてくれないだろうか...
この記事はそれをなんとかやってみた話になります。

## 環境
* macOS BigSur 11.2.1
* zsh 5.8
* g++ 12.0.0(Apple clang)
* fbxsdk 2020.2

## FBXSDKのインストール
とにもかくにもSDKをインストールしないと始まりません。
FBXSDKの[Webサイト](https://www.autodesk.com/developer-network/platform-technologies/fbx-sdk-2020-2)から、Macのところのリンクをクリックしてください。Universal Binaryとか書いてあると思います。

そしたらtgzファイルがダウンロードされると思うので、解凍します。
するとpkgファイルが作成されます。これを開くことでFBXSDKのインストールが始まります。
デフォルトだと、`/Applications/AutoDesk/FBX SDK/(バージョン)/`のようなところに保存されると思います。場所を変更したければしてもらって構いません。以降は便宜上`/FBX SDK/2020.2/`と表記します。

## お試しビルド
まず自身の環境でビルドができるのか、サンプルで試してみます。
インストールしたフォルダの中にある`samples/ViewScene`までターミナルで移動します。
そこで、

```
$ cmake .
$ make
```
とすればビルドができると思います。
人によっては`Permission Denied`なんて出るかもしれません。そんな時は~~須藤~~`sudo`をつけて実行してください。
実行ファイルの場所は出力されていると思います。自分の場合は、
`/FBX SDK/2020.2/bin/x64/clang-static/debug/`に作成されていました。
作成した実行ファイルを動かすためには、一度このdebugファイルまでターミナルで移動してから実行してください。
Finderから実行すると場所が悪いのかうまく動いてくれませんでした。

ここまでできない場合は、そもそもC++をコンパイルできる環境にないかもしれません。いろいろ調べて出直してきてください。

## 自作プログラムの作成
これでFBXSDKを使ったプログラムをコンパイルできる段階まできました。
ここからは自分で用意したプログラムを任意の場所で実行する方法になります。

まず動かすプログラムを作成します。このプログラムは[こちらのWebサイト](http://blog.livedoor.jp/tek_nishi/archives/9428032.html)から一部拝借させていただきました。FBXファイルを読み込み、何も問題なければエラーなく動き「Success」と表示されるプログラムです。

``` C++:main.cpp
#include <cstdlib>
#include <iostream>
#include <fbxsdk.h>

int main(){
  
  // FBX SDKを生成・初期化
  FbxManager* manager = FbxManager::Create();
  assert(manager);
  
  // 読み込み機能を生成
  FbxImporter* importer = FbxImporter::Create(manager, "");
  assert(importer);
  
  if (!importer->Initialize("hoge.fbx")) // 読み込むFBXファイルを指定
    {
      // 読み込み失敗
    }

  // 読み込み用のシーンを生成
  FbxScene* scene = FbxScene::Create(manager, "");
  assert(scene);
  
  // ファイルからシーンへ読み込む
  importer->Import(scene);
  
  // FbxImporterはもう使わないのでここで破棄
  importer->Destroy();

  // 
  // シーンから必要な情報を取り出す処理を書く
  //

  // シーンを破棄
  scene->Destroy();
  
  // FBS SDKを破棄
  manager->Destroy();

  std::cout << "Success" << std::endl;
}
```

このプログラムを好きな場所においてください。これで準備は整いました。

## ビルドするためのコマンド
本題です。
結論としては、このようにコマンドを入力します。

```
g++ -L"/FBX SDK/2020.2/lib/clang/debug" -I"/FBX SDK/2020.2/include" -lfbxsdk -std=c++11 main.cpp
```

順に解説をしていきます。

まずコマンドはg++です。gccでもいいのですが、その場合`-lc++`を付与する必要があります。

`-L`と`-I`は人によって見慣れたり見慣れなかったりだと思います。

`-L`はライブラリを探すパスを表しています。このパスの先に何があるかというと、`libfbxsdk.dylib`と`libfbxsdk.a`があります。これらが何かは墓穴を掘りそうなので調べてください。
指定することで、どのライブラリを探してほしいのか指定することができます。debugフォルダとreleaseフォルダがありますが、どちらでも大丈夫だと思います。気分で選んでください。ここではdebugフォルダとします。

`-I`はincludeファイルを探すパスを表しています。ヘッダーファイルとかですね。これで`#include <fbxsdk.h>`が働くようになります。

ちなみに、パスをダブルクォーテーションで括っている理由は、`FBX SDK`に空白があるからですね。フォルダ名を変更して空白なしにすれば括らずに実行できると思います。
パスはフルパスでお願いします。

`-lfbxsdk`は使用するライブラリを表しています。`-L`でパスを指定し、`-l`でライブラリを指定しているという形です。

`std=c++11`はなくてもいいですが、ないとワーニングが大量に出るので、C++11以上を指定してあげてください。C++17とかでもプログラムによっては大丈夫だと思います。

最後に実行したいファイル名を指定して実行すれば、コンパイルが通ります。
**通るだけですが。**

## 実行時エラー
試しにプログラムを実行すると、このようなエラーが出ると思います。

```
dyld: Library not loaded: @executable_path/libfbxsdk.dylib
  Referenced from: ./a.out
  Reason: image not found
```

これは`libfbxsdk.dylib`がうまく読み込めていないことに起因します。
`@executable_path`とは何かというと、実行した時のパスになります。なので今だと実行ファイルがある場所になりますかね。人それぞれ違うと思いますが。

ですが、`libfbxsdk.dylib`は`/FBX SDK/2020.2/`にあります。なんとかこの場所を指定してあげる必要があります。

その方法が、[こちらのページ](https://www.javaer101.com/ja/article/41630329.html)で紹介されている、`install_name_tool`を使用する方法です。

```
install_name_tool -id "/FBX SDK/2020.2/lib/clang/debug/libfbxsdk.dylib" "/FBX SDK/2020.2/lib/clang/debug/libfbxsdk.dylib"
```
と入力すれば、`@executable_path`がフルパスに書き変わります。これももしかしたら~~首藤~~`sudo`が必要かもしれません。適宜挿入してください。
正しく変更された場合は、

```
otool -D "/FBX SDK/2020.2/lib/clang/debug/libfbxsdk.dylib"
```
で確認できると思います。

これでもう一度コンパイルを行い、実行すると、うまくいけば「Success」と表示されます。お疲れ様でした。

## 静的ライブラリでのコンパイル(2020/02/20追記)
実は上記の方法だと動的ライブラリでのリンクとなってしまいます。
動的ライブラリだとFBXSDKがない環境だと動かない可能性があります。
せっかくFBXSDKには静的ライブラリもあるので、静的ライブラリでのリンクをする方法を調べました。

こちらのコマンドになります。([こちらの記事](https://qiita.com/segur/items/9e20a1d29bf397f1f843)を参考にさせていただきました)

```
g++ -I"/FBX SDK/2020.2/include" -lxml2 -lz -liconv -framework Cocoa -std=c++11 main.c "/FBX SDK/2020.2/lib/clang/debug/libfbxsdk.a"
```
いくつか解説をします。

`-lxml2`や`-lz`などが追加されてますね。これがないと動いてくれませんでした。
dylibファイルだと動的にこの辺をリンクしてくれるのに対し、aファイルだとこの辺も指定してあげないといけない、というように解釈していますが...よく分かってません。

また、debugフォルダを`-L`で指定するのではなく、aファイルを直接指定しています。
これにより、自分が作成したプログラムとlibfbxsdk.aファイルがうまく組み合わさってくれます。

実はlinux環境であれば、動的ライブラリの時のコマンドに、`-static`オプションと上記の`-lxml2`などをつけるだけで解決するのですが、macOSでは`-static`が使えないそうなので[^1]、動的ファイルが優先されてしまいます。なので、aファイルを直接与えてあげる必要があります。

そういえば、おそらくこのコマンドをコンパイルするたびに打つのはめんどいと思うので、makefileくらいは作ってあげたほうが楽かと思います。

[^1]: https://stackoverflow.com/questions/5259249/creating-static-mac-os-x-c-build

## おわりに
FBXモデルを表示させたかっただけなのに、環境構築みたいなところで詰まってしまいました...
おとなしくXcodeを使うことも考えましたが、絶対にできると信じて検索をしまくりました。
バージョンが違ったり状況が違ったりすると上記の方法ではできない可能性があるので、あくまで参考程度に考えてくださいね。

久々にコンパイルやリンク、静的ライブラリ動的ライブラリに触れたので正直どうして何があってできたのかが分かってないです。申し訳ありません。これからじっくり学んでいきます。